- 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
137 lines
4.5 KiB
Markdown
137 lines
4.5 KiB
Markdown
# 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
|
||
|
||
```c
|
||
// 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:
|
||
```bash
|
||
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/`
|