Skip to content

fix(marauder): bound the read accumulator (untrusted-device DoS parity with Flipper)#148

Merged
xunholy merged 1 commit into
mainfrom
fix/marauder-read-accum-cap
Jun 28, 2026
Merged

fix(marauder): bound the read accumulator (untrusted-device DoS parity with Flipper)#148
xunholy merged 1 commit into
mainfrom
fix/marauder-read-accum-cap

Conversation

@xunholy

@xunholy xunholy commented Jun 27, 2026

Copy link
Copy Markdown
Owner

Summary

Fourth trust-boundary fix in the series. Brings the Marauder read path to parity with the Flipper's existing read-size cap.

The Flipper serial path caps in-flight reads at 8 MiB (flipper.defaultMaxAccumBytes + ErrResponseTruncated). The Marauder client — a separate codebase — had no equivalent bound: readUntilPromptCtx and the Stream goroutine append'd to their accumulators without limit. The Marauder reports attacker-influenced data (SSIDs / BSSIDs / BLE names from the surrounding RF, which an adversary can flood with thousands of forged beacons) over a link that could also be a malfunctioning or malicious board, so an unbounded read accumulator is a memory-DoS vector — and an asymmetry with the Flipper transport that already defends against it.

Change

  • defaultMaxAccumBytes = 8 MiB (same value as the Flipper) + SetMaxAccumBytes / accumCap(), mirroring flipper.(*Flipper).
  • readUntilPromptCtx (the Exec/ExecCtx path): once the accumulator exceeds the cap before the prompt arrives, return the partial output with ErrResponseTruncated — same contract as the Flipper path.
  • Stream goroutine: accum normally holds only the current unterminated line (trimmed at every \n); if it exceeds the cap, a single no-newline line is flooding memory — log, send stopscan, and end the stream cleanly.

Verification

  • New tests: the no-prompt flood truncation (partial output + ErrResponseTruncated, bounded size) and the accumCap default/override/reset resolver. Existing marauder suite unchanged.
  • task ci green (lint 0 / vet / build / test:full 0 fail / govulncheck clean); task eval 17/17.

Adversarial-input hardening (untrusted device) — anchors v0.769.0.

…y with Flipper)

The Flipper serial path caps in-flight reads at 8 MiB
(flipper.defaultMaxAccumBytes + ErrResponseTruncated). The Marauder client —
a separate codebase — had no equivalent bound: readUntilPromptCtx and the
Stream goroutine append()'d to their accumulators without limit. The Marauder
reports attacker-influenced data (SSIDs / BSSIDs / BLE names from the
surrounding RF, which an adversary can flood with thousands of forged
beacons) over a link that could also be a malfunctioning or malicious board,
so an unbounded read accumulator is a memory-DoS vector — and an asymmetry
with the Flipper transport that already defends against it.

Add the matching cap:
- defaultMaxAccumBytes = 8 MiB (same value as the Flipper) + SetMaxAccumBytes
  / accumCap(), mirroring flipper.(*Flipper).
- readUntilPromptCtx: once the accumulator exceeds the cap before the prompt
  arrives, return the partial output with ErrResponseTruncated (same contract
  as the Flipper path).
- Stream goroutine: accum normally holds only the current unterminated line
  (trimmed at every '\n'); if it exceeds the cap, a single no-newline line is
  flooding memory — log, send stopscan, and end the stream cleanly.

Verified: new tests for the no-prompt flood truncation (partial output +
ErrResponseTruncated, bounded size) and the accumCap default/override/reset
resolver; existing marauder suite unchanged. task ci green (lint 0 / vet /
build / test:full 0 fail / govulncheck clean); task eval 17/17.
@xunholy
xunholy merged commit 114ceed into main Jun 28, 2026
5 of 6 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