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:
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<const uint32_t*>(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:
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:
woff2::ComputeULongSum()
woff2::ReconstructFont()
woff2::ConvertWOFF2ToTTF()
LLVMFuzzerTestOneInput()
PoC
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:
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:
./convert_woff2ttf_fuzzer <poc>
Driver source
src/convert_woff2ttf_fuzzer.cc:
#include <stddef.h>
#include <stdint.h>
#include <string>
#include <woff2/decode.h>
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
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
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 castsbuf + itoconst 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.cclibFuzzer harness. The tested build identifies as woff2v1.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.ccis:The decode path reaches this checksum helper from
src/woff2_dec.cc:checksum = ComputeULongSum(transformed_buf + table.src_offset, table.src_length);aligned_sizerounds the length down to a multiple of four, but it does not guarantee that the starting pointerbufis aligned. Iftable.src_offsetis not 4-byte aligned, every typeduint32_tload in this loop is undefined behavior.The observed project stack is:
PoC
reproducer.poc.woff2.txt
reproducer.poc.woff2.txttoreproducer.poc.woff2Build and reproduction
woff2 version tested:
v1.0.2OS:
Linux x86_64Compiler:
clangHarness:
src/convert_woff2ttf_fuzzer.ccOne working sanitizer configuration is:
Build woff2 with the fuzz harness and run:
Driver source
src/convert_woff2ttf_fuzzer.cc:Sanitizer report