fix(envd): cap PostInit request body at 1 MiB with MaxBytesReader - #3564
Open
AdaAibaby wants to merge 1 commit into
Open
fix(envd): cap PostInit request body at 1 MiB with MaxBytesReader#3564AdaAibaby wants to merge 1 commit into
AdaAibaby wants to merge 1 commit into
Conversation
PostInit read the request body with an unbounded io.ReadAll. Any process inside the VM that can reach the envd HTTP port could send an arbitrarily large body, allocating heap memory until envd is OOM-killed. envd is the sandbox control plane (file I/O, process management, cgroup control, live-upgrade handover); an OOM kill leaves user processes orphaned and breaks graceful sandbox teardown. The largest legitimate /init payload contains EnvVars and a CaBundle but no bulk data; 1 MiB is a generous upper bound no real orchestrator payload approaches. Oversized requests now return 413 instead of silently consuming memory. The cap also preserves the intent of memguard.WipeBytes: capping the body ensures the secret-bearing allocation is small enough to wipe reliably without racing a GC page-out on a huge heap allocation. Fixes: e2b-dev#3563
AdaAibaby
requested review from
ValentaTomas,
dobrac and
jakubno
as code owners
August 13, 2026 10:50
leonmeijer
reviewed
Aug 16, 2026
leonmeijer
left a comment
There was a problem hiding this comment.
The new 1 MiB limit rejects an envd init payload that is still valid under the public sandbox contract: spec/openapi.yml does not cap envVars, while the orchestrator serializes the complete env map and CA bundle into POST /init and treats a 413 as a fatal create or resume failure. Add an equivalent admission limit at the public API boundary or choose a bound that is already guaranteed there so a successfully admitted sandbox cannot fail only at envd initialization.
This envd behavior change also leaves packages/envd/pkg/version.go at 0.6.13. Without the required version bump, X-Envd-Version and compatibility or rollout logic cannot distinguish nodes that enforce the new body limit.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Closes #3563
PostInitread the full request body with no size cap:Any process inside the VM that can reach the envd HTTP port can send an arbitrarily large body, allocating heap memory until envd is OOM-killed. The auth check happens after the full body is read, so no credentials are required.
envd is the sandbox control plane — OOM-killing it orphans all user processes, breaks graceful teardown (slot release, cleanup callbacks), and can corrupt pause/resume state.
Secondary issue:
memguard.WipeBytes(body)is intended to scrub the access token from heap memory after use. Ifbodyis a multi-hundred-MiB allocation, Go's GC may have already paged parts of it to disk or the allocator may have moved the backing array before the deferred wipe runs, reducing the security guarantee.Fix
Wrap
r.Bodywithhttp.MaxBytesReader(w, r.Body, 1<<20)beforeio.ReadAll. 1 MiB is a generous upper bound — the largest legitimate/initpayload carriesEnvVarsandCaBundle(multiple PEM certs) but no bulk data.Oversized requests now return 413 Request Entity Too Large via a dedicated
*http.MaxBytesErrorbranch; all other read errors keep the existing 400 path.Changes
Single file,
packages/envd/internal/api/init.go(+17/-2 lines):r.Body = http.MaxBytesReader(w, r.Body, 1<<20)io.ReadAllerrors.As(err, &maxErr)branchNo import changes needed (
errors,io,net/httpalready imported).Testing
/initpayloads are well under 1 MiB — no behaviour change for legitimate callers.