diff --git a/src/bpf/restrict-fsaccess.bpf.c b/src/bpf/restrict-fsaccess.bpf.c index 538ddf3ef1..13e11aff50 100644 --- a/src/bpf/restrict-fsaccess.bpf.c +++ b/src/bpf/restrict-fsaccess.bpf.c @@ -69,6 +69,13 @@ volatile __u32 protected_link_ids[NUM_PROTECTED_OBJS]; /* ---- Integrity tracking hooks ---- */ +/* Preferred version: reads both value and size for defense-in-depth. + * Requires kernel v6.16+ or the backport of 1271a40eeafa ("bpf: Allow + * access to const void pointer arguments in tracing programs"). + * On older kernels btf_ctx_access() rejects loads from const void * + * arguments because it fails to skip the CONST modifier when checking + * for void pointers. prepare_restrict_fsaccess_bpf() tries this version + * first and falls back to the _compat variant below if loading fails. */ SEC("lsm/bdev_setintegrity") int BPF_PROG(restrict_fsaccess_bdev_setintegrity, struct block_device *bdev, enum lsm_integrity_type type, const void *value, __u64 size) @@ -82,6 +89,24 @@ int BPF_PROG(restrict_fsaccess_bdev_setintegrity, struct block_device *bdev, return 0; } +/* Compatibility version for kernels without 1271a40eeafa: does not + * read the const void *value argument (ctx[2]) to avoid the verifier + * rejection. Reads size (ctx[3]) directly from the raw context instead. + * This is safe because dm-verity guarantees value!=NULL iff size>0. */ +#define BDEV_SETINTEGRITY_SIZE_CTX_IDX 3 /* bdev_setintegrity(bdev, type, value, size) */ +SEC("lsm/bdev_setintegrity") +int BPF_PROG(restrict_fsaccess_bdev_setintegrity_compat, struct block_device *bdev, + enum lsm_integrity_type type) +{ + if (type == LSM_INT_DMVERITY_SIG_VALID) { + __u32 dev = bdev->bd_dev; + __u8 valid = ctx[BDEV_SETINTEGRITY_SIZE_CTX_IDX] > 0; + bpf_map_update_elem(&verity_devices, &dev, &valid, BPF_ANY); + } + + return 0; +} + SEC("lsm/bdev_free_security") void BPF_PROG(restrict_fsaccess_bdev_free, struct block_device *bdev) { diff --git a/src/core/bpf-restrict-fsaccess.c b/src/core/bpf-restrict-fsaccess.c index a38665a6e2..be66f7da01 100644 --- a/src/core/bpf-restrict-fsaccess.c +++ b/src/core/bpf-restrict-fsaccess.c @@ -62,9 +62,13 @@ assert_cc(offsetof(struct restrict_fsaccess_bss, protected_map_id_verity) == assert_cc(offsetof(struct restrict_fsaccess_bss, protected_map_id_bss) == offsetof(typeof_field(struct restrict_fsaccess_bpf, bss[0]), protected_map_id_bss)); -/* Build the skeleton links array indexed by the link enum. */ +/* Build the skeleton links array indexed by the link enum. + * For BDEV_SETINTEGRITY, use whichever variant was loaded (full or compat). + * This compat logic can be removed once the kernel baseline includes + * 1271a40eeafa ("bpf: Allow access to const void pointer arguments"). */ #define RESTRICT_FSACCESS_LINKS(obj) { \ - [RESTRICT_FILESYSTEM_ACCESS_LINK_BDEV_SETINTEGRITY] = (obj)->links.restrict_fsaccess_bdev_setintegrity, \ + [RESTRICT_FILESYSTEM_ACCESS_LINK_BDEV_SETINTEGRITY] = (obj)->links.restrict_fsaccess_bdev_setintegrity ?: \ + (obj)->links.restrict_fsaccess_bdev_setintegrity_compat, \ [RESTRICT_FILESYSTEM_ACCESS_LINK_BDEV_FREE] = (obj)->links.restrict_fsaccess_bdev_free, \ [RESTRICT_FILESYSTEM_ACCESS_LINK_BPRM_CHECK] = (obj)->links.restrict_fsaccess_bprm_check, \ [RESTRICT_FILESYSTEM_ACCESS_LINK_MMAP_FILE] = (obj)->links.restrict_fsaccess_mmap_file, \ @@ -109,6 +113,11 @@ int bpf_restrict_fsaccess_prepare(struct restrict_fsaccess_bpf **ret) { assert(ret); + /* Try the preferred version first — it reads the const void *value + * argument for defense-in-depth. On kernels before v6.16 (missing + * 1271a40eeafa) the verifier rejects loads from const void * context + * arguments, so we fall back to the _compat variant that only reads + * the size argument via raw ctx access. */ obj = restrict_fsaccess_bpf__open(); if (!obj) return log_error_errno(errno, "bpf-restrict-fsaccess: Failed to open BPF object: %m"); @@ -117,10 +126,37 @@ int bpf_restrict_fsaccess_prepare(struct restrict_fsaccess_bpf **ret) { if (r < 0) return log_error_errno(r, "bpf-restrict-fsaccess: Failed to size hash table: %m"); + r = sym_bpf_program__set_autoload(obj->progs.restrict_fsaccess_bdev_setintegrity_compat, false); + if (r < 0) + return log_error_errno(r, "bpf-restrict-fsaccess: Failed to disable compat program: %m"); + + r = restrict_fsaccess_bpf__load(obj); + if (r >= 0) { + log_debug("bpf-restrict-fsaccess: Loaded with full const void * access."); + *ret = TAKE_PTR(obj); + return 0; + } + + log_debug_errno(r, "bpf-restrict-fsaccess: Full version failed to load (%m), trying compat variant."); + obj = restrict_fsaccess_bpf_free(obj); + + obj = restrict_fsaccess_bpf__open(); + if (!obj) + return log_error_errno(errno, "bpf-restrict-fsaccess: Failed to reopen BPF object: %m"); + + r = sym_bpf_map__set_max_entries(obj->maps.verity_devices, DMVERITY_DEVICES_MAX); + if (r < 0) + return log_error_errno(r, "bpf-restrict-fsaccess: Failed to size hash table: %m"); + + r = sym_bpf_program__set_autoload(obj->progs.restrict_fsaccess_bdev_setintegrity, false); + if (r < 0) + return log_error_errno(r, "bpf-restrict-fsaccess: Failed to disable full program: %m"); + r = restrict_fsaccess_bpf__load(obj); if (r < 0) - return log_error_errno(r, "bpf-restrict-fsaccess: Failed to load BPF object: %m"); + return log_error_errno(r, "bpf-restrict-fsaccess: Failed to load BPF object (compat): %m"); + log_debug("bpf-restrict-fsaccess: Loaded with compat bdev_setintegrity."); *ret = TAKE_PTR(obj); return 0; } @@ -271,6 +307,13 @@ int bpf_restrict_fsaccess_populate_guard(struct restrict_fsaccess_bpf *obj) { FOREACH_ELEMENT(link, links) { size_t idx = link - links; + /* BDEV_SETINTEGRITY slot resolves via ?: between full and compat + * variants; assert at least one was attached. */ + if (!*link) + return log_error_errno(SYNTHETIC_ERRNO(ENODATA), + "bpf-restrict-fsaccess: %s link missing after attach.", + restrict_fsaccess_link_names[idx]); + r = bpf_get_link_ids(sym_bpf_link__fd(*link), &obj->bss->protected_link_ids[idx], &obj->bss->protected_prog_ids[idx]); @@ -412,6 +455,13 @@ int bpf_restrict_fsaccess_setup(Manager *m) { FOREACH_ELEMENT(link, links) { size_t idx = link - links; + if (!*link) { + r = log_error_errno(SYNTHETIC_ERRNO(ENODATA), + "bpf-restrict-fsaccess: %s link missing after attach.", + restrict_fsaccess_link_names[idx]); + goto fail; + } + m->restrict_fsaccess_link_fds[idx] = fcntl(sym_bpf_link__fd(*link), F_DUPFD_CLOEXEC, 3); if (m->restrict_fsaccess_link_fds[idx] < 0) { r = log_error_errno(errno, "bpf-restrict-fsaccess: Failed to dup link FD for %s: %m", diff --git a/src/shared/bpf-dlopen.c b/src/shared/bpf-dlopen.c index c7d9dbdd5b..ca74682823 100644 --- a/src/shared/bpf-dlopen.c +++ b/src/shared/bpf-dlopen.c @@ -50,6 +50,7 @@ DLSYM_PROTOTYPE(bpf_program__attach) = NULL; DLSYM_PROTOTYPE(bpf_program__attach_cgroup) = NULL; DLSYM_PROTOTYPE(bpf_program__attach_lsm) = NULL; DLSYM_PROTOTYPE(bpf_program__name) = NULL; +DLSYM_PROTOTYPE(bpf_program__set_autoload) = NULL; DLSYM_PROTOTYPE(libbpf_set_print) = NULL; DLSYM_PROTOTYPE(ring_buffer__epoll_fd) = NULL; DLSYM_PROTOTYPE(ring_buffer__free) = NULL; @@ -174,6 +175,7 @@ int dlopen_bpf(int log_level) { DLSYM_ARG_FORCE(bpf_program__attach_lsm), #endif DLSYM_ARG(bpf_program__name), + DLSYM_ARG(bpf_program__set_autoload), DLSYM_ARG(libbpf_get_error), DLSYM_ARG(libbpf_set_print), DLSYM_ARG(ring_buffer__epoll_fd), diff --git a/src/shared/bpf-dlopen.h b/src/shared/bpf-dlopen.h index 71e6ca5d1d..62ea9cf707 100644 --- a/src/shared/bpf-dlopen.h +++ b/src/shared/bpf-dlopen.h @@ -39,6 +39,7 @@ extern DLSYM_PROTOTYPE(bpf_program__attach); extern DLSYM_PROTOTYPE(bpf_program__attach_cgroup); extern DLSYM_PROTOTYPE(bpf_program__attach_lsm); extern DLSYM_PROTOTYPE(bpf_program__name); +extern DLSYM_PROTOTYPE(bpf_program__set_autoload); extern DLSYM_PROTOTYPE(libbpf_set_print); extern DLSYM_PROTOTYPE(ring_buffer__epoll_fd); extern DLSYM_PROTOTYPE(ring_buffer__free);