google / google/woff2

UBSan: misaligned 32-bit load in vendored Brotli bit reader

Open
#202 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
C++
Stars
1.8k
Forks
228
PR merge metrics
No merged PRs in 30d

Description

## Summary
Malformed WOFF2 input can trigger a misaligned 32-bit load in the Brotli decoder path vendored by woff2. The crash occurs in `brotli/c/dec/bit_reader.h` inside `BrotliLoad32LE()`, which directly casts a `uint8_t *` buffer to `const uint32_t *` and dereferences it. The malformed WOFF2 stream reaches this helper through `woff2::Woff2Uncompress()`.

I reproduced this with the `src/convert_woff2ttf_fuzzer.cc` libFuzzer harness. The tested build identifies as woff2 `v1.0.2`. On x86_64 this is reported by UBSan; on architectures or runtimes that require aligned 32-bit loads, the same pattern may fault at runtime.

This is distinct from the other WOFF2 misaligned-load issue in `woff2::ComputeULongSum()`: this report is for the vendored Brotli bit reader reached during decompression.

## Details
The relevant code in `brotli/c/dec/bit_reader.h` is:

```c
static BROTLI_INLINE uint32_t BrotliLoad32LE(const uint8_t* in) {
if (BROTLI_LITTLE_ENDIAN) {
return *((const uint32_t*)in);
} else if (BROTLI_BIG_ENDIAN) {
uint32_t value = *((const uint32_t*)in);
```

The WOFF2 decode path reaches the Brotli decoder from `src/woff2_dec.cc`:

```c++
BrotliDecoderResult result = BrotliDecoderDecompress(
src_size, src_buf, &uncompressed_size, dst_buf);
```

The input pointer passed into the bit reader is byte-addressed compressed data and is not guaranteed to be 4-byte aligned. Direct typed loads therefore violate the alignment requirement for `uint32_t`.

The observed project stack is:

```text
BrotliLoad32LE()
BrotliFillBitWindow()
BrotliFillBitWindow16()
ReadSymbolCodeLengths()
ReadHuffmanCode()
BrotliDecoderDecompressStream()
BrotliDecoderDecompress()
woff2::Woff2Uncompress()
woff2::ConvertWOFF2ToTTF()
LLVMFuzzerTestOneInput()
```

## PoC

[reproducer.poc.woff2.txt](https://github.com/user-attachments/files/29956304/reproducer.poc.woff2.txt)

* Please rename `reproducer.poc.woff2.txt` to `reproducer.poc.woff2`

## Build and reproduction
woff2 version tested: `v1.0.2`
OS: `Linux x86_64`
Compiler: `clang`
Harness: `src/convert_woff2ttf_fuzzer.cc`

One working sanitizer configuration is:

```sh
export CC=clang
export CXX=clang++
export CFLAGS="-fsanitize=address,undefined,fuzzer-no-link -fno-sanitize-recover=all -fno-omit-frame-pointer -O2 -g"
export CXXFLAGS="-fsanitize=address,undefined,fuzzer-no-link -fno-sanitize-recover=all -fno-omit-frame-pointer -O2 -g"
export ASAN_OPTIONS="detect_leaks=0:abort_on_error=1:symbolize=1"
export UBSAN_OPTIONS="print_stacktrace=1:halt_on_error=1"
```

Build woff2 with the fuzz harness and run:

```sh
./convert_woff2ttf_fuzzer
```

## Driver source
`src/convert_woff2ttf_fuzzer.cc`:

```c++
#include
#include

#include
#include

extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
std::string buf;
woff2::WOFF2StringOut out(&buf);
out.SetMaxSize(30 * 1024 * 1024);
woff2::ConvertWOFF2ToTTF(data, size, &out);
return 0;
}
```

## Sanitizer report
```text
c/dec/./bit_reader.h:114:12: runtime error: load of misaligned address 0x630000010469 for type 'const uint32_t' (aka 'const unsigned int'), which requires 4 byte alignment
#0 in BrotliLoad32LE brotli/c/dec/./bit_reader.h:114:12
#1 in BrotliFillBitWindow brotli/c/dec/./bit_reader.h:184:30
#2 in BrotliFillBitWindow16 brotli/c/dec/./bit_reader.h:213:3
#3 in ReadSymbolCodeLengths brotli/c/dec/decode.c:576:5
#4 in ReadHuffmanCode brotli/c/dec/decode.c:792:41
#5 in BrotliDecoderDecompressStream brotli/c/dec/decode.c:2110:18
#6 in BrotliDecoderDecompress brotli/c/dec/decode.c:1887:12
#7 in woff2::Woff2Uncompress src/woff2_dec.cc:761:32
#8 in woff2::ConvertWOFF2ToTTF src/woff2_dec.cc:1341:7
#9 in LLVMFuzzerTestOneInput src/convert_woff2ttf_fuzzer.cc

SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior brotli/c/dec/bit_reader.h:114:12 in
SUMMARY: libFuzzer: deadly signal
```

Contributor guide

Open the contributing guide

Research direction

Start with brotli/c/dec/bit_reader.h and inspect BrotliLoad32LE(), then trace its use from src/woff2_dec.cc through Woff2Uncompress(). Run src/convert_woff2ttf_fuzzer.cc with the supplied reproducer under the shown UBSan configuration; done means the input no longer reports a misaligned load and the decoder still completes normally.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
backend, testing
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.