Skip to content

fix: FeatureBPFFS / FeatureTraceFS verify filesystem type#32

Merged
leodido merged 1 commit into
mainfrom
fix/fs-mount-gates
Apr 23, 2026
Merged

fix: FeatureBPFFS / FeatureTraceFS verify filesystem type#32
leodido merged 1 commit into
mainfrom
fix/fs-mount-gates

Conversation

@leodido
Copy link
Copy Markdown
Owner

@leodido leodido commented Apr 23, 2026

Motivation

The probe behind FeatureBPFFS and FeatureTraceFS used os.Stat + IsDir, returning Supported=true on any host where the directory existed. On modern systemd distros /sys/fs/bpf and /sys/kernel/tracing exist by default whether or not the corresponding pseudo-filesystem is actually mounted, so callers gating on these features (e.g. before bpf_obj_pin) silently got a false positive and failed at I/O time.

A user that does:

if err := kfeatures.Check(kfeatures.FeatureBPFFS); err != nil {
    return err
}
// ... pin maps on /sys/fs/bpf

would pass the check on a typical systemd box where /sys/fs/bpf exists as a directory but bpffs is not mounted, then fail at bpf_obj_pin() with a confusing errno.

Change

Switch the gated checks to Statfs + superblock magic comparison:

  • FeatureBPFFS now requires unix.BPF_FS_MAGIC at /sys/fs/bpf.
  • FeatureTraceFS now requires unix.TRACEFS_MAGIC at /sys/kernel/tracing or its legacy fallback /sys/kernel/debug/tracing.

Single internal checkMount(path, magic) helper backs both. An injectable package-level statfs function variable lets unit tests exercise every branch (matching magic, mismatch, missing path, syscall errno) without privileged operations.

The diagnostic-only fields SystemFeatures.DebugFS and .SecurityFS keep their previous presence-only semantics — no gated Feature* exists for them today, so directory-presence is sufficient.

Notes

  • Pre-1.0 (v0.3.x), the previous behavior was a bug; correct callers see no change. CHANGELOG entry added under [Unreleased].
  • Statfs_t.Type is signed on some architectures; comparison casts to uint32.
  • This PR is part 1 of 2. A follow-up will add a parameterized RequireMount(path, magic) requirement on top of the same checkMount helper, plus integration tests against a real tmpfs mount in a new CI job.

Verification

  • make build && make test green (-race -count=1).
  • go vet ./... clean.
  • New tests in caps_mount_test.go cover checkMount, probeFilesystemMounted, and probeFilesystemMountedAny against a fake statfs for: matching magic, magic mismatch, missing path, syscall errno wrapping, primary/fallback path selection.

@leodido leodido changed the title fix: FeatureBPFFS / FeatureTraceFS verify filesystem type fix: FeatureBPFFS / FeatureTraceFS verify filesystem type Apr 23, 2026
@leodido leodido self-assigned this Apr 23, 2026
The probe behind FeatureBPFFS and FeatureTraceFS used os.Stat + IsDir,
returning Supported=true on any host where the directory existed. On
modern systemd distros /sys/fs/bpf and /sys/kernel/tracing exist by
default whether or not bpffs/tracefs is actually mounted, so callers
gating on these features (e.g. before bpf_obj_pin) silently got a
false positive and failed at I/O time.

Switch the probe to Statfs + superblock magic comparison
(BPF_FS_MAGIC, TRACEFS_MAGIC). Introduce a single internal checkMount
helper with an injectable statfs implementation so unit tests can
exercise all branches without privileged operations.

Diagnostic-only fields SystemFeatures.DebugFS and .SecurityFS keep
their previous presence-only semantics (no gated Feature* exists for
them today).
@leodido leodido force-pushed the fix/fs-mount-gates branch from 237ed5f to a06216e Compare April 23, 2026 17:08
@leodido leodido merged commit 80c265b into main Apr 23, 2026
5 checks passed
@leodido leodido deleted the fix/fs-mount-gates branch April 23, 2026 17:11
leodido added a commit that referenced this pull request Apr 23, 2026
Introduces MountRequirement and RequireMount(path, magic) for gating on
arbitrary filesystem mounts (path + superblock magic). Fills the gap
where FeatureBPFFS / FeatureTraceFS are too restrictive: bpffs at a
non-default path, cgroupv2, debugfs, tmpfs in tests, etc.

Backed by the same internal checkMount helper introduced for the bug
fix in #32, so there is a single Statfs implementation for both the
preset features and the parameterized requirement.

Tests cover three layers:

  Layer 1 (always-on, unprivileged): unit tests with the injected
  statfs fake exercise matching magic, mismatch, missing path, errno
  wrapping, dedup of identical {path, magic} pairs, and distinct pairs.

  Layer 2 (Linux integration job, root): a //go:build linux &&
  integration test mounts a real tmpfs in t.TempDir() and asserts
  RequireMount agrees with the kernel's actual f_type. Skips politely
  when not root or sandboxed.

  Layer 3 (Linux integration job, root): a bats suite cross-checks
  CLI exit codes for 'check --require bpf-fs' / 'trace-fs' against
  the runner's real mount state via stat -f.

A new 'integration' job in ci.yml runs the Layer 2 + Layer 3 tests
on ubuntu-latest with sudo (runneradmin has CAP_SYS_ADMIN). The
existing 'test' job is unchanged - the integration build tag is
opt-in.
leodido added a commit that referenced this pull request May 3, 2026
Introduces MountRequirement and RequireMount(path, magic) for gating on
arbitrary filesystem mounts (path + superblock magic). Fills the gap
where FeatureBPFFS / FeatureTraceFS are too restrictive: bpffs at a
non-default path, cgroupv2, debugfs, tmpfs in tests, etc.

Backed by the same internal checkMount helper introduced for the bug
fix in #32, so there is a single Statfs implementation for both the
preset features and the parameterized requirement.

Tests cover three layers:

  Layer 1 (always-on, unprivileged): unit tests with the injected
  statfs fake exercise matching magic, mismatch, missing path, errno
  wrapping, dedup of identical {path, magic} pairs, and distinct pairs.

  Layer 2 (Linux integration job, root): a //go:build linux &&
  integration test mounts a real tmpfs in t.TempDir() and asserts
  RequireMount agrees with the kernel's actual f_type. Skips politely
  when not root or sandboxed.

  Layer 3 (Linux integration job, root): a bats suite cross-checks
  CLI exit codes for 'check --require bpf-fs' / 'trace-fs' against
  the runner's real mount state via stat -f.

A new 'integration' job in ci.yml runs the Layer 2 + Layer 3 tests
on ubuntu-latest with sudo (runneradmin has CAP_SYS_ADMIN). The
existing 'test' job is unchanged - the integration build tag is
opt-in.
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