From 668c1ac828fb546b81da6b36dae09d754bd0f59e Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 18 Oct 2023 09:39:16 -0700 Subject: [PATCH] tools: ynl-gen: support full range of min/max checks for integer values Extend the support to full range of min/max checks. None of the existing YNL families required complex integer validation. The support is less than trivial, because we try to keep struct nla_policy tiny the min/max members it holds in place are s16. Meaning we can only express checks in range of s16. For larger ranges we need to define a structure and link it in the policy. Link: https://lore.kernel.org/r/20231018163917.2514503-3-kuba@kernel.org Signed-off-by: Jakub Kicinski --- Documentation/netlink/genetlink-c.yaml | 3 + Documentation/netlink/genetlink-legacy.yaml | 3 + Documentation/netlink/genetlink.yaml | 3 + tools/net/ynl/ynl-gen-c.py | 66 ++++++++++++++++++--- 4 files changed, 68 insertions(+), 7 deletions(-) diff --git a/Documentation/netlink/genetlink-c.yaml b/Documentation/netlink/genetlink-c.yaml index f9366aaddd21..75af0b51f3d7 100644 --- a/Documentation/netlink/genetlink-c.yaml +++ b/Documentation/netlink/genetlink-c.yaml @@ -184,6 +184,9 @@ properties: min: description: Min value for an integer attribute. type: integer + max: + description: Max value for an integer attribute. + type: integer min-len: description: Min length for a binary attribute. $ref: '#/$defs/len-or-define' diff --git a/Documentation/netlink/genetlink-legacy.yaml b/Documentation/netlink/genetlink-legacy.yaml index a6a490333a1a..c0f17a8bfe0d 100644 --- a/Documentation/netlink/genetlink-legacy.yaml +++ b/Documentation/netlink/genetlink-legacy.yaml @@ -227,6 +227,9 @@ properties: min: description: Min value for an integer attribute. type: integer + max: + description: Max value for an integer attribute. + type: integer min-len: description: Min length for a binary attribute. $ref: '#/$defs/len-or-define' diff --git a/Documentation/netlink/genetlink.yaml b/Documentation/netlink/genetlink.yaml index 2b788e607a14..4fd56e3b1553 100644 --- a/Documentation/netlink/genetlink.yaml +++ b/Documentation/netlink/genetlink.yaml @@ -157,6 +157,9 @@ properties: min: description: Min value for an integer attribute. type: integer + max: + description: Max value for an integer attribute. + type: integer min-len: description: Min length for a binary attribute. $ref: '#/$defs/len-or-define' diff --git a/tools/net/ynl/ynl-gen-c.py b/tools/net/ynl/ynl-gen-c.py index 7f4ad4014d17..9d008346dd50 100755 --- a/tools/net/ynl/ynl-gen-c.py +++ b/tools/net/ynl/ynl-gen-c.py @@ -47,6 +47,7 @@ class Type(SpecAttr): if 'len' in attr: self.len = attr['len'] + if 'nested-attributes' in attr: self.nested_attrs = attr['nested-attributes'] if self.nested_attrs == family.name: @@ -262,6 +263,27 @@ class TypeScalar(Type): if 'byte-order' in attr: self.byte_order_comment = f" /* {attr['byte-order']} */" + if 'enum' in self.attr: + enum = self.family.consts[self.attr['enum']] + low, high = enum.value_range() + if 'min' not in self.checks: + if low != 0 or self.type[0] == 's': + self.checks['min'] = low + if 'max' not in self.checks: + self.checks['max'] = high + + if 'min' in self.checks and 'max' in self.checks: + if self.checks['min'] > self.checks['max']: + raise Exception(f'Invalid limit for "{self.name}" min: {self.checks["min"]} max: {self.checks["max"]}') + self.checks['range'] = True + + low = min(self.checks.get('min', 0), self.checks.get('max', 0)) + high = max(self.checks.get('min', 0), self.checks.get('max', 0)) + if low < 0 and self.type[0] == 'u': + raise Exception(f'Invalid limit for "{self.name}" negative limit for unsigned type') + if low < -32768 or high > 32767: + self.checks['full-range'] = True + # Added by resolve(): self.is_bitfield = None delattr(self, "is_bitfield") @@ -301,14 +323,14 @@ class TypeScalar(Type): flag_cnt = len(flags['entries']) mask = (1 << flag_cnt) - 1 return f"NLA_POLICY_MASK({policy}, 0x{mask:x})" + elif 'full-range' in self.checks: + return f"NLA_POLICY_FULL_RANGE({policy}, &{c_lower(self.enum_name)}_range)" + elif 'range' in self.checks: + return f"NLA_POLICY_RANGE({policy}, {self.checks['min']}, {self.checks['max']})" elif 'min' in self.checks: return f"NLA_POLICY_MIN({policy}, {self.checks['min']})" - elif 'enum' in self.attr: - enum = self.family.consts[self.attr['enum']] - low, high = enum.value_range() - if low == 0: - return f"NLA_POLICY_MAX({policy}, {high})" - return f"NLA_POLICY_RANGE({policy}, {low}, {high})" + elif 'max' in self.checks: + return f"NLA_POLICY_MAX({policy}, {self.checks['max']})" return super()._attr_policy(policy) def _attr_typol(self): @@ -1241,7 +1263,7 @@ class CodeWriter: for one in members: line = '.' + one[0] line += '\t' * ((longest - len(one[0]) - 1 + 7) // 8) - line += '= ' + one[1] + ',' + line += '= ' + str(one[1]) + ',' self.p(line) @@ -1940,6 +1962,34 @@ def policy_should_be_static(family): return family.kernel_policy == 'split' or kernel_can_gen_family_struct(family) +def print_kernel_policy_ranges(family, cw): + first = True + for _, attr_set in family.attr_sets.items(): + if attr_set.subset_of: + continue + + for _, attr in attr_set.items(): + if not attr.request: + continue + if 'full-range' not in attr.checks: + continue + + if first: + cw.p('/* Integer value ranges */') + first = False + + sign = '' if attr.type[0] == 'u' else '_signed' + cw.block_start(line=f'struct netlink_range_validation{sign} {c_lower(attr.enum_name)}_range =') + members = [] + if 'min' in attr.checks: + members.append(('min', attr.checks['min'])) + if 'max' in attr.checks: + members.append(('max', attr.checks['max'])) + cw.write_struct_init(members) + cw.block_end(line=';') + cw.nl() + + def print_kernel_op_table_fwd(family, cw, terminate): exported = not kernel_can_gen_family_struct(family) @@ -2479,6 +2529,8 @@ def main(): print_kernel_mcgrp_hdr(parsed, cw) print_kernel_family_struct_hdr(parsed, cw) else: + print_kernel_policy_ranges(parsed, cw) + for _, struct in sorted(parsed.pure_nested_structs.items()): if struct.request: cw.p('/* Common nested types */')