Skip to content

Commit c7f810b

Browse files
bpo-46476: Fix memory leak in code objects generated by deepfreeze (pythonGH-30853)
Add _Py_Deepfreeze_Fini() and _PyStaticCode_Dealloc() functions.
1 parent ecfacc3 commit c7f810b

File tree

8 files changed

+39
-0
lines changed

8 files changed

+39
-0
lines changed

Include/internal/pycore_code.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,8 @@ void _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr,
276276
SpecializedCacheEntry *cache);
277277
void _Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, SpecializedCacheEntry *cache);
278278

279+
/* Deallocator function for static codeobjects used in deepfreeze.py */
280+
void _PyStaticCode_Dealloc(PyCodeObject *co, _Py_CODEUNIT *firstinstr);
279281

280282
#ifdef Py_STATS
281283

Include/internal/pycore_pylifecycle.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ extern void _PyWarnings_Fini(PyInterpreterState *interp);
8383
extern void _PyAST_Fini(PyInterpreterState *interp);
8484
extern void _PyAtExit_Fini(PyInterpreterState *interp);
8585
extern void _PyThread_FiniType(PyInterpreterState *interp);
86+
extern void _Py_Deepfreeze_Fini(void);
8687

8788
extern PyStatus _PyGILState_Init(_PyRuntimeState *runtime);
8889
extern PyStatus _PyGILState_SetTstate(PyThreadState *tstate);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix memory leak in code objects generated by deepfreeze. Patch by Kumar Aditya.

Objects/codeobject.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1906,3 +1906,18 @@ _PyCode_ConstantKey(PyObject *op)
19061906
}
19071907
return key;
19081908
}
1909+
1910+
void
1911+
_PyStaticCode_Dealloc(PyCodeObject *co, _Py_CODEUNIT *firstinstr)
1912+
{
1913+
PyMem_Free(co->co_quickened);
1914+
co->co_quickened = NULL;
1915+
PyMem_Free(co->co_extra);
1916+
co->co_extra = NULL;
1917+
co->co_firstinstr = firstinstr;
1918+
if (co->co_weakreflist != NULL) {
1919+
PyObject_ClearWeakRefs((PyObject *)co);
1920+
co->co_weakreflist = NULL;
1921+
}
1922+
co->co_warmup = QUICKENING_INITIAL_WARMUP_VALUE;
1923+
}

Programs/_bootstrap_python.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@
1414
#include "Python/frozen_modules/importlib._bootstrap_external.h"
1515
/* End includes */
1616

17+
/* Empty finalizer for deepfrozen modules*/
18+
void
19+
_Py_Deepfreeze_Fini(void)
20+
{
21+
}
22+
1723
/* Note that a negative size indicates a package. */
1824

1925
static const struct _frozen bootstrap_modules[] = {
@@ -103,3 +109,4 @@ main(int argc, char **argv)
103109
}
104110
Py_ExitStatusException(status);
105111
}
112+

Programs/_freeze_module.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@
2222
#include <unistd.h>
2323
#endif
2424

25+
/* Empty finalizer for deepfrozen modules */
26+
void
27+
_Py_Deepfreeze_Fini(void)
28+
{
29+
}
30+
2531
/* To avoid a circular dependency on frozen.o, we create our own structure
2632
of frozen modules instead, left deliberately blank so as to avoid
2733
unintentional import of a stale version of _frozen_importlib. */
@@ -235,3 +241,4 @@ main(int argc, char *argv[])
235241
Py_Finalize();
236242
return 1;
237243
}
244+

Python/pylifecycle.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1723,6 +1723,7 @@ finalize_interp_clear(PyThreadState *tstate)
17231723
_Py_HashRandomization_Fini();
17241724
_PyArg_Fini();
17251725
_Py_ClearFileSystemEncoding();
1726+
_Py_Deepfreeze_Fini();
17261727
}
17271728

17281729
finalize_interp_types(tstate->interp);

Tools/scripts/deepfreeze.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ def __init__(self, file: TextIO) -> None:
109109
self.cache: Dict[tuple[type, object, str], str] = {}
110110
self.hits, self.misses = 0, 0
111111
self.patchups: list[str] = []
112+
self.deallocs: list[str] = []
112113
self.write('#include "Python.h"')
113114
self.write('#include "internal/pycore_gc.h"')
114115
self.write('#include "internal/pycore_code.h"')
@@ -277,6 +278,7 @@ def generate_code(self, name: str, code: types.CodeType) -> str:
277278
self.write(f".co_varnames = {co_varnames},")
278279
self.write(f".co_cellvars = {co_cellvars},")
279280
self.write(f".co_freevars = {co_freevars},")
281+
self.deallocs.append(f"_PyStaticCode_Dealloc(&{name}, (_Py_CODEUNIT *) {removesuffix(co_code, '.ob_base.ob_base')}.ob_sval);")
280282
return f"& {name}.ob_base"
281283

282284
def generate_tuple(self, name: str, t: Tuple[object, ...]) -> str:
@@ -440,6 +442,9 @@ def generate(args: list[str], output: TextIO) -> None:
440442
else:
441443
code = compile(fd.read(), f"<frozen {modname}>", "exec")
442444
printer.generate_file(modname, code)
445+
with printer.block(f"void\n_Py_Deepfreeze_Fini(void)"):
446+
for p in printer.deallocs:
447+
printer.write(p)
443448
if verbose:
444449
print(f"Cache hits: {printer.hits}, misses: {printer.misses}")
445450

0 commit comments

Comments
 (0)