@@ -237,15 +237,29 @@ reader_decompress_samples(BinaryReader *reader, const uint8_t *data)
237237}
238238#endif
239239
240+ /* Reject a table/run count whose entries cannot fit in the bytes still
241+ * available; a malicious file could otherwise drive a huge allocation.
242+ * Each entry occupies at least min_entry_size bytes. */
243+ static int
244+ reader_validate_count (const char * what , uint32_t count ,
245+ size_t available_bytes , size_t min_entry_size )
246+ {
247+ size_t max_possible = available_bytes / min_entry_size ;
248+ if (count > max_possible ) {
249+ PyErr_Format (PyExc_ValueError ,
250+ "Invalid %s count %u exceeds maximum possible %zu" ,
251+ what , count , max_possible );
252+ return -1 ;
253+ }
254+ return 0 ;
255+ }
256+
240257static inline int
241258reader_parse_string_table (BinaryReader * reader , const uint8_t * data , size_t file_size )
242259{
243- size_t max_strings =
244- (file_size - reader -> string_table_offset ) / MIN_STRING_ENTRY_SIZE ;
245- if (reader -> strings_count > max_strings ) {
246- PyErr_Format (PyExc_ValueError ,
247- "Invalid string count %u exceeds maximum possible %zu" ,
248- reader -> strings_count , max_strings );
260+ if (reader_validate_count ("string" , reader -> strings_count ,
261+ file_size - reader -> string_table_offset ,
262+ MIN_STRING_ENTRY_SIZE ) < 0 ) {
249263 return -1 ;
250264 }
251265
@@ -289,12 +303,9 @@ reader_parse_frame_table(BinaryReader *reader, const uint8_t *data, size_t file_
289303 }
290304#endif
291305
292- size_t max_frames =
293- (file_size - reader -> frame_table_offset ) / MIN_FRAME_ENTRY_SIZE ;
294- if (reader -> frames_count > max_frames ) {
295- PyErr_Format (PyExc_ValueError ,
296- "Invalid frame count %u exceeds maximum possible %zu" ,
297- reader -> frames_count , max_frames );
306+ if (reader_validate_count ("frame" , reader -> frames_count ,
307+ file_size - reader -> frame_table_offset ,
308+ MIN_FRAME_ENTRY_SIZE ) < 0 ) {
298309 return -1 ;
299310 }
300311
@@ -1071,15 +1082,10 @@ binary_reader_replay(BinaryReader *reader, PyObject *collector, PyObject *progre
10711082 return -1 ;
10721083 }
10731084
1074- /* Validate RLE count to prevent DoS from malicious files.
1075- * Each RLE sample needs at least 2 bytes (1 byte min varint + 1 status byte).
1076- * Also reject absurdly large counts that would exhaust memory. */
1077- size_t remaining_data = reader -> sample_data_size - offset ;
1078- size_t max_possible_samples = remaining_data / 2 ;
1079- if (count > max_possible_samples ) {
1080- PyErr_Format (PyExc_ValueError ,
1081- "Invalid RLE count %u exceeds maximum possible %zu for remaining data" ,
1082- count , max_possible_samples );
1085+ /* Reject a count larger than the remaining bytes can hold; each
1086+ * RLE sample needs at least 2 bytes (1-byte min varint + status). */
1087+ if (reader_validate_count ("RLE" , count ,
1088+ reader -> sample_data_size - offset , 2 ) < 0 ) {
10831089 return -1 ;
10841090 }
10851091 if ((uint64_t )count > (uint64_t )PY_SSIZE_T_MAX - (uint64_t )replayed ) {
0 commit comments