assimp / assimp/assimp

Bug: Heap-buffer-overflow in Assimp::Compression::decompressBlock

Open
#6,624 0 comments 0 reactions 0 assignees View on GitHub
Bug Fuzzer X
Dominant language
C++
Stars
13.2k
Forks
3.2k
Avg merge
2d 8h
Merged PRs (30d)
21

Description

**Describe the bug**

A heap-buffer-overflow vulnerability exists in Assimp during data decompression, the vulnerable function is `Assimp::Compression::decompressBlock` located in `code/Common/Compression.cpp` line 188, and the actual crash occurs at `contrib/zlib/inflate.c` line 1077.

**To Reproduce**
Steps to reproduce the behavior:

1. Clone the assimp repository and build it refer to oss-fuzz.

```shell
export CC=clang \
CXX=clang++ \
CFLAGS='-fsanitize=address -O0 -g' \
CXXFLAGS='-fsanitize=address -O0 -g' \
LIB_FUZZING_ENGINE='-fsanitize=fuzzer'
```

2. Run the PoC using assimp_fuzzer:

[poc.zip](https://github.com/user-attachments/files/27237535/poc.zip)

The PoC is provided as a zip archive. After extracting, run:

```shell
./assimp_fuzzer ./poc.x
```

**Cause**

1. The `XFileImporter` allocates a fixed-size input buffer based on the file size (`fileSize + 1`).
2. When parsing the file, the decompression logic passes the full buffer to `zlib` without validating whether the compressed data adheres to the declared length.
3. The malformed compressed data incorrectly signals that there are more bytes to read than the buffer contains.
4. `zlib`'s `inflate` function, following the instructions in the malformed data, continues reading past the end of the buffer, triggering the overflow.

**ASAN Report**

```
=================================================================
==2013338==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x518000000f88 at pc 0x627ccfa1f229 bp 0x7ffde3f0ded0 sp 0x7ffde3f0dec8
READ of size 1 at 0x518000000f88 thread T0
#0 0x627ccfa1f228 in inflate /home/hexijie/fuzz/project/assimp/contrib/zlib/inflate.c:1077:17
#1 0x627ccf929eae in Assimp::Compression::decompressBlock(void const*, unsigned long, char*, unsigned long) /home/hexijie/fuzz/project/assimp/code/Common/Compression.cpp:188:15
#2 0x627ccf6a1837 in Assimp::XFileParser::XFileParser(std::vector> const&) /home/hexijie/fuzz/project/assimp/code/AssetLib/X/XFileParser.cpp:201:36
#3 0x627ccf6940ff in Assimp::XFileImporter::InternReadFile(std::__cxx11::basic_string, std::allocator> const&, aiScene*, Assimp::IOSystem*) /home/hexijie/fuzz/project/assimp/code/AssetLib/X/XFileImporter.cpp:111:17
#4 0x627ccea7498b in Assimp::BaseImporter::ReadFile(Assimp::Importer*, std::__cxx11::basic_string, std::allocator> const&, Assimp::IOSystem*) /home/hexijie/fuzz/project/assimp/code/Common/BaseImporter.cpp:131:9
#5 0x627cce5aa0cf in Assimp::Importer::ReadFile(char const*, unsigned int) /home/hexijie/fuzz/project/assimp/code/Common/Importer.cpp:709:30
#6 0x627cce5a7a19 in Assimp::Importer::ReadFileFromMemory(void const*, unsigned long, unsigned int, char const*) /home/hexijie/fuzz/project/assimp/code/Common/Importer.cpp:507:9
#7 0x627cce5a3705 in LLVMFuzzerTestOneInput /home/hexijie/fuzz/project/assimp/build/../fuzz/assimp_fuzzer.cc:62:34
#8 0x627cce4aeb24 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/hexijie/fuzz/fuzzers/assimp_fuzzer+0x313b24) (BuildId: 3707c90bb983c84eac8c279d24589825b44e573a)
#9 0x627cce497c56 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) (/home/hexijie/fuzz/fuzzers/assimp_fuzzer+0x2fcc56) (BuildId: 3707c90bb983c84eac8c279d24589825b44e573a)
#10 0x627cce49d70a in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/home/hexijie/fuzz/fuzzers/assimp_fuzzer+0x30270a) (BuildId: 3707c90bb983c84eac8c279d24589825b44e573a)
#11 0x627cce4c7ec6 in main (/home/hexijie/fuzz/fuzzers/assimp_fuzzer+0x32cec6) (BuildId: 3707c90bb983c84eac8c279d24589825b44e573a)
#12 0x79b2f902a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#13 0x79b2f902a28a in __libc_start_main csu/../csu/libc-start.c:360:3
#14 0x627cce492824 in _start (/home/hexijie/fuzz/fuzzers/assimp_fuzzer+0x2f7824) (BuildId: 3707c90bb983c84eac8c279d24589825b44e573a)

0x518000000f88 is located 0 bytes after 776-byte region [0x518000000c80,0x518000000f88)
allocated by thread T0 here:
#0 0x627cce5a1251 in operator new(unsigned long) (/home/hexijie/fuzz/fuzzers/assimp_fuzzer+0x406251) (BuildId: 3707c90bb983c84eac8c279d24589825b44e573a)
#1 0x627ccea8789e in std::__new_allocator::allocate(unsigned long, void const*) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/new_allocator.h:151:27
#2 0x627ccea87233 in std::allocator_traits>::allocate(std::allocator&, unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/alloc_traits.h:482:20
#3 0x627ccea87233 in std::_Vector_base>::_M_allocate(unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/stl_vector.h:381:20
#4 0x627ccea86b79 in std::vector>::_M_default_append(unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/vector.tcc:663:34
#5 0x627ccea7d2b5 in std::vector>::resize(unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/stl_vector.h:1016:4
#6 0x627ccf69401e in Assimp::XFileImporter::InternReadFile(std::__cxx11::basic_string, std::allocator> const&, aiScene*, Assimp::IOSystem*) /home/hexijie/fuzz/project/assimp/code/AssetLib/X/XFileImporter.cpp:106:13
#7 0x627ccea7498b in Assimp::BaseImporter::ReadFile(Assimp::Importer*, std::__cxx11::basic_string, std::allocator> const&, Assimp::IOSystem*) /home/hexijie/fuzz/project/assimp/code/Common/BaseImporter.cpp:131:9
#8 0x627cce5aa0cf in Assimp::Importer::ReadFile(char const*, unsigned int) /home/hexijie/fuzz/project/assimp/code/Common/Importer.cpp:709:30
#9 0x627cce5a7a19 in Assimp::Importer::ReadFileFromMemory(void const*, unsigned long, unsigned int, char const*) /home/hexijie/fuzz/project/assimp/code/Common/Importer.cpp:507:9
#10 0x627cce5a3705 in LLVMFuzzerTestOneInput /home/hexijie/fuzz/project/assimp/build/../fuzz/assimp_fuzzer.cc:62:34
#11 0x627cce4aeb24 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/hexijie/fuzz/fuzzers/assimp_fuzzer+0x313b24) (BuildId: 3707c90bb983c84eac8c279d24589825b44e573a)
#12 0x627cce497c56 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) (/home/hexijie/fuzz/fuzzers/assimp_fuzzer+0x2fcc56) (BuildId: 3707c90bb983c84eac8c279d24589825b44e573a)
#13 0x627cce49d70a in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/home/hexijie/fuzz/fuzzers/assimp_fuzzer+0x30270a) (BuildId: 3707c90bb983c84eac8c279d24589825b44e573a)
#14 0x627cce4c7ec6 in main (/home/hexijie/fuzz/fuzzers/assimp_fuzzer+0x32cec6) (BuildId: 3707c90bb983c84eac8c279d24589825b44e573a)
#15 0x79b2f902a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#16 0x79b2f902a28a in __libc_start_main csu/../csu/libc-start.c:360:3
#17 0x627cce492824 in _start (/home/hexijie/fuzz/fuzzers/assimp_fuzzer+0x2f7824) (BuildId: 3707c90bb983c84eac8c279d24589825b44e573a)

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/hexijie/fuzz/project/assimp/contrib/zlib/inflate.c:1077:17 in inflate
Shadow bytes around the buggy address:
0x518000000d00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x518000000d80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x518000000e00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x518000000e80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x518000000f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x518000000f80: 00[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x518000001000: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x518000001080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x518000001100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x518000001180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x518000001200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==2013338==ABORTING
```

**Screenshots**

Image

The `mBuffer` is allocated with a size of `fileSize + 1` (0x307 + 1 = 776 bytes). The valid memory range is confirmed in the debugger:

- Start address: `0x518000000c80`
- Last valid address: `0x518000000f87`
- The address `0x518000000f88` is **immediately after the valid region**.

Image

At the breakpoint in `inflate.c:1076` (just before the crash), the `z_stream` structure reveals:

- `next_in = 0x518000000f85`: The current read pointer is near the end of the buffer.
- `avail_in = 0x4`: The decompressor still expects **4 more bytes** of input data.
- Calculation: `0x518000000f85 + 0x4 = 0x518000000f89`, which is well beyond the valid buffer range.
- This mismatch will cause `inflate` to read from `0x518000000f88`, triggering the heap buffer overflow reported by AddressSanitizer.

**Platform (please complete the following information):**

- OS: Ubuntu 22.04 (x86_64)
- Version: commit 17c12da558d23d70e4c728b30958e95cd616cbcb

Contributor guide

Open the contributing guide

Research direction

Start with code/Common/Compression.cpp at decompressBlock and trace its caller in code/AssetLib/X/XFileImporter.cpp; compare the input bounds with the zlib read reported at contrib/zlib/inflate.c:1077. Build with the shown AddressSanitizer settings, extract poc.zip, and run ./assimp_fuzzer ./poc.x. Done means the malformed input no longer produces the reported heap-buffer-overflow.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.