]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-148252: Avoid overflow in `_remote_debugging` binary reader bounds checks (#148972)
authorMaurycy Pawłowski-Wieroński <maurycy@maurycy.com>
Mon, 27 Apr 2026 23:06:23 +0000 (01:06 +0200)
committerGitHub <noreply@github.com>
Mon, 27 Apr 2026 23:06:23 +0000 (00:06 +0100)
Misc/NEWS.d/next/Security/2026-04-24-23-15-42.gh-issue-148252.8BLmzd.rst [new file with mode: 0644]
Modules/_remote_debugging/binary_io.h
Modules/_remote_debugging/binary_io_reader.c
Modules/_remote_debugging/binary_io_writer.c

diff --git a/Misc/NEWS.d/next/Security/2026-04-24-23-15-42.gh-issue-148252.8BLmzd.rst b/Misc/NEWS.d/next/Security/2026-04-24-23-15-42.gh-issue-148252.8BLmzd.rst
new file mode 100644 (file)
index 0000000..531ea23
--- /dev/null
@@ -0,0 +1,3 @@
+Fixed string table and sample record bounds checks in :mod:`!_remote_debugging`
+when decoding certain ``.pyb`` inputs on 32-bit builds. Patch by Maurycy
+Pawłowski-Wieroński.
index d90546078bf68cbb2947209c37965f51c53cedac..18f989f672e103456e253d3887810ffba333e70f 100644 (file)
@@ -61,6 +61,7 @@ extern "C" {
 #define HDR_SIZE_COMPRESSION 4
 #define FILE_HEADER_SIZE     (HDR_OFF_COMPRESSION + HDR_SIZE_COMPRESSION)
 #define FILE_HEADER_PLACEHOLDER_SIZE 64
+#define SAMPLE_HEADER_FIXED_SIZE (sizeof(uint64_t) + sizeof(uint32_t) + 1)
 
 static_assert(FILE_HEADER_SIZE <= FILE_HEADER_PLACEHOLDER_SIZE,
               "FILE_HEADER_SIZE exceeds FILE_HEADER_PLACEHOLDER_SIZE");
index aca93e9cb1a30ef8212b27c45130963f83aa770b..6c32ef70ac3f659617c37d6b49410e1f56e3a14b 100644 (file)
@@ -258,7 +258,7 @@ reader_parse_string_table(BinaryReader *reader, const uint8_t *data, size_t file
             PyErr_SetString(PyExc_ValueError, "Malformed varint in string table");
             return -1;
         }
-        if (offset + str_len > file_size) {
+        if (offset > file_size || str_len > file_size - offset) {
             PyErr_SetString(PyExc_ValueError, "String table overflow");
             return -1;
         }
@@ -976,8 +976,8 @@ binary_reader_replay(BinaryReader *reader, PyObject *collector, PyObject *progre
     }
 
     while (offset < reader->sample_data_size) {
-        /* Read thread_id (8 bytes) + interpreter_id (4 bytes) */
-        if (offset + 13 > reader->sample_data_size) {
+        /* Read thread_id (8 bytes) + interpreter_id (4 bytes) + encoding byte */
+        if (reader->sample_data_size - offset < SAMPLE_HEADER_FIXED_SIZE) {
             break;  /* End of data */
         }
 
index c129c93efe23c5ff8ff4bb203734fddb3001b051..0ac6c88d0373a7760fc19c29ace5ff56d66f23f8 100644 (file)
@@ -23,7 +23,6 @@
  * ============================================================================ */
 
 /* Sample header sizes */
-#define SAMPLE_HEADER_FIXED_SIZE 13      /* thread_id(8) + interpreter_id(4) + encoding(1) */
 #define SAMPLE_HEADER_MAX_SIZE 26        /* fixed + max_varint(10) + status(1) + margin */
 #define MAX_VARINT_SIZE 10               /* Maximum bytes for a varint64 */
 #define MAX_VARINT_SIZE_U32 5            /* Maximum bytes for a varint32 */
@@ -653,10 +652,13 @@ write_sample_with_encoding(BinaryWriter *writer, ThreadEntry *entry,
     memcpy(header_buf, &entry->thread_id, 8);
     memcpy(header_buf + 8, &entry->interpreter_id, 4);
     header_buf[12] = (uint8_t)encoding_type;
-    size_t varint_len = encode_varint_u64(header_buf + 13, timestamp_delta);
-    header_buf[13 + varint_len] = status;
+    size_t varint_len = encode_varint_u64(
+        header_buf + SAMPLE_HEADER_FIXED_SIZE,
+        timestamp_delta);
+    header_buf[SAMPLE_HEADER_FIXED_SIZE + varint_len] = status;
 
-    if (writer_write_bytes(writer, header_buf, 14 + varint_len) < 0) {
+    if (writer_write_bytes(writer, header_buf,
+                           SAMPLE_HEADER_FIXED_SIZE + varint_len + 1) < 0) {
         return -1;
     }