Repository navigation
Releases: 1rhino2/fnprint
Release list
fnprint 0.6.1
published to crates.io as fnprint 0.6.1 (and the fnprint-* crates), so cargo install fnprint serves the 0.6 line now. 0.6.0 could not go to crates.io because it depended on the unicorn-tci fork by git tag; both fork crates are published as 2.1.6 and the dep is a plain registry pin again. no code change from 0.6.0, the bench is byte-identical. what 0.6 is, aarch64, 1:1 alignment with the call graph, name-free match, the ghidra script, is in the 0.6.0 notes and CHANGELOG.md.
fnprint 0.6.0
the big one. three things that were roadmap are in.
aarch64. the loader takes arm64 ELF, the emulator seeds x0..x5 and steers bl/blr/ret/svc/b.cond/cbz/tbz, and the effect model never sees the isa, so an x86-64 corpus names a stripped aarch64 build: zlib -O0, 42 named, 42 right. eval across the two: zlib x86 -> arm 97.7% rank-1, lua (876 functions) 92.6% aligned at 99.9% precision. needs unicorn-tci 2.1.6 (a per-arch tcg_qemu_tb_exec rename, two archs in one tci build used to share the x86 interpreter), pinned to the fork's git tag until it is on crates.io, so cargo install fnprint still gets 0.5.1 for now. cargo install --git https://github.com/1rhino2/fnprint fnprint gets this.
1:1 alignment with the call graph. query and match used to take the best corpus hit per function on its own, so on a stripped -O2 zlib 27 functions all claimed deflateStateCheck (the inlined state check is all junk input reaches). now a corpus function is claimed once, best pair first, then the assignment is rescored with how many of a pair's paired callers and callees are paired with each other (edges from a static sweep of each body). gcc O3 vs clang O3: 44% rank-1, 92.6% aligned at 100% precision. that stripped clang -O2 named from an -O0 corpus: 9 right / 28 wrong -> 9 right / 2 wrong. --graph-weight sets the lift.
name-free match. two fully stripped builds diff on behavior plus call graph, changed/unchanged pairs and the unpaired rest, the stripped-firmware view. --align forces it on a symboled pair.
also: calls are named from the import table only (the part that survives strip), .eh_frame is always merged into discovery so a stripped .so gives up its statics (the README demo went 9 -> 27 names), a ghidra script in contrib/ghidra_scripts that runs query or triage and applies the result (tested headless on 12.1.2), blanket execution behind FNPRINT_BLANKET=N (off, numbers in bench/NUMBERS.md say why), and a cross-arch smoke in ci.
full notes in CHANGELOG.md, numbers in bench/NUMBERS.md.
v0.5.1
bugfix release. published to crates.io as fnprint 0.5.1 (and the fnprint-* crates).
fnprint query ... | head on 0.5.0 aborted with a panic banner: every human and json line went through println!, which panics on a broken pipe, and release builds are panic = "abort". 0.5.0 had only guarded completions. now every output path exits 0 quietly when the reader hangs up, and any other write error still fails properly.
also: --help examples column is aligned, the completion example installs per-user instead of into /etc, and main is back in sync with the tags (0.5.0 had been tagged on a side branch).
full notes in CHANGELOG.md.
v0.5.0
routine quality-of-life release. surface + DX only, nothing touches the fingerprint algorithm, the corpus content, or the sandbox/privsep model. published to crates.io as fnprint 0.5.0 (and the fnprint-* crates).
new fnprint completions <shell> subcommand (bash/zsh/fish/powershell/elvish). --limit N on match/query/triage caps the human tables while json/r2 stay uncapped, so the machine contract is unchanged. index --out now writes the corpus in one transaction instead of a commit per function, much faster on big binaries, with the same rows and bands. plus a Makefile, a CHANGELOG, a --help examples block, and readme notes.
full notes in CHANGELOG.md.
v0.4.3 - dependency freshness
Routine dependency-freshness release. No behavior change, no source changes: the two parsers that read untrusted bytes were a couple of 0.x releases behind, so this refreshes them plus a semver-compatible update of the rest.
What changed
- goblin 0.8.2 -> 0.10.7 (parses attacker-controlled ELF headers, symbols, sections)
- gimli 0.31.1 -> 0.34.0 (parses attacker-controlled .eh_frame FDE ranges)
- goblin's byte reader: scroll 0.12 -> 0.13
- semver-compatible
cargo updatefor the rest; the pinned no-JIT unicorn fork is untouched
Both parser bumps compiled against the loader with no API migration, so the diff is dependency manifests plus Cargo.lock.
Verified
- Determinism holds: the same ELF indexed at
FNPRINT_SHARDS=1and=2produces byte-identical corpus DBs. - Hostile input (truncated / malformed ELF, random bytes,
/dev/zero) still degrades to a clean error, no panic/hang/OOM. - 61 tests green (incl. the loader's malformed-ELF cases and the jail tests), cargo audit clean, two independent source-reading security reviewers returned no finding.
Old 0.3.x corpora still load; the on-disk fingerprint format is unchanged by a parser bump. See SECURITY-AUDIT.md for the 0.4.3 maintenance note.
v0.4.2 - security hardening
Security hardening release. Fixes all 11 findings from an external security audit of 0.4.1 (1 High, 8 Medium, 2 Low), each with a regression test, verified by two independent source-reading reviewers.
Highlights
- Bundled SQLite 3.45.0 -> 3.50.2, past the fix for CVE-2025-3277, with a build-time version-floor assert (rusqlite 0.31 -> 0.37).
- The seccomp worker jail drops two cross-process primitives (
tgkill,sched_setaffinity) and now, before it reads any input, closes inherited fds, armsPR_SET_PDEATHSIG, and clears its environment. - The untrusted corpus reader pins the
funcstable to an exact physical schema (no view/virtual/generated/hidden columns), uses byte-accurate length guards, and enforces aggregate row/byte budgets. - Input reads open once (no stat/read TOCTOU) and reject non-regular files.
- The loader rejects byte-overlapping PT_LOAD segments; the emulator coalesces its setup maps and propagates real mapping errors.
Also
indexnow reports the mean coverage of the signal-carrying prints, so you can tell at a glance whether the fingerprints rest on real behavior or on guard-heavy stubs.- Bounded sharded-index memory, an Arc-shared symbol map and input buffer, a clamped CPU/deadline ceiling, and terminal-escaped file paths in all output.
Full per-finding writeup is in SECURITY-AUDIT.md.
0.4.1: deterministic match output
patch release, one fix.
match --format json (and the human changed-behavior table) could come out in a different order on two identical runs. the changed list was sorted by similarity only, and functions that tied on similarity fell back to hashmap iteration order, which is not stable. so diffing two match runs of the same pair gave spurious churn. added a name tiebreak, the same one query and eval already had. output is byte-identical run to run now.
found during an exhaustive audit pass. nothing else changed. fingerprint format is unchanged, so corpus .db files from 0.3.x and 0.4.0 still load. upgrade with cargo install fnprint.
0.4.0: json/rizin output, opt-in sharding
machine output and disassembler integration.
every command now takes --format {human,json,r2}. json is a stable schema (schema_version 1) for scripts and the ghidra import stub in contrib/. r2 emits afn rename commands you run straight in rizin/radare2 with . fnprint.r2, no plugin to install. symbol names from the target get sanitized before they reach json or r2, so a crafted name in a corpus cant inject r2 commands. schemas and setup in docs/integrations.md.
index can shard across several jailed worker processes with FNPRINT_SHARDS=N. its off by default: each worker reloads the whole elf and runs its own interpreter vm, so on normal binaries the per-worker overhead cancels the parallelism (measured neutral on small, slower on a mid-size lib at a per-core count). it only pays off on big, function-rich corpora, so you turn it on when you actually need it. output is byte-identical whatever the shard count.
fingerprint format is unchanged from 0.3.x, so existing corpus .db files still load. upgrade with cargo install fnprint.
v0.3.4
Metadata release for discoverability. Every published crate now carries keywords and categories so they show up in crates.io search and category listings, not just the top-level fnprint cli. No code change from 0.3.3.
install:
cargo install fnprint
0.3.3
Metadata-only release, no code change from 0.3.2. Refreshed crates.io keywords, categories, and description, and added the docs.rs link so the crate surfaces in more searches. Behavior, fingerprints, and security posture are identical to 0.3.2.