Skip to content

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

Description

@iceray00

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions