Skip to content

Commit dbf3d61

Browse files
committed
fix overflow in frame's stacksizes
1 parent 9b14083 commit dbf3d61

File tree

3 files changed

+33
-5
lines changed

3 files changed

+33
-5
lines changed

Lib/test/test_code.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,18 @@ def test_code_equal_with_instrumentation(self):
523523
self.assertNotEqual(code1, code2)
524524
sys.settrace(None)
525525

526+
def test_co_stacksize_overflow(self):
527+
# See: https://github.com/python/cpython/issues/126119.
528+
529+
# Since co_framesize = nlocalsplus + co_stacksize + FRAME_SPECIALS_SIZE,
530+
# we need to check that co_stacksize is not too large. We could fortify
531+
# the test by explicitly checking that bound, but this needs to expose
532+
# FRAME_SPECIALS_SIZE and a getter for 'nlocalsplus'.
533+
534+
c = (lambda: ...).__code__
535+
with self.assertRaisesRegex(OverflowError, "co_stacksize"):
536+
c.__replace__(co_stacksize=2147483647)
537+
526538

527539
def isinterned(s):
528540
return s is sys.intern(('_' + s + '_')[1:-1])

Objects/codeobject.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,12 @@ _PyCode_Validate(struct _PyCodeConstructor *con)
436436
PyErr_SetString(PyExc_ValueError, "code: co_varnames is too small");
437437
return -1;
438438
}
439-
439+
/* Ensure that the framesize will not overflow */
440+
int nlocalsplus = (int)PyTuple_GET_SIZE(con->localsplusnames);
441+
if (con->stacksize >= INT_MAX - nlocalsplus - FRAME_SPECIALS_SIZE) {
442+
PyErr_SetString(PyExc_OverflowError, "code: co_stacksize is too large");
443+
return -1;
444+
}
440445
return 0;
441446
}
442447

Objects/frameobject.c

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1804,11 +1804,22 @@ PyDoc_STRVAR(clear__doc__,
18041804
static PyObject *
18051805
frame_sizeof(PyFrameObject *f, PyObject *Py_UNUSED(ignored))
18061806
{
1807-
Py_ssize_t res;
1808-
res = offsetof(PyFrameObject, _f_frame_data) + offsetof(_PyInterpreterFrame, localsplus);
1807+
Py_ssize_t res = offsetof(PyFrameObject, _f_frame_data)
1808+
+ offsetof(_PyInterpreterFrame, localsplus);
18091809
PyCodeObject *code = _PyFrame_GetCode(f->f_frame);
1810-
res += _PyFrame_NumSlotsForCodeObject(code) * sizeof(PyObject *);
1811-
return PyLong_FromSsize_t(res);
1810+
int nslots = _PyFrame_NumSlotsForCodeObject(code);
1811+
assert(nslots >= 0);
1812+
if ((size_t)nslots >= (PY_SSIZE_T_MAX - res) / sizeof(PyObject *)) {
1813+
// This could happen if the underlying code object has a
1814+
// very large stacksize (but at most INT_MAX) yet that the
1815+
// above offsets make the result overflow.
1816+
//
1817+
// This should normally only happen if PY_SSIZE_T_MAX == INT_MAX,
1818+
// but we anyway raise an exception on other systems for safety.
1819+
PyErr_SetString(PyExc_OverflowError, "size exceeds PY_SSIZE_T_MAX");
1820+
return NULL;
1821+
}
1822+
return PyLong_FromSsize_t(res + nslots * sizeof(PyObject *));
18121823
}
18131824

18141825
PyDoc_STRVAR(sizeof__doc__,

0 commit comments

Comments
 (0)