https://github.com/python/cpython/commit/eb4c78df07c87237f97e4c3e6f583fd4155821b3
commit: eb4c78df07c87237f97e4c3e6f583fd4155821b3
branch: main
author: Pablo Galindo Salgado <[email protected]>
committer: pablogsal <[email protected]>
date: 2026-04-13T23:43:55+01:00
summary:
gh-148252: Fix stack depth calculation in binary reader on 32-bit platforms
(#148253)
Compute ``final_depth`` in ``decode_stack_pop_push()`` and
``decode_stack_suffix()`` using ``uint64_t`` before validating it.
On 32-bit builds, using ``size_t`` arithmetic for ``keep + push`` can wrap
for large input values, causing the later bounds check to validate the wrong
final depth. Using a widened type keeps the validation aligned with the
actual result.
files:
A Misc/NEWS.d/next/Security/2026-04-08-14-25-47.gh-issue-148252.IEp9Rt.rst
M Modules/_remote_debugging/binary_io_reader.c
diff --git
a/Misc/NEWS.d/next/Security/2026-04-08-14-25-47.gh-issue-148252.IEp9Rt.rst
b/Misc/NEWS.d/next/Security/2026-04-08-14-25-47.gh-issue-148252.IEp9Rt.rst
new file mode 100644
index 00000000000000..adc2c9287149db
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2026-04-08-14-25-47.gh-issue-148252.IEp9Rt.rst
@@ -0,0 +1,3 @@
+Fixed stack depth calculation in :mod:`!_remote_debugging` when decoding
+certain ``.pyb`` inputs on 32-bit builds. Issue originally identified and
+diagnosed by Tristan Madani (@TristanInSec on GitHub).
diff --git a/Modules/_remote_debugging/binary_io_reader.c
b/Modules/_remote_debugging/binary_io_reader.c
index 616213541e12e1..aca93e9cb1a30e 100644
--- a/Modules/_remote_debugging/binary_io_reader.c
+++ b/Modules/_remote_debugging/binary_io_reader.c
@@ -601,6 +601,20 @@ reader_get_or_create_thread_state(BinaryReader *reader,
uint64_t thread_id,
* STACK DECODING HELPERS
*
============================================================================ */
+/* Validate that final_depth fits in the stack buffer.
+ * Uses uint64_t to prevent overflow on 32-bit platforms. */
+static inline int
+validate_stack_depth(ReaderThreadState *ts, uint64_t final_depth)
+{
+ if (final_depth > ts->current_stack_capacity) {
+ PyErr_Format(PyExc_ValueError,
+ "Final stack depth %llu exceeds capacity %zu",
+ (unsigned long long)final_depth, ts->current_stack_capacity);
+ return -1;
+ }
+ return 0;
+}
+
/* Decode a full stack from sample data.
* Updates ts->current_stack and ts->current_stack_depth.
* Returns 0 on success, -1 on error (bounds violation). */
@@ -658,12 +672,9 @@ decode_stack_suffix(ReaderThreadState *ts, const uint8_t
*data,
return -1;
}
- /* Validate final depth doesn't exceed capacity */
- size_t final_depth = (size_t)shared + new_count;
- if (final_depth > ts->current_stack_capacity) {
- PyErr_Format(PyExc_ValueError,
- "Final stack depth %zu exceeds capacity %zu",
- final_depth, ts->current_stack_capacity);
+ /* Use uint64_t to prevent overflow on 32-bit platforms */
+ uint64_t final_depth = (uint64_t)shared + new_count;
+ if (validate_stack_depth(ts, final_depth) < 0) {
return -1;
}
@@ -713,12 +724,9 @@ decode_stack_pop_push(ReaderThreadState *ts, const uint8_t
*data,
}
size_t keep = (ts->current_stack_depth > pop) ? ts->current_stack_depth -
pop : 0;
- /* Validate final depth doesn't exceed capacity */
- size_t final_depth = keep + push;
- if (final_depth > ts->current_stack_capacity) {
- PyErr_Format(PyExc_ValueError,
- "Final stack depth %zu exceeds capacity %zu",
- final_depth, ts->current_stack_capacity);
+ /* Use uint64_t to prevent overflow on 32-bit platforms */
+ uint64_t final_depth = (uint64_t)keep + push;
+ if (validate_stack_depth(ts, final_depth) < 0) {
return -1;
}
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]