llvm / llvm/llvm-project

Vector Index Out-of-Bounds in BPF `fillGenericConstant` via Missing Bounds Check

Open
#187,756 1 comment 0 reactions 0 assignees View on GitHub
backend:BPF confirmed crash-on-valid generated by fuzzer
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

### Bug summary

LLVM’s BPF backend has a vector index out-of-bounds in BPFDAGToDAGISel::fillGenericConstant (llvm/lib/Target/BPF/BPFISelDAGToDAG.cpp). The function pre-sizes a std::vector (Vals) and then writes bytes at Vals[Offset + i]
based on offsets computed from malformed IR/bitcode, but it does not check that Offset + Size fits within Vals. With libc++ hardening enabled (as in oss-fuzz builds), this triggers a “vector[] index out of bounds” trap; without
hardening, it’s an out-of-bounds write (memory corruption).

### PoC / reproduction

- Entrypoint: llvm-isel-fuzzer--bpf-O2

Reproduce via OSS-Fuzz using the repo helper (this picks the sibling PoC automatically):

`python3 reproduce.py poc.bin`

PoC contents (binary bitcode; base64):

```
python3 - <<'PY'
import base64

b64 = '''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
Zi9kZXYvc3RkaW4AAAAAAAAA'''
open('poc.bin', 'wb').write(base64.b64decode(b64))
print('wrote poc.bin')
PY
```
ASAN backtrace

```
_llvm-dis-fuzzer/siblings/llvm-isel-fuzzer--bpf-O2.poc:/testcase -t gcr.io/oss-fuzz-base/base-runner:latest reproduce llvm-isel-fuzzer--bpf-O2 -runs=100.
+ FUZZER=llvm-isel-fuzzer--bpf-O2
+ 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-isel-fuzzer--bpf-O2 -runs=100 /testcase
vm.mmap_rnd_bits = 28
/out/llvm-isel-fuzzer--bpf-O2 -- -rss_limit_mb=2560 -timeout=25 -runs=100 /testcase < /dev/null
/out/llvm-isel-fuzzer: Injected args: -mtriple=bpf -O2
INFO: found LLVMFuzzerCustomMutator (0x55555c0a8f30). Disabling -len_control by default.
INFO: libFuzzer ignores flags that start with '--'
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 2039938526
INFO: Loaded 1 modules (1882635 inline 8-bit counters): 1882635 [0x55556557ec20, 0x55556574a62b),
INFO: Loaded 1 PC tables (1882635 PCs): 1882635 [0x55556574a630,0x5555674046e0),
/out/llvm-isel-fuzzer--bpf-O2: Running 1 inputs 100 time(s) each.
Running: /testcase
AddressSanitizer:DEADLYSIGNAL
=================================================================
==14==ERROR: AddressSanitizer: ILL on unknown address 0x55555ce6ae4f (pc 0x55555ce6ae4f bp 0x7fffffffcfa0 sp 0x7fffffffcf30 T0)
SCARINESS: 10 (signal)
#0 0x55555ce6ae4f in __clang_trap_msg$libc++$/usr/local/bin/../include/c++/v1/__vector/vector.h:414: libc++ Hardening assertion __n < size() failed: vector[] index out of bounds (/out/llvm-isel-fuzzer--bpf-O2+0x7916e4f)
#1 0x55555ce6ae4f in /usr/local/bin/../include/c++/v1/__vector/vector.h:0:0 operator[]
#2 0x55555ce6ae4f in /usr/local/bin/../include/c++/v1/__vector/vector.h:414:5 (anonymous namespace)::BPFDAGToDAGISel::fillGenericConstant(llvm::DataLayout const&, llvm::Constant const*, std::__1::vector>&, unsigned long)
#3 0x55555ce6ae4f in /src/llvm-project/llvm/lib/Target/BPF/BPFISelDAGToDAG.cpp:381:7 (/out/llvm-isel-fuzzer--bpf-O2+0x7916e4f)
#4 0x55555ce6c173 in (anonymous namespace)::BPFDAGToDAGISel::fillConstantStruct(llvm::DataLayout const&, llvm::ConstantStruct const*, std::__1::vector>&, int) /src/llvm-project/
llvm/lib/Target/BPF/BPFISelDAGToDAG.cpp:432:9
#5 0x55555ce6a3c2 in fillConstantArray /src/llvm-project/llvm/lib/Target/BPF/BPFISelDAGToDAG.cpp:417:9
#6 0x55555ce6a3c2 in (anonymous namespace)::BPFDAGToDAGISel::fillGenericConstant(llvm::DataLayout const&, llvm::Constant const*, std::__1::vector>&, unsigned long) /src/llvm-
project/llvm/lib/Target/BPF/BPFISelDAGToDAG.cpp:392:12
#7 0x55555ce68e82 in (anonymous namespace)::BPFDAGToDAGISel::getConstantFieldValue(llvm::GlobalAddressSDNode const*, unsigned long, unsigned long, unsigned char*) /src/llvm-project/llvm/lib/Target/BPF/BPFISelDAGToDAG.cpp:339:9
#8 0x55555ce625f8 in PreprocessLoad /src/llvm-project/llvm/lib/Target/BPF/BPFISelDAGToDAG.cpp:251:11
#9 0x55555ce625f8 in (anonymous namespace)::BPFDAGToDAGISel::PreprocessISelDAG() /src/llvm-project/llvm/lib/Target/BPF/BPFISelDAGToDAG.cpp:306:7
#10 0x555560890d18 in llvm::SelectionDAGISel::DoInstructionSelection() /src/llvm-project/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp:1305:3
#11 0x55556088ca61 in llvm::SelectionDAGISel::CodeGenAndEmitDAG() /src/llvm-project/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp:1165:5
#12 0x555560885d4b in llvm::SelectionDAGISel::SelectAllBasicBlocks(llvm::Function const&) /src/llvm-project/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp:1957:7
#13 0x55556087b049 in llvm::SelectionDAGISel::runOnMachineFunction(llvm::MachineFunction&) /src/llvm-project/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp:658:3
#14 0x555560872b93 in llvm::SelectionDAGISelLegacy::runOnMachineFunction(llvm::MachineFunction&) /src/llvm-project/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp:400:20
#15 0x55555e19259d in llvm::MachineFunctionPass::runOnFunction(llvm::Function&) /src/llvm-project/llvm/lib/CodeGen/MachineFunctionPass.cpp:112:10
#16 0x55555f822184 in llvm::FPPassManager::runOnFunction(llvm::Function&) /src/llvm-project/llvm/lib/IR/LegacyPassManager.cpp:1398:27
#17 0x55555f83d934 in llvm::FPPassManager::runOnModule(llvm::Module&) /src/llvm-project/llvm/lib/IR/LegacyPassManager.cpp:1444:16
#18 0x55555f824150 in runOnModule /src/llvm-project/llvm/lib/IR/LegacyPassManager.cpp:1513:27
#19 0x55555f824150 in llvm::legacy::PassManagerImpl::run(llvm::Module&) /src/llvm-project/llvm/lib/IR/LegacyPassManager.cpp:531:44
#20 0x55555c0a9848 in LLVMFuzzerTestOneInput /src/llvm-project/llvm/tools/llvm-isel-fuzzer/llvm-isel-fuzzer.cpp:103:6
#21 0x55555bf437ad in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:619:13
#22 0x55555bf2d4c2 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:329:6
#23 0x55555bf33390 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:865:9
#24 0x55555bf5ff22 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
#25 0x7ffff7c1d082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082) (BuildId: 5792732f783158c66fb4f3756458ca24e46e827d)
#26 0x55555bf265ad in _start (/out/llvm-isel-fuzzer--bpf-O2+0x69d25ad)

DEDUP_TOKEN: __clang_trap_msg$libc++$/usr/local/bin/../include/c++/v1/__vector/vector.h:414: libc++ Hardening assertion __n < size() failed: vector[] index out of bounds--/usr/local/bin/../include/c++/v1/__vector/vector.h:0:0--/usr/
local/bin/../include/c++/v1/__vector/vector.h:414:5
==14==Register values:
rax = 0x00007ffff7bcf000 rbx = 0x00007c1ff6de13f0 rcx = 0x0000000000000000 rdx = 0x00007ffff7e42340
rdi = 0x00007ffff7afe800 rsi = 0x0000000000000001 rbp = 0x00007fffffffcfa0 rsp = 0x00007fffffffcf30
r8 = 0x00007ffff7bbf000 r9 = 0x00007fffffffff01 r10 = 0x00007fffffffff01 r11 = 0x0000000000000201
r12 = 0x0000000000000001 r13 = 0x00000000deadab3f r14 = 0x0000000000000000 r15 = 0x0000000000000010
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: ILL (/out/llvm-isel-fuzzer--bpf-O2+0x7916e4f) in __clang_trap_msg$libc++$/usr/local/bin/../include/c++/v1/__vector/vector.h:414: libc++ Hardening assertion __n < size() failed: vector[] index out of bounds
==14==ABORTING
```

### Fix (diff)

```diff
diff --git a/llvm/lib/Target/BPF/BPFISelDAGToDAG.cpp b/llvm/lib/Target/BPF/BPFISelDAGToDAG.cpp
index dadba52de..e80b2ca84 100644
--- a/llvm/lib/Target/BPF/BPFISelDAGToDAG.cpp
+++ b/llvm/lib/Target/BPF/BPFISelDAGToDAG.cpp
@@ -365,6 +365,10 @@ bool BPFDAGToDAGISel::fillGenericConstant(const DataLayout &DL,
val_vec_type &Vals, uint64_t Offset) {
uint64_t Size = DL.getTypeAllocSize(CV->getType());

+ uint64_t ValsSize = Vals.size();
+ if (Offset > ValsSize || Size > ValsSize - Offset)
+ return false;
+
if (isa(CV) || isa(CV))
return true; // already done
```

**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.