etc.git


etc

~ scripts and random things
git clone https://github.com/djnnvx/etc.git
log | files | refs | archive

commit 57bf3d3baca9e824ac109016230b042bf98bcb26
parent 198c732cea0e80b21bfc698c4ff5600165a97157
Author: djnn <[email protected]>
Date: Thu, 2 Apr 2026 22:39:35 +0900

chore(fuzzing): improve corpus for libusb

Diffstat:
Mfuzzing/libusb/cleanup.sh | 2++
Afuzzing/libusb/fuzz_extra.c | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mfuzzing/libusb/gen_corpus.py | 92++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Mfuzzing/libusb/run-all.sh | 30+++++++++++++++++-------------
Mfuzzing/libusb/setup.sh | 7++++---

5 files changed, 170 insertions(+), 19 deletions(-)

diff --git a/fuzzing/libusb/cleanup.sh b/fuzzing/libusb/cleanup.sh
@@ -30,6 +30,7 @@ pkill -u "$(id -un)" -f "fuzz_descriptor"    2>/dev/null || true
 pkill -u "$(id -un)" -f "fuzz_bos"           2>/dev/null || true
 pkill -u "$(id -un)" -f "fuzz_iad"           2>/dev/null || true
 pkill -u "$(id -un)" -f "fuzz_usbfs"         2>/dev/null || true
+pkill -u "$(id -un)" -f "fuzz_extra"         2>/dev/null || true
 
 [[ "${CRASHES_ONLY}" -eq 1 ]] && { ok "Processes killed."; exit 0; }
 
@@ -38,6 +39,7 @@ if [[ "${KEEP_BUILDS}" -eq 0 ]]; then
     rm -f fuzz_bos        fuzz_bos.cmplog
     rm -f fuzz_iad        fuzz_iad.cmplog
     rm -f fuzz_usbfs      fuzz_usbfs.cmplog
+    rm -f fuzz_extra      fuzz_extra.cmplog
     ok "Binaries removed."
 fi
 
diff --git a/fuzzing/libusb/fuzz_extra.c b/fuzzing/libusb/fuzz_extra.c
@@ -0,0 +1,58 @@
+/*
+ * Harness: endpoint extra chain walking via libusb_get_ss_endpoint_companion_descriptor().
+ *
+ * fuzz_descriptor.c parses configs and immediately frees them — it never calls
+ * the public API that walks endpoint->extra. This harness does both: parse the
+ * config, then call the SS endpoint companion extractor on every endpoint's
+ * extra data. That walking loop is completely unexercised by any other harness.
+ */
+
+#include "fuzz-include/libusb_fuzz.h"
+#include "libusb-src/libusb/descriptor.c"
+
+static int fuzz_one(const uint8_t *data, size_t size)
+{
+    if (size < 1)
+        return 0;
+
+    struct libusb_config_descriptor *config = NULL;
+    int r = raw_desc_to_config(NULL, data,
+        (int)(size > (size_t)INT_MAX ? INT_MAX : size), &config);
+    if (r != LIBUSB_SUCCESS || !config)
+        return 0;
+
+    for (uint8_t i = 0; i < config->bNumInterfaces; i++) {
+        const struct libusb_interface *iface = &config->interface[i];
+        for (int a = 0; a < iface->num_altsetting; a++) {
+            const struct libusb_interface_descriptor *alt = &iface->altsetting[a];
+            for (uint8_t e = 0; e < alt->bNumEndpoints; e++) {
+                const struct libusb_endpoint_descriptor *ep = &alt->endpoint[e];
+
+                struct libusb_ss_endpoint_companion_descriptor *comp = NULL;
+                libusb_get_ss_endpoint_companion_descriptor(NULL, ep, &comp);
+                if (comp)
+                    libusb_free_ss_endpoint_companion_descriptor(comp);
+            }
+        }
+    }
+
+    libusb_free_config_descriptor(config);
+    return 0;
+}
+
+#ifdef __AFL_FUZZ_TESTCASE_LEN
+__AFL_FUZZ_INIT();
+int main(void)
+{
+    __AFL_INIT();
+    uint8_t *buf = __AFL_FUZZ_TESTCASE_BUF;
+    while (__AFL_LOOP(10000))
+        fuzz_one(buf, __AFL_FUZZ_TESTCASE_LEN);
+    return 0;
+}
+#else
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+    return fuzz_one(data, size);
+}
+#endif
diff --git a/fuzzing/libusb/gen_corpus.py b/fuzzing/libusb/gen_corpus.py
@@ -363,7 +363,93 @@ dev = device_desc(1)
 zero_len = struct.pack("<BBHBBBBB", 9, 0x02, 0, 0, 1, 0, 0x80, 50)
 write_seed("usbfs", "usbfs_wtotallength_0", dev + zero_len)
 
+# ─── EXTRA / ENDPOINT COMPANION SEEDS ───────────────────────────────────────
+# Seeds for fuzz_extra.c: config + endpoint with SS companion extra data.
+# LIBUSB_DT_SS_ENDPOINT_COMPANION = 0x30, size = 6 bytes.
+
+def ss_companion(blen=6, bulk_max_burst=15, bmAttr=0, wBytesPerInterval=0):
+    return struct.pack("<BBBBH", blen, 0x30, bulk_max_burst, bmAttr, wBytesPerInterval)
+
+print("[*] Generating extra/ seeds...")
+
+# Valid endpoint + SS companion (6 bytes)
+comp = ss_companion(6)
+ep = endpoint_desc(0x81, 0x02, 512)  # bulk-in
+total = 9 + 9 + len(ep) + len(comp)
+body = config_header(total) + iface_desc(1) + ep + comp
+write_seed("extra", "extra_ep_with_companion", body)
+
+# Endpoint + companion with bLength=5 (below LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE=6)
+comp_short = ss_companion(5)
+total = 9 + 9 + len(ep) + 5
+body = config_header(total) + iface_desc(1) + ep + comp_short[:5]
+write_seed("extra", "extra_companion_blen_5", body)
+
+# Endpoint + companion with bLength=2 (minimum header only)
+total = 9 + 9 + len(ep) + 2
+body = config_header(total) + iface_desc(1) + ep + b"\x02\x30"
+write_seed("extra", "extra_companion_blen_2", body)
+
+# Endpoint + companion with bLength=0 (zero — hits bLength < 2 guard)
+total = 9 + 9 + len(ep) + 2
+body = config_header(total) + iface_desc(1) + ep + b"\x00\x30"
+write_seed("extra", "extra_companion_blen_0", body)
+
+# Multiple endpoints each with a companion
+eps_with_comp = b""
+for i in range(4):
+    eps_with_comp += endpoint_desc(0x80 | (i + 1), 0x02, 512) + ss_companion(6, i, 0, 0)
+total = 9 + 9 + len(eps_with_comp)
+body = config_header(total) + iface_desc(4) + eps_with_comp
+write_seed("extra", "extra_four_eps_with_companions", body)
+
+# Endpoint with class-specific extra data (not a companion) before the companion
+class_extra = struct.pack("<BBB", 3, 0x24, 0x01)
+comp = ss_companion(6)
+total = 9 + 9 + len(ep) + len(class_extra) + len(comp)
+body = config_header(total) + iface_desc(1) + ep + class_extra + comp
+write_seed("extra", "extra_class_desc_then_companion", body)
+
+# No endpoints (nothing to walk)
+write_seed("extra", "extra_no_endpoints",
+    config_header(9, 0))
+
+# Many class-specific descriptors between interfaces (seeds config->extra_length int accumulation)
+# 30 interfaces, each preceded by several 3-byte class-specific descriptors
+class_blobs = struct.pack("<BBB", 3, 0x24, 0xFF) * 30
+ifaces_many = b""
+for i in range(30):
+    ifaces_many += class_blobs + iface_desc(0, i)
+total = 9 + len(ifaces_many)
+body = config_header(total, 30) + ifaces_many
+write_seed("extra", "extra_class_desc_accumulation", body)
+
+# ─── ADDITIONAL DESCRIPTOR SEEDS ─────────────────────────────────────────────
+# Extra seeds to stress config->extra_length signed int accumulation.
+
+print("[*] Generating additional descriptor/ seeds...")
+
+# Config with many interfaces each with large class-specific blobs (bLength=0xFF)
+# Accumulates config->extra_length toward INT_MAX with enough mutations
+big_class = struct.pack("<BB", 0xFF, 0x24) + b"\x42" * 253  # 255-byte class desc
+ifaces_with_big_class = b""
+for i in range(8):
+    ifaces_with_big_class += big_class + iface_desc(0, i)
+total = 9 + len(ifaces_with_big_class)
+body = config_header(total, 8) + ifaces_with_big_class
+write_seed("descriptor", "config_large_class_descs", body)
+
+# Config where every byte after the header is a class-specific descriptor type
+class_chain = b""
+for _ in range(50):
+    class_chain += struct.pack("<BBB", 3, 0x25, 0x00)  # 3-byte class desc, type 0x25
+total = 9 + len(class_chain) + 9  # one interface at the end
+body = config_header(total, 1) + class_chain + iface_desc(0)
+write_seed("descriptor", "config_class_desc_chain", body)
+
 print(f"\n[+] Corpus generated in {CORPUS_DIR}/")
-for d in ["descriptor", "bos", "iad", "usbfs"]:
-    n = len(os.listdir(os.path.join(CORPUS_DIR, d)))
-    print(f"    {d}/: {n} seeds")
+for d in ["descriptor", "bos", "iad", "usbfs", "extra"]:
+    path = os.path.join(CORPUS_DIR, d)
+    if os.path.isdir(path):
+        n = len(os.listdir(path))
+        print(f"    {d}/: {n} seeds")
diff --git a/fuzzing/libusb/run-all.sh b/fuzzing/libusb/run-all.sh
@@ -30,7 +30,7 @@ case "${1:-}" in
 esac
 
 command -v afl-fuzz > /dev/null 2>&1 || die "afl-fuzz not found."
-for h in fuzz_descriptor fuzz_bos fuzz_iad fuzz_usbfs; do
+for h in fuzz_descriptor fuzz_bos fuzz_iad fuzz_usbfs fuzz_extra; do
     [[ -f "${h}" ]]        || die "${h} not found. Run setup.sh first."
     [[ -f "${h}.cmplog" ]] || die "${h}.cmplog not found."
 done
@@ -44,14 +44,18 @@ mkdir -p "${OUTPUT}"
 export AFL_SKIP_CPUFREQ=1
 export AFL_AUTORESUME=1
 export AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1
-export ASAN_OPTIONS="abort_on_error=1:symbolize=0:detect_leaks=0:allocator_may_return_null=1"
+export AFL_DISABLE_TRIM=1
+export AFL_KEEP_TIMEOUTS=1
+export ASAN_OPTIONS="abort_on_error=1:symbolize=0:detect_leaks=0:allocator_may_return_null=1:detect_stack_use_after_scope=1"
 export UBSAN_OPTIONS="print_stacktrace=0:halt_on_error=1"
 
+# power_schedule: master=explore (breadth-first), secondaries vary per harness
+# -l 2AT on cmplog instances: type-2 transform + auto token insertion
 afl_cmd() {
-    local role="$1" flag="$2" target="$3" corpus_sub="$4"
+    local role="$1" flag="$2" target="$3" corpus_sub="$4" sched="${5:-fast}"
     local cmplog=""
-    [[ "${flag}" == "master" ]] && cmplog="-c ./${target}.cmplog"
-    echo "afl-fuzz ${role} -i '${CORPUS}/${corpus_sub}' -o '${OUTPUT}' -x '${DICT}' -t 500 -p fast -m none ${cmplog} -- './${target}'"
+    [[ "${flag}" == "master" || "${flag}" == "cmplog" ]] && cmplog="-c ./${target}.cmplog -l 2AT"
+    echo "afl-fuzz ${role} -i '${CORPUS}/${corpus_sub}' -o '${OUTPUT}' -x '${DICT}' -t 500 -p ${sched} -m none ${cmplog} -- './${target}'"
 }
 
 log "Starting session '${SESSION}'..."
@@ -68,14 +72,14 @@ sleep 0.5
 
 send() { tmux send-keys -t "${SESSION}:${1}.${2}" "${3}" Enter; }
 
-send 0 0 "$(afl_cmd '-M master_descriptor' master fuzz_descriptor descriptor)"
-send 0 1 "$(afl_cmd '-S sec_descriptor'    sec    fuzz_descriptor descriptor)"
-send 0 2 "$(afl_cmd '-M master_bos'        master fuzz_bos        bos)"
-send 0 3 "$(afl_cmd '-S sec_bos'           sec    fuzz_bos        bos)"
-send 1 0 "$(afl_cmd '-M master_iad'        master fuzz_iad        iad)"
-send 1 1 "$(afl_cmd '-S sec_iad'           sec    fuzz_iad        iad)"
-send 1 2 "$(afl_cmd '-M master_usbfs'      master fuzz_usbfs      usbfs)"
-send 1 3 "$(afl_cmd '-S sec_usbfs'         sec    fuzz_usbfs      usbfs)"
+send 0 0 "$(afl_cmd '-M master_descriptor' master  fuzz_descriptor descriptor explore)"
+send 0 1 "$(afl_cmd '-S sec_descriptor'   sec     fuzz_descriptor descriptor rare)"
+send 0 2 "$(afl_cmd '-M master_bos'       master  fuzz_bos        bos        explore)"
+send 0 3 "$(afl_cmd '-S sec_bos'          cmplog  fuzz_bos        bos        coe)"
+send 1 0 "$(afl_cmd '-M master_iad'       master  fuzz_iad        iad        explore)"
+send 1 1 "$(afl_cmd '-S sec_iad'          sec     fuzz_iad        iad        lin)"
+send 1 2 "$(afl_cmd '-M master_usbfs'     master  fuzz_usbfs      usbfs      explore)"
+send 1 3 "$(afl_cmd '-S sec_extra'        sec     fuzz_extra      extra      fast)"
 send 2 0 "watch -n 15 'afl-whatsup ${OUTPUT} 2>/dev/null || echo waiting...'"
 
 ok "Session started. Attach: tmux attach -t ${SESSION}"
diff --git a/fuzzing/libusb/setup.sh b/fuzzing/libusb/setup.sh
@@ -63,21 +63,22 @@ fi
 log "Building harnesses..."
 CC="afl-clang-fast"
 SRC="libusb-src/libusb"
-CFLAGS="-fsanitize=address,undefined -g -O1 -fno-omit-frame-pointer"
+CFLAGS="-fsanitize=address,undefined,signed-integer-overflow -g -O0 -fno-omit-frame-pointer"
 CFLAGS+=" -I fuzz-include -I ${SRC} -DFUZZ_BUILD"
 CFLAGS+=" -Wno-implicit-function-declaration -Wno-incompatible-pointer-types -Wno-unused-function -Wno-unused-variable"
 
 build() {
     local name="$1" extra_src="${2:-}" extra_flags="${3:-}"
     log "  ${name}..."
-    ${CC} ${CFLAGS} ${extra_flags} "${name}.c" fuzz_stubs.c ${extra_src} -o "${name}" -lpthread
-    AFL_LLVM_CMPLOG=1 ${CC} ${CFLAGS} ${extra_flags} "${name}.c" fuzz_stubs.c ${extra_src} -o "${name}.cmplog" -lpthread
+    AFL_LLVM_LAF_ALL=1 AFL_LLVM_INSTRUMENT=CLASSIC ${CC} ${CFLAGS} ${extra_flags} "${name}.c" fuzz_stubs.c ${extra_src} -o "${name}" -lpthread
+    AFL_LLVM_LAF_ALL=1 AFL_LLVM_INSTRUMENT=CLASSIC AFL_LLVM_CMPLOG=1 ${CC} ${CFLAGS} ${extra_flags} "${name}.c" fuzz_stubs.c ${extra_src} -o "${name}.cmplog" -lpthread
 }
 
 build fuzz_descriptor
 build fuzz_bos
 build fuzz_iad
 build fuzz_usbfs "fuzz_usbfs_shim.c" "-I ${SRC}/os"
+build fuzz_extra
 ok "Harnesses built."
 
 # corpus