| 1 | set -e |
| 2 | export CYCLOPS_HOME=/opt/cyclops-v2 |
| 3 | PY=/opt/cyclops-venv/bin/python |
| 4 | T=/opt/cyclops-v2/targets/src/gguf-tools |
| 5 | H=/opt/cyclops-v2/oracle/harnesses |
| 6 | W=/opt/cyclops-v2/oracle/work/gguf |
| 7 | mkdir -p "$H" "$W" "$W/seeds" |
| 8 | |
| 9 | echo "=== [1/4] BRAIN: joern memory-corruption dataflow on gguflib.c ===" |
| 10 | $PY /opt/cyclops-v2/brain/brain.py "github.com/antirez/gguf-tools" "ml-model-loaders" "$T" "c" "fdfafbe" || true |
| 11 | |
| 12 | echo "=== [2/4] generate a valid seed GGUF (gets the harness past the magic/header) ===" |
| 13 | $PY - <<'PYEOF' |
| 14 | import struct |
| 15 | buf = b"GGUF" |
| 16 | buf += struct.pack("<I", 3) |
| 17 | buf += struct.pack("<Q", 0) |
| 18 | buf += struct.pack("<Q", 1) |
| 19 | buf += struct.pack("<Q", 1) + b"a" |
| 20 | buf += struct.pack("<I", 8) |
| 21 | buf += struct.pack("<Q", 1) + b"b" |
| 22 | open("/opt/cyclops-v2/oracle/work/gguf/seeds/seed1.gguf","wb").write(buf) |
| 23 | print("seed bytes:", len(buf)) |
| 24 | PYEOF |
| 25 | |
| 26 | echo "=== [3/4] write libFuzzer harness for the full parse path ===" |
| 27 | cat > "$H/gguf_harness.c" <<'EOF' |
| 28 | // Drive the real parse path: gguf_open() mmaps + parses header, then we walk |
| 29 | // every key and tensor (attacker-controlled offsets/lengths/type enums). |
| 30 | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
| 31 | char path[] = "/dev/shm/gguffzXXXXXX"; |
| 32 | int fd = mkstemp(path); |
| 33 | if (fd < 0) return 0; |
| 34 | if (write(fd, data, size) != (ssize_t)size) { close(fd); unlink(path); return 0; } |
| 35 | close(fd); |
| 36 | gguf_ctx *ctx = gguf_open(path); |
| 37 | if (ctx) { |
| 38 | gguf_key key; |
| 39 | while (gguf_get_key(ctx, &key)) { |
| 40 | // touch the value (exercises type dispatch / value parsing) |
| 41 | gguf_print_value(ctx, key.type, key.val, 0); |
| 42 | } |
| 43 | gguf_tensor tensor; |
| 44 | while (gguf_get_tensor(ctx, &tensor)) { /* walks tensor info offsets */ } |
| 45 | gguf_close(ctx); |
| 46 | } |
| 47 | unlink(path); |
| 48 | return 0; |
| 49 | } |
| 50 | EOF |
| 51 | |
| 52 | echo "=== [4/4] compile (ASan+UBSan+fuzzer) and launch background fuzz ===" |
| 53 | cd "$W" |
| 54 | clang -g -O1 -fsanitize=fuzzer,address,undefined -fno-omit-frame-pointer \ |
| 55 | "$H/gguf_harness.c" "$T/gguflib.c" "$T/fp16.c" "$T/sds.c" \ |
| 56 | -I"$T" -lm -o "$W/fuzz_gguf" 2>&1 | tail -5 |
| 57 | echo "compiled: $(ls -la $W/fuzz_gguf 2>/dev/null | awk '{print $5}') bytes" |
| 58 | mkdir -p "$W/corpus" && cp "$W/seeds/"* "$W/corpus/" 2>/dev/null || true |
| 59 | nohup "$W/fuzz_gguf" -max_total_time=1800 -print_final_stats=1 \ |
| 60 | -artifact_prefix="$W/crash-" "$W/corpus" "$W/seeds" \ |
| 61 | > "$W/fuzz.log" 2>&1 & |
| 62 | echo "FUZZ_PID=$!" |
| 63 | sleep 8 |
| 64 | echo "=== early fuzz log ==="; tail -8 "$W/fuzz.log" 2>/dev/null |