Skip to content

fix(envd): detect cgroup v2 filesystem before initializing Cgroup2Manager#2263

Merged
arkamar merged 9 commits intoe2b-dev:mainfrom
DL1231:cgroup-fix
Apr 2, 2026
Merged

fix(envd): detect cgroup v2 filesystem before initializing Cgroup2Manager#2263
arkamar merged 9 commits intoe2b-dev:mainfrom
DL1231:cgroup-fix

Conversation

@DL1231
Copy link
Copy Markdown
Contributor

@DL1231 DL1231 commented Mar 30, 2026

Summary

On cgroup v1 hosts, Cgroup2Manager silently "succeeds" initialization because /sys/fs/cgroup is a tmpfs where mkdir and writeFile both work without error. The resulting file descriptors point to regular directories, not cgroup v2 entries.
When Go's clone3 syscall uses these fds with CLONE_INTO_CGROUP, the kernel returns EBADF, causing all fork/exec operations to fail — including process start and socat port forwarding.

This PR adds a statfs check for CGROUP2_SUPER_MAGIC (0x63677270) before creating cgroup directories, so the manager correctly returns an error and falls back to NoopManager on cgroup v1 systems.

Root Cause

  1. /sys/fs/cgroup on cgroup v1 is a tmpfs, not cgroup2fs
  2. os.MkdirAll("/sys/fs/cgroup/ptys") succeeds (creates a regular directory)
  3. os.WriteFile("cpu.weight", "200") succeeds (creates a regular file)
  4. unix.Open() returns a valid fd — but to a regular directory
  5. UseCgroupFD: true is set, Go uses clone3(CLONE_INTO_CGROUP)
  6. Kernel validates the fd is not a cgroup v2 fd → EBADF

Symptoms

fork/exec /bin/bash: bad file descriptor
fork/exec /usr/bin/socat: bad file descriptor

All child process creation fails; filesystem RPCs (which don't fork) work fine.

On cgroup v1 systems, /sys/fs/cgroup is a tmpfs where mkdir and
writeFile succeed silently, causing Cgroup2Manager to initialize
with fds pointing to regular directories instead of cgroup v2
entries. The kernel then rejects clone3(CLONE_INTO_CGROUP) with
EBADF, breaking all fork/exec operations (process start, socat).

Add a statfs check for CGROUP2_SUPER_MAGIC before proceeding,
so the manager correctly falls back to NoopManager on cgroup v1.
Copy link
Copy Markdown

@claude claude bot left a comment

Choose a reason for hiding this comment

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

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b24d02ead6

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@jakubno jakubno assigned arkamar and unassigned ValentaTomas Mar 30, 2026
@jakubno jakubno requested review from arkamar and removed request for ValentaTomas, dobrac and jakubno March 30, 2026 18:29
DL1231 added 3 commits March 31, 2026 08:46
When rootPath points to a not-yet-created subdirectory (e.g.
/sys/fs/cgroup/envd), Statfs returns ENOENT causing a silent
fallback to NoopManager. Walk up to the nearest existing ancestor
before calling Statfs so custom --cgroup-root paths are validated
correctly against the underlying filesystem type.
- Replace magic number 0x63677270 with named const cgroup2SuperMagic
- Add filepath.Clean before ancestor walk for symlink safety
- Add TestNewCgroup2Manager_NonCgroup2FS to verify non-cgroup2 rejection
Comment on lines +62 to +72
checkPath := filepath.Clean(config.rootPath)
for {
if _, err := os.Stat(checkPath); err == nil {
break
}
parent := filepath.Dir(checkPath)
if parent == checkPath {
break
}
checkPath = parent
}
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.

If the cgroup root path doesn't exist, this walks up to a parent that could be a different filesystem entirely (sysfs, rootfs), making the statfs check meaningless. The cgroup root should exist, and NewCgroup2Manager() should just error if it doesn't.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the review, fixed in 162bbda49

DL1231 and others added 2 commits March 31, 2026 19:15
The walk-up loop could traverse past the cgroup mount into a different
filesystem (sysfs, rootfs), making the statfs check meaningless.

The rootPath is the cgroup mount point itself (default /sys/fs/cgroup)
and must exist. Sub-cgroup directories are created later by MkdirAll
in createCgroup, so there is no need to walk up to an ancestor.

If the cgroup root doesn't exist, error out immediately.
Co-authored-by: Petr Vaněk <arkamar@atlas.cz>
@DL1231 DL1231 requested a review from arkamar April 1, 2026 07:59
Copy link
Copy Markdown
Contributor

@arkamar arkamar left a comment

Choose a reason for hiding this comment

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

Thanks!

@arkamar arkamar enabled auto-merge (squash) April 2, 2026 07:41
@arkamar arkamar merged commit 4520979 into e2b-dev:main Apr 2, 2026
30 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.

3 participants