Skip to content

Commit ce6cb81

Browse files
committed
tools: ynl-gen: individually free previous values on double set
When user calls request_attrA_set() multiple times (for the same attribute), and attrA is of type which allocates memory - we try to free the previously associated values. For array types (including multi-attr) we have only freed the array, but the array may have contained pointers. Refactor the code generation for free attr and reuse the generated lines in setters to flush out the previous state. Since setters are static inlines in the header we need to add forward declarations for the free helpers of pure nested structs. Track which types get used by arrays and include the right forwad declarations. At least ethtool string set and bit set would not be freed without this. Tho, admittedly, overriding already set attribute twice is likely a very very rare thing to do. Fixes: be5bea1 ("net: add basic C code generators for Netlink") Reviewed-by: Donald Hunter <[email protected]> Reviewed-by: Jacob Keller <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent dfa464b commit ce6cb81

File tree

1 file changed

+45
-17
lines changed

1 file changed

+45
-17
lines changed

tools/net/ynl/pyynl/ynl_gen_c.py

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,15 @@ def _complex_member_type(self, ri):
162162
def free_needs_iter(self):
163163
return False
164164

165-
def free(self, ri, var, ref):
165+
def _free_lines(self, ri, var, ref):
166166
if self.is_multi_val() or self.presence_type() == 'len':
167-
ri.cw.p(f'free({var}->{ref}{self.c_name});')
167+
return [f'free({var}->{ref}{self.c_name});']
168+
return []
169+
170+
def free(self, ri, var, ref):
171+
lines = self._free_lines(ri, var, ref)
172+
for line in lines:
173+
ri.cw.p(line)
168174

169175
def arg_member(self, ri):
170176
member = self._complex_member_type(ri)
@@ -263,6 +269,10 @@ def setter(self, ri, space, direction, deref=False, ref=None):
263269
var = "req"
264270
member = f"{var}->{'.'.join(ref)}"
265271

272+
local_vars = []
273+
if self.free_needs_iter():
274+
local_vars += ['unsigned int i;']
275+
266276
code = []
267277
presence = ''
268278
for i in range(0, len(ref)):
@@ -272,14 +282,19 @@ def setter(self, ri, space, direction, deref=False, ref=None):
272282
if i == len(ref) - 1 and self.presence_type() != 'bit':
273283
continue
274284
code.append(presence + ' = 1;')
285+
ref_path = '.'.join(ref[:-1])
286+
if ref_path:
287+
ref_path += '.'
288+
code += self._free_lines(ri, var, ref_path)
275289
code += self._setter_lines(ri, member, presence)
276290

277291
func_name = f"{op_prefix(ri, direction, deref=deref)}_set_{'_'.join(ref)}"
278292
free = bool([x for x in code if 'free(' in x])
279293
alloc = bool([x for x in code if 'alloc(' in x])
280294
if free and not alloc:
281295
func_name = '__' + func_name
282-
ri.cw.write_func('static inline void', func_name, body=code,
296+
ri.cw.write_func('static inline void', func_name, local_vars=local_vars,
297+
body=code,
283298
args=[f'{type_name(ri, direction, deref=deref)} *{var}'] + self.arg_member(ri))
284299

285300

@@ -482,8 +497,7 @@ def _attr_get(self, ri, var):
482497
['unsigned int len;']
483498

484499
def _setter_lines(self, ri, member, presence):
485-
return [f"free({member});",
486-
f"{presence}_len = strlen({self.c_name});",
500+
return [f"{presence}_len = strlen({self.c_name});",
487501
f"{member} = malloc({presence}_len + 1);",
488502
f'memcpy({member}, {self.c_name}, {presence}_len);',
489503
f'{member}[{presence}_len] = 0;']
@@ -536,8 +550,7 @@ def _attr_get(self, ri, var):
536550
['unsigned int len;']
537551

538552
def _setter_lines(self, ri, member, presence):
539-
return [f"free({member});",
540-
f"{presence}_len = len;",
553+
return [f"{presence}_len = len;",
541554
f"{member} = malloc({presence}_len);",
542555
f'memcpy({member}, {self.c_name}, {presence}_len);']
543556

@@ -574,12 +587,14 @@ def is_recursive(self):
574587
def _complex_member_type(self, ri):
575588
return self.nested_struct_type
576589

577-
def free(self, ri, var, ref):
590+
def _free_lines(self, ri, var, ref):
591+
lines = []
578592
at = '&'
579593
if self.is_recursive_for_op(ri):
580594
at = ''
581-
ri.cw.p(f'if ({var}->{ref}{self.c_name})')
582-
ri.cw.p(f'{self.nested_render_name}_free({at}{var}->{ref}{self.c_name});')
595+
lines += [f'if ({var}->{ref}{self.c_name})']
596+
lines += [f'{self.nested_render_name}_free({at}{var}->{ref}{self.c_name});']
597+
return lines
583598

584599
def _attr_typol(self):
585600
return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
@@ -632,15 +647,19 @@ def _complex_member_type(self, ri):
632647
def free_needs_iter(self):
633648
return 'type' not in self.attr or self.attr['type'] == 'nest'
634649

635-
def free(self, ri, var, ref):
650+
def _free_lines(self, ri, var, ref):
651+
lines = []
636652
if self.attr['type'] in scalars:
637-
ri.cw.p(f"free({var}->{ref}{self.c_name});")
653+
lines += [f"free({var}->{ref}{self.c_name});"]
638654
elif 'type' not in self.attr or self.attr['type'] == 'nest':
639-
ri.cw.p(f"for (i = 0; i < {var}->{ref}n_{self.c_name}; i++)")
640-
ri.cw.p(f'{self.nested_render_name}_free(&{var}->{ref}{self.c_name}[i]);')
641-
ri.cw.p(f"free({var}->{ref}{self.c_name});")
655+
lines += [
656+
f"for (i = 0; i < {var}->{ref}n_{self.c_name}; i++)",
657+
f'{self.nested_render_name}_free(&{var}->{ref}{self.c_name}[i]);',
658+
f"free({var}->{ref}{self.c_name});",
659+
]
642660
else:
643661
raise Exception(f"Free of MultiAttr sub-type {self.attr['type']} not supported yet")
662+
return lines
644663

645664
def _attr_policy(self, policy):
646665
return self.base_type._attr_policy(policy)
@@ -666,8 +685,7 @@ def attr_put(self, ri, var):
666685
def _setter_lines(self, ri, member, presence):
667686
# For multi-attr we have a count, not presence, hack up the presence
668687
presence = presence[:-(len('_present.') + len(self.c_name))] + "n_" + self.c_name
669-
return [f"free({member});",
670-
f"{member} = {self.c_name};",
688+
return [f"{member} = {self.c_name};",
671689
f"{presence} = n_{self.c_name};"]
672690

673691

@@ -755,6 +773,7 @@ def __init__(self, family, space_name, type_list=None, inherited=None):
755773
self.request = False
756774
self.reply = False
757775
self.recursive = False
776+
self.in_multi_val = False # used by a MultiAttr or and legacy arrays
758777

759778
self.attr_list = []
760779
self.attrs = dict()
@@ -1122,6 +1141,10 @@ def _load_nested_sets(self):
11221141
if attr in rs_members['reply']:
11231142
self.pure_nested_structs[nested].reply = True
11241143

1144+
if spec.is_multi_val():
1145+
child = self.pure_nested_structs.get(nested)
1146+
child.in_multi_val = True
1147+
11251148
self._sort_pure_types()
11261149

11271150
# Propagate the request / reply / recursive
@@ -1136,6 +1159,8 @@ def _load_nested_sets(self):
11361159
struct.child_nests.update(child.child_nests)
11371160
child.request |= struct.request
11381161
child.reply |= struct.reply
1162+
if spec.is_multi_val():
1163+
child.in_multi_val = True
11391164
if attr_set in struct.child_nests:
11401165
struct.recursive = True
11411166

@@ -2958,6 +2983,9 @@ def main():
29582983
for attr_set, struct in parsed.pure_nested_structs.items():
29592984
ri = RenderInfo(cw, parsed, args.mode, "", "", attr_set)
29602985
print_type_full(ri, struct)
2986+
if struct.request and struct.in_multi_val:
2987+
free_rsp_nested_prototype(ri)
2988+
cw.nl()
29612989

29622990
for op_name, op in parsed.ops.items():
29632991
cw.p(f"/* ============== {op.enum_name} ============== */")

0 commit comments

Comments
 (0)