Skip to content

gh-126119: fix some crashes in code objects if co_stacksize is absurdly large #126122

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions Lib/test/test_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,12 @@
import ctypes
except ImportError:
ctypes = None

try:
import _testcapi
except ImportError:
_testcapi = None

from test.support import (cpython_only,
check_impl_detail, requires_debug_ranges,
gc_collect, Py_GIL_DISABLED)
Expand Down Expand Up @@ -595,6 +601,25 @@ def test_code_equal_with_instrumentation(self):
self.assertNotEqual(code1, code2)
sys.settrace(None)

@unittest.skipUnless(ctypes, "requires ctypes")
@unittest.skipUnless(_testcapi, "requires _testcapi")
def test_co_framesize_overflow(self):
# See: https://github.com/python/cpython/issues/126119.

def foo(a, b):
x = a * b
return x

c = foo.__code__

co_nlocalsplus = len({*c.co_varnames, *c.co_cellvars, *c.co_freevars})
# anything below that limit is a valid co_stacksize
evil_stacksize = int(_testcapi.INT_MAX / 16 - co_nlocalsplus)

with self.assertRaisesRegex(OverflowError, "stack size is too large"):
c.__replace__(co_stacksize=evil_stacksize)
c.__replace__(co_stacksize=evil_stacksize - 1)


def isinterned(s):
return s is sys.intern(('_' + s + '_')[1:-1])
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fix a crash in DEBUG builds due to an overflow when the :attr:`co_stacksize
<codeobject.co_stacksize>` field of a :ref:`code object <code-objects>` is
set to an absurdly large integer.
Reported by Valery Fedorenko. Patch by Bénédikt Tran.
17 changes: 16 additions & 1 deletion Objects/codeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,22 @@ _PyCode_Validate(struct _PyCodeConstructor *con)
PyErr_SetString(PyExc_ValueError, "code: co_varnames is too small");
return -1;
}

/*
* Since framesize = stacksize + nlocalsplus + FRAME_SPECIALS_SIZE is used
* as framesize * sizeof(PyObject *) and assumed to be < INT_MAX in many
* other places, we need to limit stacksize + nlocalsplus in order to
* avoid overflows.
*
* See https://github.com/python/cpython/issues/126119 for details
* and corresponding PR for the rationale on the upper limit value.
*/
Py_ssize_t limit = (Py_ssize_t)(INT_MAX / 16);
Py_ssize_t nlocalsplus = PyTuple_GET_SIZE(con->localsplusnames);
if (nlocalsplus >= limit || con->stacksize >= limit - nlocalsplus) {
PyErr_SetString(PyExc_OverflowError,
"code: locals + stack size is too large");
return -1;
}
return 0;
}

Expand Down
Loading