Lz4: Protection fault when decompressing highly compressible frames > 2GB

Created on 26 Apr 2018  路  3Comments  路  Source: lz4/lz4

dctx->dictSize is being cast from a size_t to an int at points along the LZ4F_decompress->LZ4_decompress_generic call chain. When a frame containing extremely compressible data that will expand to more than 2GB is sent to LZ4F_decompress, a protection fault is generated in LZ4_decompress_generic at this line: memcpy(op, dictEnd - copySize, copySize);

I removed the (int) casts for the dictionary size throughout and that seems to fix the problem. I'm not familiar enough with the code to know if there are any side effects of that, however.

The code below is a new main() that can be pasted into frametest.c to reproduce the problem. If the random fill block is enabled there is no fault.

int main()
{
    for (size_t testSize = 1900LL MB; 1; testSize += 100LL MB)
    {
        LZ4F_preferences_t preferences;
        memset(&preferences, 0, sizeof(preferences));
        preferences.frameInfo.blockSizeID = LZ4F_max4MB;
        preferences.frameInfo.contentSize = testSize;
        size_t maxCompressed = LZ4F_compressFrameBound(testSize, &preferences);
        void* uncompressed = malloc(testSize);
        if (uncompressed == 0)
        {
            DISPLAY("No uncompressed buffer");
            return -1;
        }
        DISPLAY("Filling %llu\n", testSize);
        memset(uncompressed, 0, testSize);
#if 0
        char* pos = (char*)uncompressed;
        char* end = pos + testSize;
        while (pos + sizeof(int) < end)
        {
            *(int*)pos++ = rand();
        }
#endif
        void* compressed = malloc(maxCompressed);
        if (compressed == 0)
        {
            DISPLAY("No compressed buffer");
            return -1;
        }
        size_t compressedSize = LZ4F_compressFrame(compressed, maxCompressed, uncompressed, testSize, &preferences);
        if (LZ4F_isError(compressedSize))
        {
            DISPLAY("Compression failed: %llu\n", compressedSize);
            return -1;
        }
        DISPLAY("Compressed %llu to %llu\n", testSize, compressedSize);
        LZ4F_dctx* context;
        size_t result = LZ4F_createDecompressionContext(&context, LZ4F_VERSION);
        if (LZ4F_isError(result))
        {
            DISPLAY("Decompression context failed (error %llu : %s)", result, LZ4F_getErrorName(result));
            return -1;
        }
        size_t uncompressedSize = testSize;
        LZ4F_decompressOptions_t options;
        memset(&options, 0, sizeof(options));
        result = LZ4F_decompress(context, uncompressed, &uncompressedSize, compressed, &compressedSize, &options);
        if (LZ4F_isError(result))
        {
            DISPLAY("Decompression failed (error %llu : %s)", result, LZ4F_getErrorName(result));
            return -1;
        }
        if (uncompressedSize == testSize)
        {
            DISPLAY("Decompression succeeded\n");
        }
        LZ4F_freeDecompressionContext(context);
        free(compressed);
        free(uncompressed);
    }
    return 0;
}
bug

Most helpful comment

@kilodyne, thanks for the report!

I confirmed the issue with your example reproduction and I've put up a fix. Should get that fixed for you soon!

All 3 comments

Thanks for the notification and very clear reproduction case @kilodyne !
We'll look into it !

@kilodyne, thanks for the report!

I confirmed the issue with your example reproduction and I've put up a fix. Should get that fixed for you soon!

@felixhandte great, thank you for working on this so quickly.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ppodolsky picture ppodolsky  路  4Comments

vonHabsi picture vonHabsi  路  7Comments

terrelln picture terrelln  路  6Comments

KyleLangley picture KyleLangley  路  4Comments

felix2ch picture felix2ch  路  3Comments