facebook/zstd

Undefined behavior in `ZSTD_decompress` for certain inputs

squeek502 opened this issue · 0 comments

Describe the bug
When compiled with clang and -fsanitize=undefined, ZSTD_decompress will hit undefined behavior with certain inputs:

/decompress/zstd_decompress_block.c:1696:45: runtime error: applying non-zero offset 79823856 to null pointer

To Reproduce

  1. Inputs that trigger the UB: zstd-non-zero-offset-inputs.zip (found when fuzzing a Zig implementation of a zstd decompressor)
  2. Compile zstd with clang via make lib CFLAGS="-fsanitize=undefined -fPIC"
  3. Compile examples with make LDFLAGS="-fsanitize=undefined"
  4. Run one of the inputs through simple_decompression, e.g. ./simple_decompression 'id:000032,sig:06,src:000673,time:3423388,execs:797038,op:havoc,rep:2'

Example of the output with DEBUGLEVEL=10

.//decompress/zstd_decompress.c: ZSTD_getFrameHeader_advanced: minInputSize = 5, srcSize = 39 
.//decompress/zstd_decompress.c: ZSTD_decompressMultiFrame 
.//decompress/zstd_decompress.c: reading magic number FD2FB528 (expecting FD2FB528) 
.//decompress/zstd_decompress.c: ZSTD_decompressFrame (srcSize:39) 
.//decompress/zstd_decompress.c: ZSTD_getFrameHeader_advanced: minInputSize = 5, srcSize = 9 
.//decompress/zstd_decompress_block.c: ZSTD_decompressBlock_internal (size : 20) 
.//decompress/zstd_decompress_block.c: ZSTD_decodeLiteralsBlock 
.//decompress/zstd_decompress_block.c: ZSTD_decodeLiteralsBlock : cSize=1, nbLiterals=0 
.//decompress/zstd_decompress_block.c: ZSTD_decodeSeqHeaders 
.//decompress/zstd_decompress_block.c: ZSTD_getLongOffsetsShare: (tableLog=5) 
.//decompress/zstd_decompress_block.c: ZSTD_decompressSequencesLong 
.//decompress/zstd_decompress_block.c: ZSTD_initFseState : val=5 using 5 bits 
.//decompress/zstd_decompress_block.c: ZSTD_initFseState : val=24 using 5 bits 
.//decompress/zstd_decompress_block.c: ZSTD_initFseState : val=0 using 0 bits 
.//decompress/zstd_decompress_block.c: seq: litL=0, matchL=3, offset=8 
/decompress/zstd_decompress_block.c:1696:45: runtime error: applying non-zero offset 94193544721696 to null pointer
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /decompress/zstd_decompress_block.c:1696:45 in 
.//decompress/zstd_decompress_block.c: seq: litL=2, matchL=3, offset=1 
.//decompress/zstd_decompress_block.c: seq: litL=0, matchL=3, offset=4 
.//decompress/zstd_decompress_block.c: seq: litL=0, matchL=3, offset=8 
.//decompress/zstd_decompress_block.c: seq: litL=0, matchL=3, offset=2 
.//decompress/zstd_decompress_block.c: seq: litL=0, matchL=3, offset=4 
.//decompress/zstd_decompress_block.c: seq: litL=0, matchL=3, offset=8 
.//decompress/zstd_decompress_block.c: seq: litL=0, matchL=3, offset=2 
.//decompress/zstd_decompress_block.c: seq: litL=2, matchL=3, offset=8 
.//decompress/zstd_decompress_block.c:884: ERROR!: check sequenceLength > (size_t)(oend - op) failed, returning ERROR(dstSize_tooSmall): last match must fit within dstBuffer
simple_decompression.c:40 CHECK(!ZSTD_isError(err)) failed: Destination buffer is too small

Expected behavior
No UB

Desktop:

  • OS: Linux
  • Compiler clang

Additional context

UB does not happen with the streaming_decompression example for these inputs.

Related issues: #2110, #3236