Skip to content
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

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

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
12 changes: 12 additions & 0 deletions Lib/test/test_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,18 @@ def test_code_equal_with_instrumentation(self):
self.assertNotEqual(code1, code2)
sys.settrace(None)

def test_co_stacksize_overflow(self):
# See: https://github.com/python/cpython/issues/126119.

# Since co_framesize = nlocalsplus + co_stacksize + FRAME_SPECIALS_SIZE,
# we need to check that co_stacksize is not too large. We could fortify
# the test by explicitly checking that bound, but this needs to expose
# FRAME_SPECIALS_SIZE and a getter for 'nlocalsplus'.

c = (lambda: ...).__code__
with self.assertRaisesRegex(OverflowError, "co_stacksize"):
c.__replace__(co_stacksize=2147483647)


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 absurdely large integer.
picnixz marked this conversation as resolved.
Show resolved Hide resolved
Reported by Valery Fedorenko. Patch by Bénédikt Tran.
7 changes: 6 additions & 1 deletion Objects/codeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,12 @@ _PyCode_Validate(struct _PyCodeConstructor *con)
PyErr_SetString(PyExc_ValueError, "code: co_varnames is too small");
return -1;
}

/* Ensure that the framesize will not overflow */
int nlocalsplus = (int)PyTuple_GET_SIZE(con->localsplusnames);
if (con->stacksize >= INT_MAX - nlocalsplus - FRAME_SPECIALS_SIZE) {
PyErr_SetString(PyExc_OverflowError, "code: co_stacksize is too large");
return -1;
}
return 0;
}

Expand Down
19 changes: 15 additions & 4 deletions Objects/frameobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1804,11 +1804,22 @@ PyDoc_STRVAR(clear__doc__,
static PyObject *
frame_sizeof(PyFrameObject *f, PyObject *Py_UNUSED(ignored))
{
Py_ssize_t res;
res = offsetof(PyFrameObject, _f_frame_data) + offsetof(_PyInterpreterFrame, localsplus);
Py_ssize_t res = offsetof(PyFrameObject, _f_frame_data)
+ offsetof(_PyInterpreterFrame, localsplus);
PyCodeObject *code = _PyFrame_GetCode(f->f_frame);
res += _PyFrame_NumSlotsForCodeObject(code) * sizeof(PyObject *);
return PyLong_FromSsize_t(res);
int nslots = _PyFrame_NumSlotsForCodeObject(code);
assert(nslots >= 0);
if ((size_t)nslots >= (PY_SSIZE_T_MAX - res) / sizeof(PyObject *)) {
picnixz marked this conversation as resolved.
Show resolved Hide resolved
// This could happen if the underlying code object has a
// very large stacksize (but at most INT_MAX) yet that the
// above offsets make the result overflow.
//
// This should normally only happen if PY_SSIZE_T_MAX == INT_MAX,
// but we anyway raise an exception on other systems for safety.
PyErr_SetString(PyExc_OverflowError, "size exceeds PY_SSIZE_T_MAX");
return NULL;
}
return PyLong_FromSsize_t(res + nslots * sizeof(PyObject *));
picnixz marked this conversation as resolved.
Show resolved Hide resolved
}

PyDoc_STRVAR(sizeof__doc__,
Expand Down
Loading