Commit Graph
9 Commits
Author SHA1 Message Date
Daan De Meyer bdbcff6530 Various dlopen/linking cleanups from #42100 (#42166)
- **bpf-util: rename from bpf-dlopen, unify version-specific symbol
handling**
- **cryptsetup: dlopen libcryptsetup in tokens**
- **tree-wide: dlopen libpam in pam plugins**
- **test-bus-marshal: dlopen() glib and libdbus instead of linking
directly**
- **lock-util: Simplify timeout for lock_generic_with_timeout()**
- **color-util: simplify hsv_to_rgb, fix rgb_to_hsv negative-hue wrap**
- **tree-wide: Use our own macros instead of fabs()/fmax()/fmin()**
- **locale-util: dlopen() libintl instead of linking against it**
- **home: Use log2u64() over log2()**
- **meson: drop libdl, threads, and librt dependencies**
- **libc: Use dlsym() from a constructor instead of weak symbols**
- **libc: Make sure C23 versions of strtol(), sscanf() are not used**
2026-05-19 08:54:27 +02:00
Daan De Meyer 7d822ca8af bpf-util: rename from bpf-dlopen, unify version-specific symbol handling
Renames src/shared/bpf-dlopen.{c,h} to src/shared/bpf-util.{c,h} and
folds the former src/shared/bpf-compat.h (struct forward decl and
compat_bpf_map_create() helper) into the new header.

Aligns dlopen_bpf() with the standard wrapper pattern: drops the
manual dlopen_safe()/dlsym_many_or_warn()/TAKE_PTR(dl) plumbing and
the bespoke 'cached' int in favor of dlopen_many_sym_or_warn() inside
a FOREACH_STRING() soname-fallback loop.

Unifies declaration of the version-specific symbols (bpf_create_map,
bpf_map_create, bpf_object__next_map, bpf_token_create) into a single
DISABLE_WARNING_REDUNDANT_DECLS block in the header, and alphabetically
merges the DLSYM_PROTOTYPE list. DLSYM_OPTIONAL is used to load each
one — call sites already handle NULL (compat_bpf_map_create() and the
sym_bpf_object__next_map guard in userns-restrict.c). bpf_token_create
additionally defaults to a missing_bpf_token_create() stub returning
-ENOSYS, so callers can branch on the errno instead of NULL-checking
the pointer.

Updates test-bpf-token to match: drops the compile-time
LIBBPF_MAJOR_VERSION ≥ 1.5 gate and the direct <bpf/bpf.h> include in
favor of dlopen_bpf() + sym_bpf_token_create(), and treats -ENOSYS as
the test-skip path (covering both 'libbpf too old' and 'kernel lacks
BPF_TOKEN_CREATE support').
2026-05-18 21:17:38 +00:00
Yu Watanabe 94f807be03 merge-bpf-compdb: apply "ruff format" 2026-05-18 02:47:09 +09:00
Christian Brauner 4684df8f97 core: work around btf_ctx_access() rejection of const void * in BPF LSM
Kernels before v6.16 (missing commit 1271a40eeafa "bpf: Allow access to
const void pointer arguments in tracing programs") have a bug in
btf_ctx_access() where const void * parameters in LSM hook signatures
are not recognized as void pointers. The function checks t->type == 0
to detect void *, but for const void * the BTF chain is PTR -> CONST ->
void, so t->type points to the CONST node rather than directly to
type_id 0. This causes the verifier to reject any BPF program that
reads the const void *value argument of bdev_setintegrity:

  func 'bpf_lsm_bdev_setintegrity' arg2 type UNKNOWN is not a struct
  invalid bpf_context access off=16 size=8

Work around this by providing a compat variant of the
bdev_setintegrity BPF program that avoids reading the const void *value
argument entirely. Instead it reads the size argument (a scalar integer)
directly from the raw BPF context (ctx[3]), which is not subject to the
broken type check. This is safe because dm-verity guarantees that value
and size are always in lockstep: both NULL/0 for unsigned devices, both
non-zero for signed devices.

The loader tries the full version first (which reads both value and size
for defense-in-depth) and falls back to the compat variant if loading
fails. bpf_program__set_autoload(false) disables whichever variant is
not needed so the verifier never sees it.

This compat logic can be removed once the minimum kernel baseline
includes the 1271a40eeafa fix.

Signed-off-by: Christian Brauner <brauner@kernel.org>
2026-05-13 10:36:13 +02:00
Christian Brauner 51e88de7f8 core: add self-protection guard for RestrictFileSystemAccess= BPF LSM
Add self-protection guard programs to the RestrictFileSystemAccess= skeleton that
prevent non-PID1 processes from obtaining FDs to our maps, programs, or
links via the bpf() syscall.

This blocks the primary attack vector against the RestrictFileSystemAccess= policy:
using BPF_MAP_GET_FD_BY_ID to get an FD to the verity_devices map,
then BPF_MAP_UPDATE_ELEM to inject fake trusted devices. Protection of
program and link IDs is defense-in-depth (the kernel already blocks
BPF_LINK_UPDATE and BPF_LINK_DETACH for LSM tracing links).

Additionally, a ptrace guard (lsm/ptrace_access_check) blocks
PTRACE_MODE_ATTACH to PID1 from other processes, preventing
extraction of sensitive state from PID1's address space via
ptrace, /proc/1/mem, process_vm_readv(), or pidfd_getfd().

Guard logic:
1. Allow all BPF ops from PID1 (tgid == 1, unspoofable)
2. Deny BPF_MAP_GET_FD_BY_ID for our protected map IDs
3. Deny BPF_PROG_GET_FD_BY_ID for our program IDs
4. Deny BPF_LINK_GET_FD_BY_ID for our link IDs
5. Allow everything else (zero collateral damage)

The guard starts inactive (all protected IDs default to 0 in .bss).
After skeleton attach, PID1 queries kernel-assigned IDs via
bpf_obj_get_info_by_fd() and writes them into the guard globals via
the mmap'd .bss, then extracts owned FDs and destroys the skeleton.
Destroying the skeleton unmaps the .bss page from PID1's address
space, so no BPF state — guard globals, protected map/prog/link IDs,
initramfs_s_dev — remains readable via /proc/1/mem. The kernel map
data persists (held by the dup'd FDs) but is only accessible via
bpf_map_* syscalls, which the guard itself blocks for non-PID1.

Signed-off-by: Christian Brauner <brauner@kernel.org>
2026-05-13 10:36:12 +02:00
Christian Brauner 68fe7fa4d6 core: add RestrictFileSystemAccess= BPF LSM for dm-verity execution enforcement
Add a new RestrictFileSystemAccess= boolean setting in the [Manager] section of
system.conf that enforces execution only from signed dm-verity block
devices and the initramfs during early boot.

When RestrictFileSystemAccess=yes is set, PID1 loads a BPF LSM program early in boot
that:

Integrity tracking (self-populating, no userspace involvement):
 - bdev_setintegrity: records dm-verity signature status in a BPF hash
   map when the kernel signals device integrity via
   security_bdev_setintegrity()
 - bdev_free_security: removes devices from the map on teardown

Execution enforcement (deny-default policy):
 - bprm_check_security: blocks execve() from untrusted sources
 - mmap_file: blocks PROT_EXEC mmap (shared libs, anonymous exec memory)
 - file_mprotect: blocks W->X transitions (JIT, libffi, etc.)

Trust anchors:
 - Signed dm-verity volumes (sig_valid flag in the BPF map)
 - Initramfs (s_dev captured at load time, cleared after switch_root)
 - Everything else is denied (tmpfs, procfs, sysfs, anonymous PROT_EXEC)

PID1 requires dm-verity require_signatures=1 to be enabled and refuses
to load the BPF program otherwise, ensuring the kernel enforces that all
dm-verity devices carry valid signatures.

After attach, PID1 extracts owned FDs from the skeleton (link FDs +
.bss map FD) and lets the skeleton be destroyed. The dup'd link FDs
keep programs attached via the kernel reference chain (link FD ->
bpf_link -> bpf_prog -> bpf_map). Destroying the skeleton unmaps the
.bss page from PID1's address space so no BPF state is readable via
/proc/1/mem. The .bss map FD is retained for targeted writes (clearing
initramfs_s_dev after switch_root via mmap).

Signed-off-by: Christian Brauner <brauner@kernel.org>
2026-05-13 10:36:12 +02:00
Daan De Meyer e9afaaeaac bpf: suppress false-positive clang-tidy/clangd diagnostics under src/bpf
clang-tidy's misc-use-internal-linkage fires on BPF map declarations
(they have the SEC(".maps") attribute and must retain external linkage
so bpftool gen skeleton can resolve them as ELF symbols), and its
misc-include-cleaner flags errno.h as unused even where a /* IWYU
pragma: keep */ is present. clangd's own unused-includes analysis
emits the equivalent diagnostic independently of clang-tidy.

Add src/bpf/.clang-tidy and src/bpf/.clangd that inherit the parent
configs and scope these suppressions to BPF sources only.
2026-04-22 16:49:36 +02:00
Daan De Meyer b779c52d4c bpf: register compile_commands.json entries for bpf programs
compile_commands.json is generated by ninja from c_COMPILER rules, so
BPF programs (built via custom_target and thus emitted as CUSTOM_COMMAND
rules) never show up there. Clangd consequently falls back to
heuristics when opening .bpf.c files, with poor diagnostic fidelity.

Register a meson postconf script per BPF program that upserts an entry
into compile_commands.json using the same argv meson constructed for
the custom_target. The script runs after meson has written the DB,
substitutes @INPUT@/@OUTPUT@, and keys entries by source path so
repeated reconfigures don't accumulate duplicates.
2026-04-22 16:49:36 +02:00
Daan De Meyer e6fc73350f bpf: move all programs into src/bpf/ and consolidate meson logic
The six .bpf.c files and their shared .bpf.h headers now live directly
under src/bpf/, rather than scattered across src/core/bpf/<prog>/,
src/network/bpf/<prog>/ and src/nsresourced/bpf/<prog>/.

All BPF compilation logic — the BPF_FRAMEWORK determination (clang/gcc/
bpftool/llvm-strip lookups), flag and command construction, vmlinux.h
handling, the bpf_programs list and the loop that builds the unstripped
object, the stripped object and the skeleton header for each program —
moves into src/bpf/meson.build. The top-level meson.build only keeps
option handling and the libbpf dependency. subdir('src/bpf') is pulled
up before config.h generation so that BPF_FRAMEWORK, HAVE_VMLINUX_H and
ENABLE_SYSCTL_BPF land in conf in time.

Skeleton wrapper headers (<prog>-skel.h) are now emitted by a
configure_file() template (src/bpf/bpf-skel-wrapper.h.in) at meson
setup, replacing the previously checked-in shims of the same name.

Consumer #include paths are flattened:
"bpf/<prog>/<prog>-skel.h" becomes "<prog>-skel.h",
"bpf/<prog>/<prog>-api.bpf.h" becomes "<prog>-api.bpf.h". src/bpf is
added to includes so the shared BPF headers resolve.

sysctl-write-event.h, now shared between userspace (networkd-sysctl.c)
and BPF (sysctl-monitor.bpf.c) from a single location, gains guarded
includes so pid_t and uint64_t resolve on both sides: vmlinux.h in the
BPF case (selected via __bpf__), stdint.h + sys/types.h otherwise.
2026-04-22 16:49:36 +02:00