google / google/woff2

UBSan: misaligned 32-bit load in ComputeULongSum

Open Beginner friendly
#203 0 comments 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 `woff2::ComputeULongSum()` while reconstructing a font. The function iterates over a byte buffer and, on little-endian builds, directly casts `buf + i` to `const uint32_t *`. The buffer offset is derived from WOFF2 table metadata and is not guaranteed to be 4-byte aligned.

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 strict-alignment architectures, the same pattern can be a runtime fault.

This is distinct from the other WOFF2 misaligned-load issue in `BrotliLoad32LE()`: this report is for the checksum helper reached while reconstructing table data.

## Details
The relevant code in `src/woff2_common.cc` is:

```c++
uint32_t ComputeULongSum(const uint8_t* buf, size_t size) {
uint32_t checksum = 0;
size_t aligned_size = size & ~3;
for (size_t i = 0; i < aligned_size; i += 4) {
#if defined(WOFF_LITTLE_ENDIAN)
uint32_t v = *reinterpret_cast(buf + i);
checksum += (((v & 0xFF) << 24) | ((v & 0xFF00) << 8) |
((v & 0xFF0000) >> 8) | ((v & 0xFF000000) >> 24));
```

The decode path reaches this checksum helper from `src/woff2_dec.cc`:

```c++
checksum = ComputeULongSum(transformed_buf + table.src_offset,
table.src_length);
```

`aligned_size` rounds the length down to a multiple of four, but it does not guarantee that the starting pointer `buf` is aligned. If `table.src_offset` is not 4-byte aligned, every typed `uint32_t` load in this loop is undefined behavior.

The observed project stack is:

```text
woff2::ComputeULongSum()
woff2::ReconstructFont()
woff2::ConvertWOFF2ToTTF()
LLVMFuzzerTestOneInput()
```

## PoC

[reproducer.poc.woff2.txt](https://github.com/user-attachments/files/29956384/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
src/woff2_common.cc:23:18: runtime error: load of misaligned address 0x6030000000d7 for type 'const uint32_t' (aka 'const unsigned int'), which requires 4 byte alignment
#0 in woff2::ComputeULongSum src/woff2_common.cc:23:18
#1 in woff2::ReconstructFont src/woff2_dec.cc:956:20
#2 in woff2::ConvertWOFF2ToTTF src/woff2_dec.cc:1348:9
#3 in LLVMFuzzerTestOneInput src/convert_woff2ttf_fuzzer.cc

SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior src/woff2_common.cc:23:18 in
SUMMARY: libFuzzer: deadly signal
```

Contributor guide

Open the contributing guide

Research direction

Start in src/woff2_common.cc at ComputeULongSum and trace its call from src/woff2_dec.cc: ReconstructFont. Reproduce the UBSan report with the attached PoC and src/convert_woff2ttf_fuzzer.cc using the documented sanitizer configuration. Done means the PoC no longer triggers a misaligned-load report while conversion completes normally.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
security
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.