- Rust IR encoder (encodeRustIr): walks HIR expr trees, fires SafetyObserver hooks - Swift SIL frontend (fold-swift): swiftc -emit-sil parser, demangling, NDJSON output - SwiftBackend: --mode swift CLI routing, shells out to fold-swift - Binary-to-fuzz: Mach-O export discovery, ARM64 ABI param-count inference, dlopen harness generation - Language-agnostic IR: json_keys canonical constants, Call/Member/ArraySubscript/Cast/HeapNew nodes - Dogfood fixes: SARIF kind field, cross-detector dedup, harness_file in predictions, Swift stoi safety - Corpuses: swift-sil-z3-corpus.md, binary-fuzz-corpus.md - AGENTS.md: cross-language wire format rule
4.5 KiB
4.5 KiB
Binary-to-Fuzz Pipeline Corpus
Generated 2026-05-27. Reference for Mach-O binary → ABI inference → harness → fuzz.
ARM64 Mach-O ABI (Apple Platforms)
Source: Apple "ARM64 Function Calling Conventions"
Register Assignment
| Register | Role |
|---|---|
| x0–x7 | Integer/pointer arguments 0–7 |
| d0–d7 | Floating-point arguments 0–7 |
| x8 | Indirect result location |
| x9–x15 | Temporary (caller-saved) |
| x16–x17 | Intra-procedure-call scratch |
| x18 | Platform register |
| x19–x28 | Callee-saved |
| x29 (fp) | Frame pointer |
| x30 (lr) | Link register (return address) |
| sp | Stack pointer |
Parameter Count Inference Heuristic
For each exported function, r2 disassembles the first basic block. Count distinct register reads (not writes) among x0–x7 BEFORE the first call/branch. This estimates the parameter count.
Example: function reads x0, x1, x2 → 3 parameters
function reads x0 only → 1 parameter
function writes to x0, reads nothing → 0 parameters
Limitations:
- Variadic functions are not detected
- Struct-by-value arguments (passed via x8 indirect + x0 pointer) may be miscounted
- Functions that stash args to callee-saved regs before use may be undercounted
- Heuristic only — not a formal ABI analysis
Mach-O Structure
| Section | Contents |
|---|---|
__TEXT,__text |
Executable code |
__TEXT,__cstring |
C string literals |
__DATA,__data |
Initialized data |
__DATA,__bss |
Zero-initialized data |
__LINKEDIT |
Export trie, symbol table, string table |
__TEXT,__swift5_* |
Swift metadata sections |
r2 Binary Analysis Commands (fold usage)
| r2 Command | Output | fold Use |
|---|---|---|
aflj |
Function list JSON | Discover functions for fuzz targets |
isj |
Symbol table JSON | Resolve function names, classify exports |
iEj |
Exports JSON | Identify dlopen-able symbols |
izzj |
String catalog JSON | Existing binary security detector path |
pdbj @ addr |
Basic block disassembly JSON | ABI parameter-count inference |
afij @ addr |
Function info JSON | Function boundaries, call graph |
dlopen Harness Pattern
Source: rev.ng blog, AFL++ binary-only fuzzing docs
// Auto-generated by fold binary-fuzz-prep
// Target: /path/to/library.node or Mach-O binary
#include <dlfcn.h>
#include <stdint.h>
#include <stddef.h>
// Resolved function pointers
typedef void (*target_fn_t)(uint64_t, uint64_t, uint64_t);
static target_fn_t target_fn = NULL;
// One-time library load
__attribute__((constructor))
static void load_library(void) {
void* handle = dlopen("TARGET_PATH", RTLD_NOW);
if (handle) {
target_fn = (target_fn_t)dlsym(handle, "TARGET_SYMBOL");
}
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (!target_fn || size < 24) return 0; // 3 × uint64_t
uint64_t arg0, arg1, arg2;
memcpy(&arg0, data + 0, 8);
memcpy(&arg1, data + 8, 8);
memcpy(&arg2, data + 16, 8);
target_fn(arg0, arg1, arg2);
return 0;
}
Compile:
clang++ -fsanitize=fuzzer,address -g -O1 harness.cc -o harness.fuzz
Prior Art
| Source | Relevance |
|---|---|
| rev.ng blog "Fuzzing binaries with libFuzzer" | dlopen + harness pattern for Mach-O/ELF |
| arXiv 2306.15596 "Automated Fuzzing Harness Generation" | API fuzzing harness synthesis |
| AFL++ binary-only docs | dlopen-based library fuzzing with persistent mode |
| Romain Thomas "Fuzzing Windows ARM64" | ARM64 binary fuzzing with QBDI |
| JMPscare (BAR 2021) | Introspection for binary-only fuzzing |
| "Binary-level directed fuzzing" (ANSSI thesis) | Directed fuzzing of closed-source binaries |
Integration Architecture
Mach-O binary → r2 aflj → function list
↓
r2 pdbj @ addr → param count (ABI inference)
↓
harness_emitter → dlopen C harness
↓
clang++ -fsanitize=fuzzer,address → harness.fuzz
↓
libFuzzer + ASan → crash artifacts
↓
ASan parser → ConcreteWitness findings
References
- Apple ARM64 ABI:
https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms - rev.ng binary fuzzing:
https://rev.ng/blog/fuzzing-binaries - AFL++ binary-only:
https://aflplus.plus/docs/fuzzing_binary-only_targets/ - arXiv 2306.15596: Automated Fuzzing Harness Generation for Library APIs
- r2 book:
https://book.rada.re/