Skip to content

Audit fix: F-18 — cap recv_file() at 1 GiB to prevent disk-exhaustion DoS#157

Merged
adequatelimited merged 2 commits intomasterfrom
audit-fixes
Apr 13, 2026
Merged

Audit fix: F-18 — cap recv_file() at 1 GiB to prevent disk-exhaustion DoS#157
adequatelimited merged 2 commits intomasterfrom
audit-fixes

Conversation

@adequatelimited
Copy link
Copy Markdown
Collaborator

Summary

Promotes F-18 from audit-fixes to master.

Fix Issue Files Intent
F-18 #92 src/network.c, src/types.h, src/test/_f18-recv-file-limit.c.disable Cap cumulative bytes in recv_file() at 1 GiB; fix the disk-exhaustion DoS in which a malicious peer streams OP_SEND_FILE packets forever

Scope audit

 src/network.c                           |   7 ++
 src/test/_f18-recv-file-limit.c.disable | 212 ++++++++++++++++++++++++++++++++
 src/types.h                             |   1 +
 3 files changed, 220 insertions(+)

All changes localized. The .disable test harness adds 212 lines of test-only code that is NOT built by make test (the .disable suffix excludes it from the Makefile glob, matching the existing convention: gettx-tag-resolve.c.disable, proof-checkproof.c.disable, proof-support.c.disable).

Artifact sweep

Verified the diff contains NO:

  • PoW bypass (return VEOK at top of validate_pow())
  • LOCAL TEST ONLY, DO NOT COMMIT, F-.. TRACE, RESTORE before commit, -ftrivial-auto-var-init markers
  • Co-Authored-By attribution trailers

src/tfile.c:validate_pow() starts with the original const word32 peach_trigger[2] declaration, unchanged.

Build

make NO_CUDA=1 mochimo passes cleanly on default -Werror -Wextra -Wpedantic with gcc-13 on Ubuntu 24.04 WSL. No -Wno-error workaround needed.

Empirical validation (from PR #156)

Fork-based test harness (committed as .disable) that stands up a malicious peer streaming unlimited OP_SEND_FILE packets and verifies recv_file() trips the cap. Ran with cap temporarily lowered to 16 MiB for fast cycle (restored to 1 GiB before commit):

parent: cap = 16777216 bytes
parent: calling callserver() then recv_file()...
child: handshake done, streaming OP_SEND_FILE...
parent: recv_file returned 1 (expect 1 = VERROR)
parent: partial file correctly removed
[PASS] F-18: recv_file enforced the size cap

Test plan

  • CI builds pass on Ubuntu x64, Ubuntu arm64
  • CodeQL passes

recv_file() had no upper limit on total bytes received. The loop
appended OP_SEND_FILE packet payloads to the output file until the
peer sent a short (EOF-signalling) packet or the connection dropped.
A peer that streams full-size packets forever can fill the node's
disk -- remote DoS.

Added MAX_RECV_FILE_BYTES (1 GiB) in types.h and a cumulative `total`
counter in recv_file(). When total exceeds the cap, the loop breaks
and the existing error path deletes the partial file via
fclose+remove, returning VERROR. 1 GiB is well above any legitimate
single-file transfer the protocol performs (tfile.dat: tens-to-
hundreds of MB; block files: <= 448 MB by protocol limits).

Verified with a fork-based test harness (committed as .disable per
the existing convention for reference test code not wired into
`make test`). The harness forks a fake peer that streams OP_SEND_FILE
indefinitely; the parent calls recv_file() and confirms VERROR return
plus partial-file removal. Tested with cap temporarily lowered to
16 MiB for a fast cycle; cap restored to 1 GiB before commit.

Closes #92
fix(network): cap recv_file() cumulative bytes at 1 GiB
@adequatelimited adequatelimited merged commit 24109ae into master Apr 13, 2026
4 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant