facebook / facebook/zstd

Undefined behavior in `ZSTD_decompress` for certain inputs

Open
#3,506 0 comments 3 reactions 1 assignee Claimed by @daniellerozenblit View on GitHub
Dominant language
C
Stars
27.9k
Forks
2.6k
Avg merge
1d 3h
Merged PRs (30d)
8

Description

**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](https://github.com/facebook/zstd/files/10728643/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: https://github.com/facebook/zstd/issues/2110, https://github.com/facebook/zstd/issues/3236

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.