Skip to content

Muse audit public findings 10/17/2026 #1062

Closed

Description

@bobberb

Audit's done. Most important thing first: the 3 "Critical" consensus-split bugs from the early signal did not survive verification. I diffed each against actual Bitcoin Core source before writing anything up — Core pre-scans for OP_SUCCESS in tapscript exactly like rbitcoin does, Core has no pre-fExec gate for OP_RESERVED, and Core's disabled-opcode check sits pre-fExec just like rbitcoin's. All three were false positives, and I've documented exactly why so you don't report bunk.

The real findings that replaced them: rbitcoin wrongly pre-fails OP_VERIF/OP_VERNOTIF in dead branches (Core skips them — fail-closed chain stall, High), wrongly requires a witness commitment whenever witness data exists without one (High, fail-closed), and its signature-encoding check omits Core's sighash-byte range check (Medium, latent). Plus 13 remote-DoS items and 6 store-integrity items, all with file:line evidence.

Here's your paste-ready public issue (non-sensitive only — the sensitive ones are in the attached private notes for your direct contact):


Audit notes: hardening, hygiene, and process gaps (non-consensus)

Independent read-only audit of master at 4e2a813 (~300k lines / 16 crates).
Static review only — no builds, no live infrastructure. Method: per-area deep
dives (consensus/script, P2P, store, primitives/crypto, mempool, RPC/Electrum/
Esplora/SV2, supply chain), each finding carries file:line evidence.

A small number of consensus-critical and remotely-triggerable findings are
being reported privately per SECURITY.md and are deliberately absent here.

What follows is the non-sensitive remainder: process, hygiene, hardening, and
defense-in-depth. No fund-loss or RCE paths were found (there is no wallet
crate; fund-moving RPCs are explicitly refused).

Verified clean (with reasoning): primitives/crypto (SIMD sound, no hand-rolled
crypto — uses bitcoin_hashes); P2P framing, IBD header validation, compact
blocks, rate limiting; store io_uring completion safety; mempool fee floors and
eviction ordering. Strongest positives: differential fuzzing against live
bitcoind oracles, CodeQL security-extended on PRs, 93% coverage gate,
clippy -D warnings, all 156 registry deps checksummed.

Supply chain & CI process

S1 — deny.toml bans are permissive. [bans] multiple-versions = "warn",
wildcards = "allow", deny = []. The lockfile shows what warn tolerates:
secp256k1 0.28.2 + 0.29.1 (two secp256k1-sys C builds), bitcoin_hashes
0.13/0.14/0.16, hex-conservative ×4, getrandom 0.2 + 0.4. Duplicate C builds
widen the patched surface ("patch one, miss the other"). Suggest
multiple-versions = "deny" with explicit skips, wildcards = "deny".

S2 — Fuzzing is nightly-cron only, not a PR gate.
.github/workflows/fuzz.yml — nightly/weekly libFuzzer, not a required check;
same for miri.yml (primitives only) and core-functional.yml. A
consensus-changing PR can merge with zero fuzz signal. Suggest a short
(60–120s) per-target PR job, or at minimum replaying fuzz/corpus as a
regression gate.

S3 — No sanitizer coverage in CI; Miri covers only primitives. No
ASan/TSan/UBSan jobs; rbitcoin-store has the largest unsafe surface and never
runs under Miri/sanitizers. Suggest nightly ASan+UBSan for store/net suites.

S4 — mutants proc-macro in [dependencies], not dev-deps
(crates/rbitcoin-consensus/Cargo.toml:15-16,
crates/rbitcoin-query/Cargo.toml:17-18). Third-party proc-macro compiled into
production builds for zero runtime effect. Move to dev-dependencies.

S5 — BIP324 v2 transport depends on an unmaintained AEAD crate.
Cargo.lock: bip324 0.11.0 → chacha20-poly1305 0.1.2 (Dec 2019,
superseded by chacha20poly1305 0.10.x). P2P is v2-only per SECURITY.md, so
all peer-traffic AEAD rests on code that will never receive a security patch.
No known vulnerability — structural risk. Suggest upgrading bip324 to a
release on chacha20poly1305 0.10 (or vendoring) and tracking it in
deny.toml.

S6 — SECURITY.md Scope omits the script-skip milestone. "Consensus and
script: pure-Rust verification" is promised without mentioning that the default
build skips script/sig checks below block 840000 (anchored milestone). The skip
is documented in the README/OPERATOR.md, but not in the security policy.
Suggest a "Validation caveats" note in SECURITY.md.

S7 — min_work_be in MilestoneAnchor is stored but never enforced
(crates/rbitcoin-consensus/src/milestone.rs). Documented as a "chain work
floor (Core nMinimumChainWork shape)"; nothing reads the field. Either
enforce it or remove it — a dead security-looking field invites wrong
assumptions.

S8 — 356 unwrap/expect in non-test consensus code. Spot-checked sites
are guarded, but suggest an ast-grep rule flagging new ones (CI already runs
ast-grep). Consider #![forbid(unsafe_code)] on the zero-unsafe crates
(mempool, electrum, cli, rpc, test) to lock in the current state.

Hardening gaps (defense-in-depth, not directly exploitable)

H1 — No absolute fee cap (-maxtxfee equivalent).
crates/rbitcoin-mempool/src/accept.rs. A fat-fingered multi-BTC fee is
admitted, relayed, and would be mined. Sender-confined loss; still cheap to add.

H2 — No MAX_MONEY range check on the mempool admission path
(accept.rs:365 checks overflow but not range). Values trace to validated
UTXOs, so not practically exploitable — defense-in-depth only.

H3 — Silent-payments hand-rolled EC has no fuzz target.
crates/rbitcoin-consensus/src/silent_payments.rs implements BIP-352 tweak
math by hand (combine_keys + mul_tweak), reachable from the Electrum serve
path with attacker-influenced txouts. 21 fuzz targets exist but none cover it.
Suggest a differential target vs a reference implementation.

H4 — Esplora: no CORS ACAO, GET /broadcast without Origin/Host checks
(crates/rbitcoin-esplora/src/server.rs:819). Restrictive default, but simple
cross-origin requests (e.g. <img> GET) aren't stopped by CORS. Remove
GET /broadcast; validate Origin/Host on POST /tx.

H5 — --sv2-tp-authority-sec on the command line is visible in ps;
a -file variant exists — nudge it in the docs.

H6 — RPC cookie file that is group/world-readable warns instead of
refusing
(crates/rbitcoin-rpc/src/auth.rs).

Minor notes

  • rbitcoin-primitives/src/merkle.rs:16-30: mutation predicate stricter than
    Core (any equal adjacent pair vs tail-only) — not practically exploitable.
  • rbitcoin-primitives/src/compact.rs:44-70: read_compact_size accepts
    non-canonical encodings; no wire consumer today — add a doc warning or a
    strict variant.
  • rbitcoin-primitives/src/scriptnum.rs:25-30: scriptnum_encode(i64::MIN)
    panics in debug; scriptnum_decode_width trusts caller max_len ≤ 8 —
    add a debug_assert.
  • rbitcoin-primitives/src/loc_simd.rs: aarch64 NEON target_feature on
    callees not callers — sound (NEON mandatory on AArch64), propagate for
    hygiene.
  • rbitcoin-primitives/src/lib.rs:198-208: Network::parse rejects
    testnet4 (functional gap).
  • rbitcoin-store: readonly_map.rs TOCTOU between metadata().len() and
    mmap (external truncate → SIGBUS); store.secret XOR is
    obfuscation-at-rest, not encryption (documented — fine).
  • rbitcoin-net/src/seeds.rs:135-157: DNS seed results not filtered for
    bogons; :307-320: flat 8192-entry AddrMan (no new/tried buckets),
    mitigated by asmap-diverse selection.
  • rbitcoin-net/src/chain.rs:174-186,372: unbounded Invalidated set /
    fork_tips — every entry costs the attacker a valid-PoW block
    (hashpower-bounded); consider a cap+FIFO anyway.
  • rbitcoin-consensus/src/script/interpreter.rs:108:
    uses_segwit_serialization misclassifies zero-input txs; unreachable in
    consensus (check_tx_local rejects empty inputs first) — defense-in-depth.

Coverage limits (honest)

No builds or test runs (no cargo toolchain in this environment); all findings
are manual static analysis. Bitcoin Core behaviors cited were diffed against
current Core source (src/script/interpreter.cpp,
src/validation.cpp). Skimmed rather than deeply reviewed: tx_relay.rs,
bip324 internals, parts of ibd/, SH build pipeline, fee-estimation math,
rbitcoin-node wiring, test/bench/log crates.


The private findings (2 High consensus + 1 Medium consensus + 1 milestone footgun + 13 DoS + 6 store-integrity, plus the false-positive corrections) are in the attached notes for your direct contact — kept out of the issue per the repo's disclosure policy.

Activity

  1. reardencode commented on Oct 18, 2026

    @reardencode
    Owner

    looking into these, thank you!

  2. rearden-grok commented on Oct 18, 2026

    @rearden-grok
    Contributor

    #1074 is the follow-up for the items that were not already in another PR.

    The cookie file refuses a mode wider than --rpc-cookie-perms (see #1060). cargo deny fails a new duplicate crate. mutants is an optional feature so the proc-macro is off in a normal node build. Seed inject and addr gossip drop unroutable clearnet addresses; addpeeraddress and --connect do not. Fork-tip hints are capped at 64. Invalidated hashes stay uncapped.

    P2P still has no absurd-fee cap, mempool fee math still checks u64 overflow rather than MAX_MONEY, and BIP324 stays on the chacha20-poly1305 0.1 that upstream bip324 0.11 links. Those are recorded in COMPAT.md, TESTING.md, and SECURITY.md. Fuzz stays a nightly job. A Sunday UBSan run covers asmap and block_wire only.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions