Skip to content

fix: validate FileSize in NewDataBuilder to prevent OOM DoS#25710

Merged
f0ssel merged 2 commits into
mainfrom
plat-231/fix-oom-dos-filesize-validation
May 27, 2026
Merged

fix: validate FileSize in NewDataBuilder to prevent OOM DoS#25710
f0ssel merged 2 commits into
mainfrom
plat-231/fix-oom-dos-filesize-validation

Conversation

@f0ssel

@f0ssel f0ssel commented May 27, 2026

Copy link
Copy Markdown
Member

NewDataBuilder allocated make([]byte, 0, req.FileSize) using the client-supplied int64 with no upper-bound check. The DRPC 4 MiB wire cap limits message size but not the integer value, so a crafted message with FileSize = 1<<40 forces a 1 TiB allocation, triggering an unrecoverable runtime.throw that kills the entire coderd process.

Add a MaxFileSize constant (100 MiB, matching HTTPFileMaxBytes in coderd/files.go) and reject negative or oversized FileSize, plus negative or excessive Chunks, before the allocation. BytesToDataUpload also returns an error for oversized data to preserve the encode/decode round-trip contract. Fix a pre-existing reversed subtraction in the Add() overflow error message.

Closes https://linear.app/codercom/issue/PLAT-231

Implementation details
  • provisionersdk/proto/dataupload.go: New exported MaxFileSize constant; validation in NewDataBuilder and BytesToDataUpload. Fixed reversed subtraction in Add() error.
  • provisionersdk/proto/dataupload_test.go: New TestNewDataBuilderValidation with 7 subtests.
  • Updated all 5 callers of BytesToDataUpload for new error return.
  • Audited all make([]byte, ...) in provisioner paths; no other client-supplied sizes.

Generated by Coder Agents on behalf of @f0ssel

…vent OOM DoS

NewDataBuilder allocated make([]byte, 0, req.FileSize) using the
client-supplied int64 with no upper bound. A crafted DRPC message
with FileSize=1<<40 forces a 1 TiB allocation, triggering an
unrecoverable runtime.throw that kills coderd.

Add MaxFileSize (100 MiB) constant and reject negative or oversized
FileSize and negative Chunks before the allocation.
@f0ssel f0ssel marked this pull request as ready for review May 27, 2026 00:51
@f0ssel

f0ssel commented May 27, 2026

Copy link
Copy Markdown
Member Author

/coder-agents-review

@coder-agents-review

coder-agents-review Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

Review posted | Chat
Requested: 2026-05-27 00:53 UTC by @f0ssel
Spend: $17.61 / $100.00

Review history
  • R1 (2026-05-27): 15 reviewers, 2 Nit, 2 P3, 2 P4, COMMENT. Review

deep-review v0.5.0 | Round 1 | 20b50dd..cc2d2a8

Last posted: Round 1, 6 findings (2 P3, 2 P4, 2 Nit), COMMENT. Review

Finding inventory

Findings

# Sev Status Location Summary Round Reviewer Posted
CRF-1 P3 Open dataupload_test.go:19 Missing boundary test at exactly MaxFileSize R1 Bisky P3, Chopper Nit Yes
CRF-2 P3 Open dataupload.go:36 BytesToDataUpload/NewDataBuilder round-trip contract broken; fuzz test invariant violated for >MaxFileSize inputs R1 Hisoka P3, Mafuuu P3 Yes
CRF-3 P4 Open dataupload.go:39 Chunks validated for negativity but not upper-bounded R1 Hisoka P3, Kite P4, Meruem P3, Knov P3, Ryosuke Note Yes
CRF-4 P4 Open dataupload.go:74 Pre-existing: Add() error message reversed subtraction produces negative "bytes over" value R1 Chopper Yes
CRF-5 Nit Open dataupload.go:37 Error message uses raw byte counts instead of human-readable sizes R1 Leorio Yes
CRF-6 Nit Open dataupload_test.go:67 crypto/rand.Read error check is dead code since Go 1.24 R1 Ging-Go Yes
CRF-7 Note Dropped by orchestrator (observation, no action needed) dataupload.go:13 MaxFileSize/HTTPFileMaxBytes identical expressions with comment-only coupling R1 Chopper, Leorio, Mafuuu, Meruem, Knov No
CRF-8 Note Dropped by orchestrator (supplemental test suggestion) dataupload_test.go:52 ZeroFileSize could call Complete() for full lifecycle verification R1 Bisky No
CRF-9 Note Dropped by orchestrator (pre-existing observation) dataupload.go:72 MaxFileSize cap implicitly fixes int(b.Size) truncation risk on 32-bit R1 Knov No

Contested and acknowledged

(none)

Round log

Round 1

Panel. 2 P3, 2 P4, 2 Nit new. 3 Notes dropped. Reviewed against 20b50dd..cc2d2a8.

About deep-review

CRF = Coder Review Finding (P0-P4, Nit, Note)

Reviewer Focus
Bisky tests
Chopper ops/errors
Churn-guard change verification
Ging language modernization
Gon naming
Hisoka edge cases
Killua perf
Kite change integrity
Knov contracts
Knuckle SQL
Kurapika security
Leorio docs
Luffy product
Mafu-san process
Mafuuu contracts
Melody dispatch/pairing
Meruem structural
Nami frontend
Netero mechanical checks
Pariston premise testing
Pen-botter product gaps
Razor verification
Robin duplication
Ryosuke Go arch
Takumi concurrency
Zoro shape

🤖 Managed by Coder Agents.

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well-placed fix. The vulnerability diagnosis is accurate: an untrusted int64 reaches make([]byte, 0, req.FileSize) and the DRPC wire cap does not bound the integer value. The constant matches HTTPFileMaxBytes by expression, the validation is in the single constructor that gates both callers, and the sibling make audit is confirmed. Pariston ran four alternative framings and couldn't build a case against the approach. Commit message and PR description are strong.

Severity count: 2 P3, 2 P4, 2 Nit.

The two P3s are the boundary test gap at exactly MaxFileSize (off-by-one regression risk) and the broken round-trip contract between BytesToDataUpload and NewDataBuilder for inputs exceeding the new limit. The P4s are the missing Chunks upper bound (5 reviewers flagged this, practical impact bounded by DRPC transport) and a pre-existing reversed subtraction in the Add() error message.

"The contract is broken: 'encode then decode' is no longer a guaranteed identity for all valid Go []byte inputs." (Mafuuu)


provisionersdk/proto/dataupload.go:74

P4 [CRF-4] Pre-existing: when expectedSize > b.Size, the computation b.Size - int64(expectedSize) yields a negative number. The error message reports "data is now 110 bytes, -10 bytes over the limit of 100" instead of "10 bytes over the limit." Should be int64(expectedSize) - b.Size.

Not introduced by this PR, but same file and same error handling path.

Chopper: "An operator receiving 'data is now 110 bytes, -10 bytes over the limit of 100' has to mentally invert the sign."

(Chopper P4)

🤖

🤖 This review was automatically generated with Coder Agents.

Comment thread provisionersdk/proto/dataupload_test.go
Comment thread provisionersdk/proto/dataupload.go
Comment thread provisionersdk/proto/dataupload.go
Comment thread provisionersdk/proto/dataupload.go
Comment thread provisionersdk/proto/dataupload_test.go Outdated
CRF-1 (P3): Add ExactMaxFileSize boundary test at exactly MaxFileSize.
CRF-2 (P3): Add error return to BytesToDataUpload so the encode/decode
  round-trip contract holds; data exceeding MaxFileSize is rejected at
  the sender. Update all 5 callers.
CRF-3 (P4): Add Chunks upper-bound validation; chunk count must be
  consistent with FileSize and ChunkSize.
CRF-4 (P4): Fix pre-existing reversed subtraction in Add() error
  message (b.Size-int64(expectedSize) to int64(expectedSize)-b.Size).
@f0ssel f0ssel changed the title fix(provisionersdk/proto): validate FileSize in NewDataBuilder to prevent OOM DoS fix: validate FileSize in NewDataBuilder to prevent OOM DoS May 27, 2026
@f0ssel f0ssel merged commit a2e1ddb into main May 27, 2026
29 of 30 checks passed
@f0ssel f0ssel deleted the plat-231/fix-oom-dos-filesize-validation branch May 27, 2026 18:30
@github-actions github-actions Bot locked and limited conversation to collaborators May 27, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants