Skip to content

Commit b4b59d1

Browse files
authored
gh-99240: Fix double-free bug in Argument Clinic str_converter generated code (GH-99241)
Fix double-free bug mentioned at python/cpython#99240, by moving memory clean up out of "exit" label. Automerge-Triggered-By: GH:erlend-aasland
1 parent c4882aa commit b4b59d1

File tree

1 file changed

+23
-2
lines changed

1 file changed

+23
-2
lines changed

clinic-latest/clinic.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,12 @@ def __init__(self):
348348
# "goto exit" if there are any.
349349
self.return_conversion = []
350350

351+
# The C statements required to do some operations
352+
# after the end of parsing but before cleaning up.
353+
# These operations may be, for example, memory deallocations which
354+
# can only be done without any error happening during argument parsing.
355+
self.post_parsing = []
356+
351357
# The C statements required to clean up after the impl call.
352358
self.cleanup = []
353359

@@ -820,6 +826,7 @@ def parser_body(prototype, *fields, declarations=''):
820826
{modifications}
821827
{return_value} = {c_basename}_impl({impl_arguments});
822828
{return_conversion}
829+
{post_parsing}
823830
824831
{exit_label}
825832
{cleanup}
@@ -1460,6 +1467,7 @@ def render_function(self, clinic, f):
14601467
template_dict['impl_parameters'] = ", ".join(data.impl_parameters)
14611468
template_dict['impl_arguments'] = ", ".join(data.impl_arguments)
14621469
template_dict['return_conversion'] = format_escape("".join(data.return_conversion).rstrip())
1470+
template_dict['post_parsing'] = format_escape("".join(data.post_parsing).rstrip())
14631471
template_dict['cleanup'] = format_escape("".join(data.cleanup))
14641472
template_dict['return_value'] = data.return_value
14651473

@@ -1484,6 +1492,7 @@ def render_function(self, clinic, f):
14841492
return_conversion=template_dict['return_conversion'],
14851493
initializers=template_dict['initializers'],
14861494
modifications=template_dict['modifications'],
1495+
post_parsing=template_dict['post_parsing'],
14871496
cleanup=template_dict['cleanup'],
14881497
)
14891498

@@ -2725,6 +2734,10 @@ def _render_non_self(self, parameter, data):
27252734
# parse_arguments
27262735
self.parse_argument(data.parse_arguments)
27272736

2737+
# post_parsing
2738+
if post_parsing := self.post_parsing():
2739+
data.post_parsing.append('/* Post parse cleanup for ' + name + ' */\n' + post_parsing.rstrip() + '\n')
2740+
27282741
# cleanup
27292742
cleanup = self.cleanup()
27302743
if cleanup:
@@ -2820,6 +2833,14 @@ def modify(self):
28202833
"""
28212834
return ""
28222835

2836+
def post_parsing(self):
2837+
"""
2838+
The C statements required to do some operations after the end of parsing but before cleaning up.
2839+
Return a string containing this code indented at column 0.
2840+
If no operation is necessary, return an empty string.
2841+
"""
2842+
return ""
2843+
28232844
def cleanup(self):
28242845
"""
28252846
The C statements required to clean up after this variable.
@@ -3416,10 +3437,10 @@ def converter_init(self, *, accept={str}, encoding=None, zeroes=False):
34163437
if NoneType in accept and self.c_default == "Py_None":
34173438
self.c_default = "NULL"
34183439

3419-
def cleanup(self):
3440+
def post_parsing(self):
34203441
if self.encoding:
34213442
name = self.name
3422-
return "".join(["if (", name, ") {\n PyMem_FREE(", name, ");\n}\n"])
3443+
return f"PyMem_FREE({name});\n"
34233444

34243445
def parse_arg(self, argname, displayname):
34253446
if self.format_unit == 's':

0 commit comments

Comments
 (0)