From: Clayton Craft Date: Fri, 20 Mar 2026 00:08:31 +0000 (-0700) Subject: nsresourced: fix BPF loading when using kernel compiled with Clang X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=bc6380ae0765ebc6f09fc2afd27070cfb3acc0b6;p=thirdparty%2Fsystemd.git nsresourced: fix BPF loading when using kernel compiled with Clang This fixes an issue where nsresourced fails to load BPF on kernels compiled with Clang (this output was from v259): $ sudo env SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/systemd-nsresourced ; int BPF_PROG(userns_restrict_path_chown, struct path *path, void* uid, void *gid, int ret) { @ userns-restrict.bpf.c:134 ... ; return validate_path(path, ret); @ userns-restrict.bpf.c:135 ... ; static int validate_path(const struct path *path, int ret) { @ userns-restrict.bpf.c:120 ... ; task = (struct task_struct*) bpf_get_current_task_btf(); @ userns-restrict.bpf.c:84 ... ; task_userns = task->cred->user_ns; @ userns-restrict.bpf.c:85 ... R2 invalid mem access 'rcu_ptr_or_null_' When Clang is used (which sets CONFIG_PAHOLE_HAS_BTF_TAG), btf_type_tag support is enabled. As a result, an rcu type tag is added to task_struct::cred: $ bpftool btf dump file /sys/kernel/btf/vmlinux | grep "STRUCT 'task_struct'" [459] STRUCT 'task_struct' size=4672 vlen=242 $ bpftool btf dump file /sys/kernel/btf/vmlinux | grep -A200 "^\[459\] STRUCT 'task_struct'" | grep cred 'ptracer_cred' type_id=802 bits_offset=14528 'real_cred' type_id=802 bits_offset=14592 'cred' type_id=802 bits_offset=14656 $ bpftool btf dump file /sys/kernel/btf/vmlinux | grep '^\[802\]' [802] PTR '(anon)' type_id=801 $ bpftool btf dump file /sys/kernel/btf/vmlinux | grep '^\[801\]' [801] TYPE_TAG 'rcu' type_id=803 Since the struct ptr *could* be null, we have to add a null pointer check to satisfy the bpf verifier. --- diff --git a/src/nsresourced/bpf/userns-restrict/userns-restrict.bpf.c b/src/nsresourced/bpf/userns-restrict/userns-restrict.bpf.c index 25d609bf38f..d70493fe7af 100644 --- a/src/nsresourced/bpf/userns-restrict/userns-restrict.bpf.c +++ b/src/nsresourced/bpf/userns-restrict/userns-restrict.bpf.c @@ -119,6 +119,7 @@ static int userns_owns_mount(struct user_namespace *userns, struct vfsmount *v) static int validate_mount(struct vfsmount *v, int ret) { struct user_namespace *task_userns; unsigned task_userns_inode; + const struct cred *cred; struct task_struct *task; void *mnt_id_map; struct mount *m; @@ -129,7 +130,10 @@ static int validate_mount(struct vfsmount *v, int ret) { /* Get user namespace from task */ task = (struct task_struct*) bpf_get_current_task_btf(); - task_userns = task->cred->user_ns; + cred = task->cred; + if (!cred) + return -EPERM; + task_userns = cred->user_ns; /* fsuid/fsgid are the UID/GID in the initial user namespace, before any idmapped mounts have been * applied. There is no way (yet) to figure out what the UID/GID that will be written to disk will be @@ -138,7 +142,7 @@ static int validate_mount(struct vfsmount *v, int ret) { * translate the transient UID range to something else. For other UIDs/GIDs, there's no need to do * these checks as we don't insist on idmapped mounts or such for UIDs/GIDs outside the transient * ranges. */ - if (!uid_is_transient(task->cred->fsuid.val) && !uid_is_transient((uid_t) task->cred->fsgid.val)) + if (!uid_is_transient(cred->fsuid.val) && !uid_is_transient((uid_t) cred->fsgid.val)) return 0; r = userns_owns_mount(task_userns, v); @@ -170,6 +174,7 @@ SEC("lsm/path_chown") int BPF_PROG(userns_restrict_path_chown, struct path *path, unsigned long long uid, unsigned long long gid, int ret) { struct user_namespace *task_userns; unsigned task_userns_inode; + const struct cred *cred; struct task_struct *task; struct vfsmount *v; void *mnt_id_map; @@ -180,7 +185,10 @@ int BPF_PROG(userns_restrict_path_chown, struct path *path, unsigned long long u /* Get user namespace from task */ task = (struct task_struct*) bpf_get_current_task_btf(); - task_userns = task->cred->user_ns; + cred = task->cred; + if (!cred) + return -EPERM; + task_userns = cred->user_ns; v = path->mnt; r = userns_owns_mount(task_userns, v);