diff --git a/contrib/seekable_format/tests/seekable_tests.c b/contrib/seekable_format/tests/seekable_tests.c index cc4c83295b9..d20e8d9df59 100644 --- a/contrib/seekable_format/tests/seekable_tests.c +++ b/contrib/seekable_format/tests/seekable_tests.c @@ -1,5 +1,6 @@ #include #include +#include #include // malloc #include #include @@ -54,6 +55,29 @@ static int seekBuffWithTotal(void* opaque, long long offset, int origin) return 0; } +static int testFrameSizeBounds(const ZSTD_seekable* stream, const ZSTD_seekTable* table) +{ + unsigned const nbFrames = ZSTD_seekTable_getNumFrames(table); + unsigned const indices[] = {nbFrames, nbFrames + 1, UINT_MAX}; + size_t i; + for (i = 0; i < sizeof(indices) / sizeof(indices[0]); ++i) { + unsigned const index = indices[i]; + size_t const results[] = { + ZSTD_seekable_getFrameCompressedSize(stream, index), + ZSTD_seekable_getFrameDecompressedSize(stream, index), + ZSTD_seekTable_getFrameCompressedSize(table, index), + ZSTD_seekTable_getFrameDecompressedSize(table, index), + }; + size_t j; + for (j = 0; j < sizeof(results) / sizeof(results[0]); ++j) { + if (ZSTD_getErrorCode(results[j]) != ZSTD_error_frameIndex_tooLarge) { + return 1; + } + } + } + return 0; +} + /* Basic unit tests for zstd seekable format */ int main(int argc, const char** argv) { @@ -129,12 +153,14 @@ int main(int argc, const char** argv) unsigned const fo1idx = ZSTD_seekTable_offsetToFrameIndex(zst, 1); assert(fo1idx == 0); + int const boundsStatus = testFrameSizeBounds(stream, zst); free(inBuffer); free(seekBuffer); free(outBuffer); ZSTD_seekable_freeCStream(zscs); ZSTD_seekTable_free(zst); ZSTD_seekable_free(stream); + if (boundsStatus != 0) goto _test_error; } printf("Success!\n"); @@ -172,6 +198,17 @@ int main(int argc, const char** argv) goto _test_error; } } + /* This valid seek table has no frames, so even index 0 is invalid. */ + { ZSTD_seekTable* const table = ZSTD_seekTable_create_fromSeekable(stream); + assert(table != NULL); + assert(ZSTD_seekTable_getNumFrames(table) == 0); + int const boundsStatus = testFrameSizeBounds(stream, table); + ZSTD_seekTable_free(table); + if (boundsStatus != 0) { + ZSTD_seekable_free(stream); + goto _test_error; + } } + /* Should return an error, but not hang */ { const size_t offset = 2; size_t const status = ZSTD_seekable_decompress(stream, uncompressed_data, uncompressed_size, offset); diff --git a/contrib/seekable_format/zstd_seekable.h b/contrib/seekable_format/zstd_seekable.h index e24e4e33edd..5449fd9292b 100644 --- a/contrib/seekable_format/zstd_seekable.h +++ b/contrib/seekable_format/zstd_seekable.h @@ -157,7 +157,7 @@ ZSTDLIB_API size_t ZSTD_seekable_writeSeekTable(ZSTD_frameLog* fl, ZSTD_outBuffe * An `offset` past the end of the data is an error. * * The seek table access functions can be used to obtain the data contained -* in the seek table. If frameIndex is larger than the value returned by +* in the seek table. If frameIndex is greater than or equal to * ZSTD_seekable_getNumFrames(), they will return error codes checkable with * ZSTD_isError(). Note that since the offset access functions return * unsigned long long instead of size_t, in this case they will instead return