Cap a report at 32 KB on both sides of the upload

The client would send up to 256 KB and the sink answered a settling 400 above
64 KB, so the two disagreed about what a valid report is, and the client
deletes what it is told is invalid. Unreachable in practice — the module table
and backtrace are both bounded, which puts the ceiling near 10 KB — but the
two constants have to agree for the disagreement not to matter.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
majcosta
2026-07-28 10:25:47 -03:00
committed by majcosta
co-authored by Claude Opus 5
parent 1d0b4beb4c
commit c3e2d1ae3d
3 changed files with 10 additions and 4 deletions
+1 -1
View File
@@ -43,7 +43,7 @@ assert.equal(await sent.body.get("files[0]").text(), REPORT);
// junk: client should delete these, so they must be 400
assert.equal((await post("hello")).status, 400);
assert.equal((await post("x".repeat(64 * 1024 + 1))).status, 400);
assert.equal((await post("x".repeat(32 * 1024 + 1))).status, 400);
// throttled: 429, and nothing reaches Discord. reportIsSettled() leaves 429
// unsettled, so the client keeps the report for next launch.
+5 -1
View File
@@ -13,7 +13,11 @@
// So never answer a settling 4xx for a failure on our side: that throws the report
// away. 429 is the one 4xx that is safe, because the client does not settle it.
const MAX_BYTES = 64 * 1024; // reports run 2-8 KB; anything near this is not ours
// Reports run 2-8 KB, and the fixed bounds in writeExceptionBacktrace (128
// modules, 64 frames) cap them near 10 KB. Kept equal to kMaxReportBytes in the
// client: anything the client is willing to send must not meet a settling 400
// here, or the upload is what destroys the report.
const MAX_BYTES = 32 * 1024;
// Every field below is lifted out of the uploaded file, and the endpoint is public
// and unauthenticated: treat all of it as attacker-chosen, not just the handle.
+4 -2
View File
@@ -37,8 +37,10 @@ void writeConsent(bool yes) {
CloseHandle(h);
}
// A report bigger than this is not one of ours; never put it on the wire.
const DWORD kMaxReportBytes = 256 * 1024;
// A report bigger than this is not one of ours; never put it on the wire. Kept
// equal to MAX_BYTES in the sink, which answers a settling 400 above it: sending
// what the sink will not take is how an upload destroys the report it carries.
const DWORD kMaxReportBytes = 32 * 1024;
// POST one report file to url. Returns the HTTP status, or 0 if the request never
// completed (no connection, DNS failure, timeout) — see reportIsSettled().