diff --git a/contrib/seekable_format/tests/seekable_tests.c b/contrib/seekable_format/tests/seekable_tests.c index 809ea6425eb..b535f5571ef 100644 --- a/contrib/seekable_format/tests/seekable_tests.c +++ b/contrib/seekable_format/tests/seekable_tests.c @@ -6,6 +6,7 @@ #include #include "../zstd_seekable.h" +#include "zstd_errors.h" /* ZSTD_seekable_customFile implementation that reads/seeks a buffer while keeping track of total bytes read */ @@ -365,6 +366,28 @@ int main(int argc, const char** argv) } printf("Success!\n"); + printf("Test %u - reject seek table with numFrames beyond format max: ", testNb++); + { /* A corrupt footer claiming numFrames > ZSTD_SEEKABLE_MAXFRAMES must be + * rejected up front: such a value overflows the seek table size math. */ + const uint8_t compressed_data[17] = { + 0x5e, 0x2a, 0x4d, 0x18, /* skippable frame magic (| 0xE) */ + 0x09, 0x00, 0x00, 0x00, /* skippable frame size = 9 */ + 0x01, 0x00, 0x00, 0x08, /* footer: numFrames = 0x08000001 */ + 0x00, /* footer: seek table descriptor */ + 0xb1, 0xea, 0x92, 0x8f, /* seekable magic number */ + }; + + ZSTD_seekable* const stream = ZSTD_seekable_create(); + assert(stream != NULL); + { size_t const status = ZSTD_seekable_initBuff(stream, compressed_data, sizeof(compressed_data)); + if (ZSTD_getErrorCode(status) != ZSTD_error_corruption_detected) { + ZSTD_seekable_free(stream); + goto _test_error; + } } + ZSTD_seekable_free(stream); + } + printf("Success!\n"); + /* TODO: Add more tests */ printf("Finished tests\n"); return 0; diff --git a/contrib/seekable_format/zstdseek_decompress.c b/contrib/seekable_format/zstdseek_decompress.c index ab9088a1061..dc66c35f7f9 100644 --- a/contrib/seekable_format/zstdseek_decompress.c +++ b/contrib/seekable_format/zstdseek_decompress.c @@ -397,6 +397,13 @@ static size_t ZSTD_seekable_loadSeekTable(ZSTD_seekable* zs) U32 const frameSize = tableSize + ZSTD_seekTableFooterSize + ZSTD_SKIPPABLEHEADERSIZE; U32 remaining = frameSize - ZSTD_seekTableFooterSize; /* don't need to re-read footer */ + + /* numFrames is bounded by the format (the compressor never writes more + * than ZSTD_SEEKABLE_MAXFRAMES). A larger value can only come from a + * corrupt footer and overflows the tableSize/frameSize math above, so + * reject it before those values are used to seek or size the table. */ + if (numFrames > ZSTD_SEEKABLE_MAXFRAMES) return ERROR(corruption_detected); + { U32 const toRead = MIN(remaining, SEEKABLE_BUFF_SIZE); CHECK_IO(src.seek(src.opaque, -(S64)frameSize, SEEK_END)); CHECK_IO(src.read(src.opaque, zs->inBuff, toRead));