llvm / llvm/llvm-project

XCOFF YAML emitter stack buffer overflow via unchecked section name length

Open
#187,749 0 comments 0 reactions 0 assignees View on GitHub
code-cleanup generated by fuzzer objectyaml
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

**Bug summary**

The `writeName()` function in LLVM's XCOFF YAML-to-object emitter (`XCOFFEmitter.cpp`) copies section names into a fixed 8-byte stack buffer using `memcpy` without bounds checking. A YAML input with a section name longer than 8 characters causes a stack buffer overflow write. The bug is confirmed with a 91-byte YAML PoC containing a 12-character section name (`.longname123`), producing a 4-byte overwrite past the buffer. As a developer tool (yaml2obj/llvm-object-yaml-fuzzer), real-world attack surface is narrow.

**PoC / reproduction**

- Entrypoint: `llvm-object-yaml-fuzzer`

Reproduce via OSS-Fuzz (uses `reproduce.py`, which reads the PoC file above):

```bash
export OSS_FUZZ_DIR=~/oss-fuzz
python3 reproduce.py poc.bin
```

PoC contents:

```text
--- !XCOFF
FileHeader:
MagicNumber: 0x1DF
Sections:
- Name: .longname123
Flags: []
```

**ASAN backtrace**

```text
+ FUZZER=llvm-object-yaml-fuzzer
+ shift
+ '[' '!' -v TESTCASE ']'
+ TESTCASE=/testcase
+ '[' '!' -f /testcase ']'
+ export RUN_FUZZER_MODE=interactive
+ RUN_FUZZER_MODE=interactive
+ export FUZZING_ENGINE=libfuzzer
+ FUZZING_ENGINE=libfuzzer
+ export SKIP_SEED_CORPUS=1
+ SKIP_SEED_CORPUS=1
+ run_fuzzer llvm-object-yaml-fuzzer -runs=100 /testcase
vm.mmap_rnd_bits = 28
/out/llvm-object-yaml-fuzzer -- -rss_limit_mb=2560 -timeout=25 -runs=100 /testcase < /dev/null
INFO: libFuzzer ignores flags that start with '--'
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 1432731312
INFO: Loaded 1 modules (370164 inline 8-bit counters): 370164 [0x555558a8efe0, 0x555558ae95d4),
INFO: Loaded 1 PC tables (370164 PCs): 370164 [0x555558ae95d8,0x55555908f518),
/out/llvm-object-yaml-fuzzer: Running 1 inputs 100 time(s) each.
Running: /testcase
=================================================================
==14==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffff7b19ab8 at pc 0x555556b2a05e bp 0x7fffffffdab0 sp 0x7fffffffd270
WRITE of size 12 at 0x7ffff7b19ab8 thread T0
SCARINESS: 60 (multi-byte-write-stack-buffer-overflow)
#0 0x555556b2a05d in __asan_memcpy /src/llvm-project/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp:63:3
#1 0x5555574e0039 in (anonymous namespace)::writeName(llvm::StringRef, llvm::support::endian::Writer) /src/llvm-project/llvm/lib/ObjectYAML/XCOFFEmitter.cpp:90:3
#2 0x5555574d4c1a in writeSectionHeaders /src/llvm-project/llvm/lib/ObjectYAML/XCOFFEmitter.cpp:531:5
#3 0x5555574d4c1a in (anonymous namespace)::XCOFFWriter::writeXCOFF() /src/llvm-project/llvm/lib/ObjectYAML/XCOFFEmitter.cpp:877:5
#4 0x5555574cd71c in llvm::yaml::yaml2xcoff(llvm::XCOFFYAML::Object&, llvm::raw_ostream&, llvm::function_ref) /src/llvm-project/llvm/lib/ObjectYAML/XCOFFEmitter.cpp:896:17
#5 0x555556b71493 in llvm::yaml::convertYAML(llvm::yaml::Input&, llvm::raw_ostream&, llvm::function_ref, unsigned int, unsigned long) /src/llvm-project/llvm/lib/ObjectYAML/yaml2obj.cpp:51:14
#6 0x555556b71d24 in llvm::yaml::yaml2ObjectFile(llvm::SmallVectorImpl&, llvm::StringRef, llvm::function_ref) /src/llvm-project/llvm/lib/ObjectYAML/yaml2obj.cpp:72:8
#7 0x555556b702d6 in LLVMFuzzerTestOneInput /src/llvm-project/llvm/tools/llvm-object-yaml-fuzzer/llvm-object-yaml-fuzzer.cpp:27:7
#8 0x555556a0d6bd in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:619:13
#9 0x5555569f73d2 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:329:6
#10 0x5555569fd2a0 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:865:9
#11 0x555556a29e32 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
#12 0x7ffff7c1d082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082) (BuildId: 5792732f783158c66fb4f3756458ca24e46e827d)
#13 0x5555569f04bd in _start (/out/llvm-object-yaml-fuzzer+0x149c4bd)

DEDUP_TOKEN: __asan_memcpy--(anonymous namespace)::writeName(llvm::StringRef, llvm::support::endian::Writer)--writeSectionHeaders
Address 0x7ffff7b19ab8 is located in stack of thread T0 at offset 56 in frame
 #0 0x5555574dff1f in (anonymous namespace)::writeName(llvm::StringRef, llvm::support::endian::Writer) /src/llvm-project/llvm/lib/ObjectYAML/XCOFFEmitter.cpp:86

DEDUP_TOKEN: (anonymous namespace)::writeName(llvm::StringRef, llvm::support::endian::Writer)
This frame has 3 object(s):
[32, 33) 'value.addr'
[48, 56) 'Name.sroa.0' (line 87) <== Memory access at offset 56 overflows this variable
[80, 81) 'SrcName' (line 89)
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /src/llvm-project/llvm/lib/ObjectYAML/XCOFFEmitter.cpp:90:3 in (anonymous namespace)::writeName(llvm::StringRef, llvm::support::endian::Writer)
Shadow bytes around the buggy address:
0x7ffff7b19800: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
0x7ffff7b19880: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
0x7ffff7b19900: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
0x7ffff7b19980: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
0x7ffff7b19a00: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
=>0x7ffff7b19a80: f1 f1 f1 f1 f8 f2 00[f2]f2 f2 01 f3 f3 f3 f3 f3
0x7ffff7b19b00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7ffff7b19b80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7ffff7b19c00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7ffff7b19c80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7ffff7b19d00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
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
==14==ABORTING
```

**Fix (diff)**

```diff
diff --git a/llvm/lib/ObjectYAML/XCOFFEmitter.cpp b/llvm/lib/ObjectYAML/XCOFFEmitter.cpp
index 5d7d6a114..e83a80fff 100644
--- a/llvm/lib/ObjectYAML/XCOFFEmitter.cpp
+++ b/llvm/lib/ObjectYAML/XCOFFEmitter.cpp
@@ -87,7 +87,10 @@ static void writeName(StringRef StrName, support::endian::Writer W) {
char Name[XCOFF::NameSize];
memset(Name, 0, XCOFF::NameSize);
char SrcName[] = "";
- memcpy(Name, StrName.size() ? StrName.data() : SrcName, StrName.size());
+ size_t CopySize = StrName.size();
+ if (CopySize > XCOFF::NameSize)
+ CopySize = XCOFF::NameSize;
+ memcpy(Name, CopySize ? StrName.data() : SrcName, CopySize);
ArrayRef NameRef(Name, XCOFF::NameSize);
W.write(NameRef);
}
```
**Background of that issue**
This bug was found as a part of an Anthropic research into the use of large language models for automated vulnerability discovery in open source software. Anthropic then engaged Trail of Bits to independently triage and validate those issues.

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.