]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-148252: Fix stack depth calculation in binary reader on 32-bit platforms (#148253)
authorPablo Galindo Salgado <Pablogsal@gmail.com>
Mon, 13 Apr 2026 22:43:55 +0000 (23:43 +0100)
committerGitHub <noreply@github.com>
Mon, 13 Apr 2026 22:43:55 +0000 (23:43 +0100)
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.

Misc/NEWS.d/next/Security/2026-04-08-14-25-47.gh-issue-148252.IEp9Rt.rst [new file with mode: 0644]
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 (file)
index 0000000..adc2c92
--- /dev/null
@@ -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).
index 616213541e12e1695c8752d8de3cf1305e2ca62c..aca93e9cb1a30ef8212b27c45130963f83aa770b 100644 (file)
@@ -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;
     }