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:
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:
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:
BrotliLoad32LE()
BrotliFillBitWindow()
BrotliFillBitWindow16()
ReadSymbolCodeLengths()
ReadHuffmanCode()
BrotliDecoderDecompressStream()
BrotliDecoderDecompress()
woff2::Woff2Uncompress()
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
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
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.hinsideBrotliLoad32LE(), which directly casts auint8_t *buffer toconst uint32_t *and dereferences it. The malformed WOFF2 stream reaches this helper throughwoff2::Woff2Uncompress().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 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.his:The WOFF2 decode path reaches the Brotli decoder from
src/woff2_dec.cc: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:
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