lizard icon indicating copy to clipboard operation
lizard copied to clipboard

LizardF_compressFrameBound underestimates for tiny inputs causing heap OOB in LizardF_compressFrame

Open hgarrereyn opened this issue 3 months ago • 2 comments

I understand this library may no longer be maintained, but in case there are still users who may be affected by this:

There is a small bug in LizardF_compressFrameBound causing it to underestimate bound sizes for small inputs, leading to OOB writes:

testcase.cc

#include <cstdio>
#include <cstdlib>
#include <cstdint>
extern "C" {
#include "/fuzz/install/include/lizard_frame.h"
}
int main() {
    // 1-byte input
    const unsigned char src[1] = {0};

    // Preferences: only set contentSize; all other fields default/zero
    LizardF_preferences_t prefs{}; // zero-initialize
    prefs.frameInfo.contentSize = 1; // known content size

    // Library promises this bound guarantees success
    size_t bound = LizardF_compressFrameBound(1, &prefs);
    if (bound == 0) return 0;

    char *dst = (char*)malloc(bound);
    // Crashes with ASan heap-buffer-overflow: library writes past 'dst'
    size_t written = LizardF_compressFrame(dst, bound, src, 1, &prefs);
    fprintf(stderr, "bound=%zu written=%zu\n", bound, written);
    (void)written;
    return 0;
}

casr report

{
  "Date": "2025-09-21T04:11:03.101632+00:00",
  "Uname": "Linux 2fe9075ff52e 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/tmpv73y9kwj/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=/fuzz/src/lib",
    "_=/usr/local/bin/agfi"
  ],
  "ProcCmdline": "/tmp/tmpv73y9kwj/reproducer",
  "Stdin": "",
  "ProcStatus": [],
  "ProcMaps": [],
  "ProcFiles": [],
  "NetworkConnections": [],
  "CrashSeverity": {
    "Type": "EXPLOITABLE",
    "ShortDescription": "heap-buffer-overflow(write)",
    "Description": "Heap buffer overflow",
    "Explanation": "The target writes data past the end, or before the beginning, of the intended heap buffer."
  },
  "Stacktrace": [
    "    #0 0x55555561a7e3 in __asan_memcpy (/tmp/tmpv73y9kwj/reproducer+0xc67e3) (BuildId: bc1331dfdfe6ec06a38aa5826490659a614cfc10)",
    "    #1 0x7ffff7ea0d18 in Lizard_writeBlock /fuzz/src/lib/lizard_compress.c:243:5",
    "    #2 0x7ffff7f3952d in Lizard_compress_generic /fuzz/src/lib/lizard_compress.c:535:13",
    "    #3 0x7ffff7f3952d in Lizard_compress_extState /fuzz/src/lib/lizard_compress.c:592:12",
    "    #4 0x7ffff7f66aa4 in LizardF_compressBlock /fuzz/src/lib/lizard_frame.c:461:18",
    "    #5 0x7ffff7f66aa4 in LizardF_compressUpdate /fuzz/src/lib/lizard_frame.c:554:19",
    "    #6 0x7ffff7f6498c in LizardF_compressFrame /fuzz/src/lib/lizard_frame.c:297:17",
    "    #7 0x55555565b62b in main /tmp/tmpv73y9kwj/reproducer.cpp:21:22",
    "    #8 0x7ffff794bd8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16",
    "    #9 0x7ffff794be3f in __libc_start_main csu/../csu/libc-start.c:392:3",
    "    #10 0x555555580304 in _start (/tmp/tmpv73y9kwj/reproducer+0x2c304) (BuildId: bc1331dfdfe6ec06a38aa5826490659a614cfc10)"
  ],
  "Registers": {},
  "Disassembly": [],
  "Package": "",
  "PackageVersion": "",
  "PackageArchitecture": "",
  "PackageDescription": "",
  "AsanReport": [
    "==156==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x503000000058 at pc 0x55555561a7e4 bp 0x7ffffffd1b80 sp 0x7ffffffd1340",
    "WRITE of size 1 at 0x503000000058 thread T0",
    "    #0 0x55555561a7e3 in __asan_memcpy (/tmp/tmpv73y9kwj/reproducer+0xc67e3) (BuildId: bc1331dfdfe6ec06a38aa5826490659a614cfc10)",
    "    #1 0x7ffff7ea0d18 in Lizard_writeBlock /fuzz/src/lib/lizard_compress.c:243:5",
    "    #2 0x7ffff7f3952d in Lizard_compress_generic /fuzz/src/lib/lizard_compress.c:535:13",
    "    #3 0x7ffff7f3952d in Lizard_compress_extState /fuzz/src/lib/lizard_compress.c:592:12",
    "    #4 0x7ffff7f66aa4 in LizardF_compressBlock /fuzz/src/lib/lizard_frame.c:461:18",
    "    #5 0x7ffff7f66aa4 in LizardF_compressUpdate /fuzz/src/lib/lizard_frame.c:554:19",
    "    #6 0x7ffff7f6498c in LizardF_compressFrame /fuzz/src/lib/lizard_frame.c:297:17",
    "    #7 0x55555565b62b in main /tmp/tmpv73y9kwj/reproducer.cpp:21:22",
    "    #8 0x7ffff794bd8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16",
    "    #9 0x7ffff794be3f in __libc_start_main csu/../csu/libc-start.c:392:3",
    "    #10 0x555555580304 in _start (/tmp/tmpv73y9kwj/reproducer+0x2c304) (BuildId: bc1331dfdfe6ec06a38aa5826490659a614cfc10)",
    "",
    "0x503000000058 is located 0 bytes after 24-byte region [0x503000000040,0x503000000058)",
    "allocated by thread T0 here:",
    "    #0 0x55555561c6ae in malloc (/tmp/tmpv73y9kwj/reproducer+0xc86ae) (BuildId: bc1331dfdfe6ec06a38aa5826490659a614cfc10)",
    "    #1 0x55555565b614 in main /tmp/tmpv73y9kwj/reproducer.cpp:19:24",
    "    #2 0x7ffff794bd8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16",
    "",
    "SUMMARY: AddressSanitizer: heap-buffer-overflow (/tmp/tmpv73y9kwj/reproducer+0xc67e3) (BuildId: bc1331dfdfe6ec06a38aa5826490659a614cfc10) in __asan_memcpy",
    "Shadow bytes around the buggy address:",
    "  0x502ffffffd80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
    "  0x502ffffffe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
    "  0x502ffffffe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
    "  0x502fffffff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
    "  0x502fffffff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
    "=>0x503000000000: fa fa 00 00 00 fa fa fa 00 00 00[fa]fa fa fa fa",
    "  0x503000000080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa",
    "  0x503000000100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa",
    "  0x503000000180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa",
    "  0x503000000200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa",
    "  0x503000000280: 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/lib/lizard_compress.c:243:5",
  "Source": [
    "    239        *start = LIZARD_FLAG_UNCOMPRESSED;",
    "    240        *op = start + 1;",
    "    241        MEM_writeLE24(*op, inputSize);",
    "    242        *op += 3;",
    "--->243        memcpy(*op, ip, inputSize);",
    "    244        *op += inputSize;",
    "    245        return 0;",
    "    246    ",
    "    247    _output_error:",
    "    248        LIZARD_LOG_COMPRESS(\"Lizard_writeBlock ERROR size=%d/%d flagsLen=%d literalsLen=%d lenLen=%d offset16Len=%d offset24Len=%d\\n\", (int)(*op-start), (int)(oend-start), flagsLen, literalsLen, lenLen, offset16Len, offset24Len);"
  ]
}

hgarrereyn avatar Sep 21 '25 04:09 hgarrereyn

Thanks for reporting it. I confirm the issue:

size=0 bound=23 written=11
size=1 bound=24 written=29 -> THE ISSUE
size=2 bound=25 written=25
size=3 bound=26 written=26
size=4 bound=27 written=27
size=5 bound=28 written=28
size=6 bound=29 written=29
size=7 bound=30 written=30
size=8 bound=31 written=31
size=9 bound=32 written=32
size=10 bound=33 written=33
size=11 bound=34 written=34
size=12 bound=35 written=35

inikep avatar Oct 09 '25 16:10 inikep

The issue is fixed at https://github.com/inikep/lizard/commit/d3becc7e80 in the main branch.

inikep avatar Oct 09 '25 17:10 inikep