Use-after-free in BrotliEncoderCompressStream when prepared dictionary retains pointer to caller buffer #550

Closed
opened 2026-01-29 20:45:35 +00:00 by claunia · 2 comments
Owner

Originally created by @hgarrereyn on GitHub (Sep 25, 2025).

There is a potential UAF when BrotliEncoderPrepareDictionary retains a stale pointer to the original dictionary buffer passed in. Other APIs have explicitly documented lifetime rules about ownership, but I couldn't find any comments/documentation about BrotliEncoderPrepareDictionary indicating the original buffer is expected to be kept alive.

Tested on the most recent commit: 85d46ce6

As a user I would expect either:

  1. BrotliEncoderPrepareDictionary makes a copy of the underlying buffer, and does not retain a pointer to the original source (thus users can free it)
  2. The lifetime requirement is explicitly documented somewhere

The following testcase triggers an ASAN violation caused by retaining a pointer to an underlying dictionary buffer which is freed before trying to perform compression:

testcase

#include <cstdint>
#include <cstring>
#include <string>
#include <vector>
extern "C" {
#include "brotli/encode.h"
}
int main(){
  // Serialized dictionary bytes (first 115 bytes from the original reproducer)
  const uint8_t dict_bytes[115] = {
    66,82,83,68,1,0,5,0,8,0,10,0,97,81,77,85,45,55,67,121,82,112,0,0,0,0,3,0,76,54,100,0,0,27,0,178,214,247,225,95,133,228,247,4,46,184,80,61,96,134,160,126,246,1,129,131,143,116,253,138,204,172,30,0,63,164,158,165,46,220,53,169,213,146,118,50,17,174,123,243,91,168,231,143,185,77,35,160,46,192,92,234,16,90,14,0,99,76,71,80,100,113,51,121,52,76,72,57,69,51,0,199,221,182,211
  };
  BrotliEncoderPreparedDictionary* dict = nullptr;
  {
    // tmp's storage is freed at end of this scope
    std::string tmp(reinterpret_cast<const char*>(dict_bytes), sizeof(dict_bytes));
    dict = BrotliEncoderPrepareDictionary((BrotliSharedDictionaryType)0,
                                          tmp.size(),
                                          reinterpret_cast<const uint8_t*>(tmp.data()),
                                          0,
                                          nullptr,
                                          nullptr,
                                          nullptr);
    if (!dict) return 0; // bail if dictionary parse fails
  } // tmp destroyed here; if library retained pointer, UAF will occur later

  BrotliEncoderState* st = BrotliEncoderCreateInstance(nullptr,nullptr,nullptr);
  if (!st) return 0;
  (void)BrotliEncoderAttachPreparedDictionary(st, dict);

  // Drive encoder with enough data and FINISH to exercise HQ Zopfli path
  std::vector<uint8_t> in(2303);
  for (size_t i=0;i<in.size();++i) in[i] = static_cast<uint8_t>((i*131u+7u)&0xFF);
  const uint8_t* next_in = in.data();
  size_t avail_in = in.size();
  uint8_t out_buf[64];
  uint8_t* next_out = out_buf;
  size_t avail_out = sizeof(out_buf);
  size_t total_out = 0;
  while (true) {
    BROTLI_BOOL ok = BrotliEncoderCompressStream(st, BROTLI_OPERATION_FINISH,
                                                 &avail_in, &next_in,
                                                 &avail_out, &next_out,
                                                 &total_out);
    if (!ok) break;
    if (!BrotliEncoderHasMoreOutput(st) && avail_in == 0) break;
    if (avail_out == 0) { next_out = out_buf; avail_out = sizeof(out_buf); }
  }
  return 0;
}

report

{
  "Date": "2025-09-21T16:23:03.968754+00:00",
  "Uname": "Linux 6d89288ba001 5.15.0-140-generic #150-Ubuntu SMP Sat Apr 12 06:00:09 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux",
  "OS": "Ubuntu",
  "OSRelease": "22.04",
  "Architecture": "amd64",
  "ExecutablePath": "/tmp/tmp87su4xhj/reproducer",
  "ProcEnviron": [
    "LIBAFL_EDGES_MAP_SIZE=800000",
    "PWD=/fuzz/workspace",
    "CXX=gf_libafl_cxx",
    "GRAPHFUZZ_USE_ASAN=1",
    "HOME=/root",
    "ASAN_OPTIONS=hard_rss_limit_mb=1024:detect_leaks=0",
    "TERM=xterm-256color",
    "SHLVL=1",
    "LD_LIBRARY_PATH=/fuzz/install/lib",
    "PATH=/root/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
    "CC=gf_libafl_cc",
    "DEBIAN_FRONTEND=noninteractive",
    "OLDPWD=/build",
    "_=/usr/local/bin/agfi"
  ],
  "ProcCmdline": "/tmp/tmp87su4xhj/reproducer",
  "Stdin": "",
  "ProcStatus": [],
  "ProcMaps": [],
  "ProcFiles": [],
  "NetworkConnections": [],
  "CrashSeverity": {
    "Type": "NOT_EXPLOITABLE",
    "ShortDescription": "heap-use-after-free(read)",
    "Description": "Use of deallocated memory",
    "Explanation": "The target crashed when reading from memory after it has been freed."
  },
  "Stacktrace": [
    "    #0 0x55555574e379 in UpdateNodes /fuzz/src/c/enc/backward_references_hq.c:502:29",
    "    #1 0x5555557571e3 in ZopfliIterate /fuzz/src/c/enc/backward_references_hq.c:664:19",
    "    #2 0x5555557571e3 in BrotliCreateHqZopfliBackwardReferences /fuzz/src/c/enc/backward_references_hq.c:924:22",
    "    #3 0x555555671077 in EncodeData /fuzz/src/c/enc/encode.c:1104:5",
    "    #4 0x555555663847 in BrotliEncoderCompressStream /fuzz/src/c/enc/encode.c:1685:18",
    "    #5 0x55555565ec16 in main /tmp/tmp87su4xhj/reproducer.cpp:41:22",
    "    #6 0x7ffff7a6dd8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16",
    "    #7 0x7ffff7a6de3f in __libc_start_main csu/../csu/libc-start.c:392:3",
    "    #8 0x5555555834c4 in _start (/tmp/tmp87su4xhj/reproducer+0x2f4c4) (BuildId: e65d5dd2e799f4fc3d57d2a874ca39d45f905fac)"
  ],
  "Registers": {},
  "Disassembly": [],
  "Package": "",
  "PackageVersion": "",
  "PackageArchitecture": "",
  "PackageDescription": "",
  "AsanReport": [
    "==156==ERROR: AddressSanitizer: heap-use-after-free on address 0x50c0000000b0 at pc 0x55555574e37a bp 0x7fffffffdec0 sp 0x7fffffffdeb8",
    "READ of size 1 at 0x50c0000000b0 thread T0",
    "    #0 0x55555574e379 in UpdateNodes /fuzz/src/c/enc/backward_references_hq.c:502:29",
    "    #1 0x5555557571e3 in ZopfliIterate /fuzz/src/c/enc/backward_references_hq.c:664:19",
    "    #2 0x5555557571e3 in BrotliCreateHqZopfliBackwardReferences /fuzz/src/c/enc/backward_references_hq.c:924:22",
    "    #3 0x555555671077 in EncodeData /fuzz/src/c/enc/encode.c:1104:5",
    "    #4 0x555555663847 in BrotliEncoderCompressStream /fuzz/src/c/enc/encode.c:1685:18",
    "    #5 0x55555565ec16 in main /tmp/tmp87su4xhj/reproducer.cpp:41:22",
    "    #6 0x7ffff7a6dd8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16",
    "    #7 0x7ffff7a6de3f in __libc_start_main csu/../csu/libc-start.c:392:3",
    "    #8 0x5555555834c4 in _start (/tmp/tmp87su4xhj/reproducer+0x2f4c4) (BuildId: e65d5dd2e799f4fc3d57d2a874ca39d45f905fac)",
    "",
    "0x50c0000000b0 is located 112 bytes inside of 116-byte region [0x50c000000040,0x50c0000000b4)",
    "freed by thread T0 here:",
    "    #0 0x55555565cb3d in operator delete(void*) (/tmp/tmp87su4xhj/reproducer+0x108b3d) (BuildId: e65d5dd2e799f4fc3d57d2a874ca39d45f905fac)",
    "    #1 0x55555565e82d in __gnu_cxx::new_allocator<char>::deallocate(char*, unsigned long) /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/ext/new_allocator.h:145:2",
    "    #2 0x55555565e82d in std::allocator_traits<std::allocator<char>>::deallocate(std::allocator<char>&, char*, unsigned long) /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/alloc_traits.h:496:13",
    "    #3 0x55555565e82d in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::_M_destroy(unsigned long) /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/basic_string.h:245:9",
    "    #4 0x55555565e82d in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::_M_dispose() /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/basic_string.h:240:4",
    "    #5 0x55555565e82d in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::~basic_string() /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/basic_string.h:672:9",
    "    #6 0x55555565e82d in main /tmp/tmp87su4xhj/reproducer.cpp:25:3",
    "    #7 0x7ffff7a6dd8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16",
    "",
    "previously allocated by thread T0 here:",
    "    #0 0x55555565c2dd in operator new(unsigned long) (/tmp/tmp87su4xhj/reproducer+0x1082dd) (BuildId: e65d5dd2e799f4fc3d57d2a874ca39d45f905fac)",
    "    #1 0x55555565e723 in void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::_M_construct<char const*>(char const*, char const*, std::forward_iterator_tag) /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/basic_string.tcc:219:14",
    "    #2 0x55555565e723 in void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::_M_construct_aux<char const*>(char const*, char const*, std::__false_type) /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/basic_string.h:255:11",
    "    #3 0x55555565e723 in void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::_M_construct<char const*>(char const*, char const*) /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/basic_string.h:274:4",
    "    #4 0x55555565e723 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::basic_string(char const*, unsigned long, std::allocator<char> const&) /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/basic_string.h:521:9",
    "    #5 0x55555565e723 in main /tmp/tmp87su4xhj/reproducer.cpp:16:17",
    "    #6 0x7ffff7a6dd8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16",
    "",
    "SUMMARY: AddressSanitizer: heap-use-after-free /fuzz/src/c/enc/backward_references_hq.c:502:29 in UpdateNodes",
    "Shadow bytes around the buggy address:",
    "  0x50bffffffe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
    "  0x50bffffffe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
    "  0x50bfffffff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
    "  0x50bfffffff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
    "  0x50c000000000: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd",
    "=>0x50c000000080: fd fd fd fd fd fd[fd]fa fa fa fa fa fa fa fa fa",
    "  0x50c000000100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa",
    "  0x50c000000180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa",
    "  0x50c000000200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa",
    "  0x50c000000280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa",
    "  0x50c000000300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa",
    "Shadow byte legend (one shadow byte represents 8 application bytes):",
    "  Addressable:           00",
    "  Partially addressable: 01 02 03 04 05 06 07",
    "  Heap left redzone:       fa",
    "  Freed heap region:       fd",
    "  Stack left redzone:      f1",
    "  Stack mid redzone:       f2",
    "  Stack right redzone:     f3",
    "  Stack after return:      f5",
    "  Stack use after scope:   f8",
    "  Global redzone:          f9",
    "  Global init order:       f6",
    "  Poisoned by user:        f7",
    "  Container overflow:      fc",
    "  Array cookie:            ac",
    "  Intra object redzone:    bb",
    "  ASan internal:           fe",
    "  Left alloca redzone:     ca",
    "  Right alloca redzone:    cb",
    "==156==ABORTING"
  ],
  "MsanReport": [],
  "UbsanReport": [],
  "LuaReport": [],
  "PythonReport": [],
  "GoReport": [],
  "JavaReport": [],
  "RustReport": [],
  "JsReport": [],
  "CSharpReport": [],
  "CrashLine": "/fuzz/src/c/enc/backward_references_hq.c:502:29",
  "Source": [
    "    498            offset = offset - addon->chunk_offsets[d] - backward;",
    "    499            limit = addon->chunk_offsets[d + 1] - addon->chunk_offsets[d] - offset;",
    "    500            limit = limit > max_len ? max_len : limit;",
    "    501            if (best_len >= limit ||",
    "--->502                continuation != source[offset + best_len]) {",
    "    503              continue;",
    "    504            }",
    "    505            len = FindMatchLengthWithLimit(&source[offset],",
    "    506                                           &ringbuffer[cur_ix_masked],",
    "    507                                           limit);"
  ]
}
Originally created by @hgarrereyn on GitHub (Sep 25, 2025). There is a potential UAF when `BrotliEncoderPrepareDictionary` retains a stale pointer to the original dictionary buffer passed in. Other APIs have explicitly documented lifetime rules about ownership, but I couldn't find any comments/documentation about `BrotliEncoderPrepareDictionary` indicating the original buffer is expected to be kept alive. Tested on the most recent commit: `85d46ce6` As a user I would expect either: 1. `BrotliEncoderPrepareDictionary` makes a copy of the underlying buffer, and does not retain a pointer to the original source (thus users can free it) 2. The lifetime requirement is explicitly documented somewhere The following testcase triggers an ASAN violation caused by retaining a pointer to an underlying dictionary buffer which is freed before trying to perform compression: **testcase** ```cpp #include <cstdint> #include <cstring> #include <string> #include <vector> extern "C" { #include "brotli/encode.h" } int main(){ // Serialized dictionary bytes (first 115 bytes from the original reproducer) const uint8_t dict_bytes[115] = { 66,82,83,68,1,0,5,0,8,0,10,0,97,81,77,85,45,55,67,121,82,112,0,0,0,0,3,0,76,54,100,0,0,27,0,178,214,247,225,95,133,228,247,4,46,184,80,61,96,134,160,126,246,1,129,131,143,116,253,138,204,172,30,0,63,164,158,165,46,220,53,169,213,146,118,50,17,174,123,243,91,168,231,143,185,77,35,160,46,192,92,234,16,90,14,0,99,76,71,80,100,113,51,121,52,76,72,57,69,51,0,199,221,182,211 }; BrotliEncoderPreparedDictionary* dict = nullptr; { // tmp's storage is freed at end of this scope std::string tmp(reinterpret_cast<const char*>(dict_bytes), sizeof(dict_bytes)); dict = BrotliEncoderPrepareDictionary((BrotliSharedDictionaryType)0, tmp.size(), reinterpret_cast<const uint8_t*>(tmp.data()), 0, nullptr, nullptr, nullptr); if (!dict) return 0; // bail if dictionary parse fails } // tmp destroyed here; if library retained pointer, UAF will occur later BrotliEncoderState* st = BrotliEncoderCreateInstance(nullptr,nullptr,nullptr); if (!st) return 0; (void)BrotliEncoderAttachPreparedDictionary(st, dict); // Drive encoder with enough data and FINISH to exercise HQ Zopfli path std::vector<uint8_t> in(2303); for (size_t i=0;i<in.size();++i) in[i] = static_cast<uint8_t>((i*131u+7u)&0xFF); const uint8_t* next_in = in.data(); size_t avail_in = in.size(); uint8_t out_buf[64]; uint8_t* next_out = out_buf; size_t avail_out = sizeof(out_buf); size_t total_out = 0; while (true) { BROTLI_BOOL ok = BrotliEncoderCompressStream(st, BROTLI_OPERATION_FINISH, &avail_in, &next_in, &avail_out, &next_out, &total_out); if (!ok) break; if (!BrotliEncoderHasMoreOutput(st) && avail_in == 0) break; if (avail_out == 0) { next_out = out_buf; avail_out = sizeof(out_buf); } } return 0; } ``` **report** ```json { "Date": "2025-09-21T16:23:03.968754+00:00", "Uname": "Linux 6d89288ba001 5.15.0-140-generic #150-Ubuntu SMP Sat Apr 12 06:00:09 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux", "OS": "Ubuntu", "OSRelease": "22.04", "Architecture": "amd64", "ExecutablePath": "/tmp/tmp87su4xhj/reproducer", "ProcEnviron": [ "LIBAFL_EDGES_MAP_SIZE=800000", "PWD=/fuzz/workspace", "CXX=gf_libafl_cxx", "GRAPHFUZZ_USE_ASAN=1", "HOME=/root", "ASAN_OPTIONS=hard_rss_limit_mb=1024:detect_leaks=0", "TERM=xterm-256color", "SHLVL=1", "LD_LIBRARY_PATH=/fuzz/install/lib", "PATH=/root/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "CC=gf_libafl_cc", "DEBIAN_FRONTEND=noninteractive", "OLDPWD=/build", "_=/usr/local/bin/agfi" ], "ProcCmdline": "/tmp/tmp87su4xhj/reproducer", "Stdin": "", "ProcStatus": [], "ProcMaps": [], "ProcFiles": [], "NetworkConnections": [], "CrashSeverity": { "Type": "NOT_EXPLOITABLE", "ShortDescription": "heap-use-after-free(read)", "Description": "Use of deallocated memory", "Explanation": "The target crashed when reading from memory after it has been freed." }, "Stacktrace": [ " #0 0x55555574e379 in UpdateNodes /fuzz/src/c/enc/backward_references_hq.c:502:29", " #1 0x5555557571e3 in ZopfliIterate /fuzz/src/c/enc/backward_references_hq.c:664:19", " #2 0x5555557571e3 in BrotliCreateHqZopfliBackwardReferences /fuzz/src/c/enc/backward_references_hq.c:924:22", " #3 0x555555671077 in EncodeData /fuzz/src/c/enc/encode.c:1104:5", " #4 0x555555663847 in BrotliEncoderCompressStream /fuzz/src/c/enc/encode.c:1685:18", " #5 0x55555565ec16 in main /tmp/tmp87su4xhj/reproducer.cpp:41:22", " #6 0x7ffff7a6dd8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16", " #7 0x7ffff7a6de3f in __libc_start_main csu/../csu/libc-start.c:392:3", " #8 0x5555555834c4 in _start (/tmp/tmp87su4xhj/reproducer+0x2f4c4) (BuildId: e65d5dd2e799f4fc3d57d2a874ca39d45f905fac)" ], "Registers": {}, "Disassembly": [], "Package": "", "PackageVersion": "", "PackageArchitecture": "", "PackageDescription": "", "AsanReport": [ "==156==ERROR: AddressSanitizer: heap-use-after-free on address 0x50c0000000b0 at pc 0x55555574e37a bp 0x7fffffffdec0 sp 0x7fffffffdeb8", "READ of size 1 at 0x50c0000000b0 thread T0", " #0 0x55555574e379 in UpdateNodes /fuzz/src/c/enc/backward_references_hq.c:502:29", " #1 0x5555557571e3 in ZopfliIterate /fuzz/src/c/enc/backward_references_hq.c:664:19", " #2 0x5555557571e3 in BrotliCreateHqZopfliBackwardReferences /fuzz/src/c/enc/backward_references_hq.c:924:22", " #3 0x555555671077 in EncodeData /fuzz/src/c/enc/encode.c:1104:5", " #4 0x555555663847 in BrotliEncoderCompressStream /fuzz/src/c/enc/encode.c:1685:18", " #5 0x55555565ec16 in main /tmp/tmp87su4xhj/reproducer.cpp:41:22", " #6 0x7ffff7a6dd8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16", " #7 0x7ffff7a6de3f in __libc_start_main csu/../csu/libc-start.c:392:3", " #8 0x5555555834c4 in _start (/tmp/tmp87su4xhj/reproducer+0x2f4c4) (BuildId: e65d5dd2e799f4fc3d57d2a874ca39d45f905fac)", "", "0x50c0000000b0 is located 112 bytes inside of 116-byte region [0x50c000000040,0x50c0000000b4)", "freed by thread T0 here:", " #0 0x55555565cb3d in operator delete(void*) (/tmp/tmp87su4xhj/reproducer+0x108b3d) (BuildId: e65d5dd2e799f4fc3d57d2a874ca39d45f905fac)", " #1 0x55555565e82d in __gnu_cxx::new_allocator<char>::deallocate(char*, unsigned long) /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/ext/new_allocator.h:145:2", " #2 0x55555565e82d in std::allocator_traits<std::allocator<char>>::deallocate(std::allocator<char>&, char*, unsigned long) /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/alloc_traits.h:496:13", " #3 0x55555565e82d in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::_M_destroy(unsigned long) /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/basic_string.h:245:9", " #4 0x55555565e82d in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::_M_dispose() /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/basic_string.h:240:4", " #5 0x55555565e82d in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::~basic_string() /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/basic_string.h:672:9", " #6 0x55555565e82d in main /tmp/tmp87su4xhj/reproducer.cpp:25:3", " #7 0x7ffff7a6dd8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16", "", "previously allocated by thread T0 here:", " #0 0x55555565c2dd in operator new(unsigned long) (/tmp/tmp87su4xhj/reproducer+0x1082dd) (BuildId: e65d5dd2e799f4fc3d57d2a874ca39d45f905fac)", " #1 0x55555565e723 in void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::_M_construct<char const*>(char const*, char const*, std::forward_iterator_tag) /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/basic_string.tcc:219:14", " #2 0x55555565e723 in void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::_M_construct_aux<char const*>(char const*, char const*, std::__false_type) /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/basic_string.h:255:11", " #3 0x55555565e723 in void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::_M_construct<char const*>(char const*, char const*) /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/basic_string.h:274:4", " #4 0x55555565e723 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::basic_string(char const*, unsigned long, std::allocator<char> const&) /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/basic_string.h:521:9", " #5 0x55555565e723 in main /tmp/tmp87su4xhj/reproducer.cpp:16:17", " #6 0x7ffff7a6dd8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16", "", "SUMMARY: AddressSanitizer: heap-use-after-free /fuzz/src/c/enc/backward_references_hq.c:502:29 in UpdateNodes", "Shadow bytes around the buggy address:", " 0x50bffffffe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", " 0x50bffffffe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", " 0x50bfffffff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", " 0x50bfffffff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", " 0x50c000000000: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd", "=>0x50c000000080: fd fd fd fd fd fd[fd]fa fa fa fa fa fa fa fa fa", " 0x50c000000100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa", " 0x50c000000180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa", " 0x50c000000200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa", " 0x50c000000280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa", " 0x50c000000300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa", "Shadow byte legend (one shadow byte represents 8 application bytes):", " Addressable: 00", " Partially addressable: 01 02 03 04 05 06 07", " Heap left redzone: fa", " Freed heap region: fd", " Stack left redzone: f1", " Stack mid redzone: f2", " Stack right redzone: f3", " Stack after return: f5", " Stack use after scope: f8", " Global redzone: f9", " Global init order: f6", " Poisoned by user: f7", " Container overflow: fc", " Array cookie: ac", " Intra object redzone: bb", " ASan internal: fe", " Left alloca redzone: ca", " Right alloca redzone: cb", "==156==ABORTING" ], "MsanReport": [], "UbsanReport": [], "LuaReport": [], "PythonReport": [], "GoReport": [], "JavaReport": [], "RustReport": [], "JsReport": [], "CSharpReport": [], "CrashLine": "/fuzz/src/c/enc/backward_references_hq.c:502:29", "Source": [ " 498 offset = offset - addon->chunk_offsets[d] - backward;", " 499 limit = addon->chunk_offsets[d + 1] - addon->chunk_offsets[d] - offset;", " 500 limit = limit > max_len ? max_len : limit;", " 501 if (best_len >= limit ||", "--->502 continuation != source[offset + best_len]) {", " 503 continue;", " 504 }", " 505 len = FindMatchLengthWithLimit(&source[offset],", " 506 &ringbuffer[cur_ix_masked],", " 507 limit);" ] } ```
Author
Owner

@eustas commented on GitHub (Sep 25, 2025):

I agree, that documentation should be done more visible and explicit. Here is what we have now:
9686382ff3/c/enc/compound_dictionary.h (L22)

"Lean" prepared dictionary. LZ77 data is referenced. It is the responsibility of caller of "prepare dictionary" to keep the LZ77 data while prepared dictionary is in use.
@eustas commented on GitHub (Sep 25, 2025): I agree, that documentation should be done more visible and explicit. Here is what we have now: https://github.com/google/brotli/blob/9686382ff301fcfa18160de6e891f682299ae983/c/enc/compound_dictionary.h#L22 ``` "Lean" prepared dictionary. LZ77 data is referenced. It is the responsibility of caller of "prepare dictionary" to keep the LZ77 data while prepared dictionary is in use. ```
Author
Owner

@eustas commented on GitHub (Sep 25, 2025):

Clarified in #1332

@eustas commented on GitHub (Sep 25, 2025): Clarified in #1332
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/brotli#550