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 index 00000000000000..531ea2348ffdef --- /dev/null +++ b/Misc/NEWS.d/next/Security/2026-04-24-23-15-42.gh-issue-148252.8BLmzd.rst @@ -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. diff --git a/Modules/_remote_debugging/binary_io.h b/Modules/_remote_debugging/binary_io.h index d90546078bf68c..18f989f672e103 100644 --- a/Modules/_remote_debugging/binary_io.h +++ b/Modules/_remote_debugging/binary_io.h @@ -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"); diff --git a/Modules/_remote_debugging/binary_io_reader.c b/Modules/_remote_debugging/binary_io_reader.c index aca93e9cb1a30e..6c32ef70ac3f65 100644 --- a/Modules/_remote_debugging/binary_io_reader.c +++ b/Modules/_remote_debugging/binary_io_reader.c @@ -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 */ } diff --git a/Modules/_remote_debugging/binary_io_writer.c b/Modules/_remote_debugging/binary_io_writer.c index c129c93efe23c5..0ac6c88d0373a7 100644 --- a/Modules/_remote_debugging/binary_io_writer.c +++ b/Modules/_remote_debugging/binary_io_writer.c @@ -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; }