Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bpf: Mark dentry->d_inode as trusted_or_null

LSM hooks such as security_path_mknod() and security_inode_rename() have
access to newly allocated negative dentry, which has NULL d_inode.
Therefore, it is necessary to do the NULL pointer check for d_inode.

Also add selftests that checks the verifier enforces the NULL pointer
check.

Signed-off-by: Song Liu <song@kernel.org>
Reviewed-by: Matt Bobrowski <mattbobrowski@google.com>
Link: https://lore.kernel.org/r/20250613052857.1992233-1-song@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Song Liu and committed by
Alexei Starovoitov
a766cfbb 9afe6529

+35 -3
+2 -3
kernel/bpf/verifier.c
··· 7027 7027 struct inode *f_inode; 7028 7028 }; 7029 7029 7030 - BTF_TYPE_SAFE_TRUSTED(struct dentry) { 7031 - /* no negative dentry-s in places where bpf can see it */ 7030 + BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct dentry) { 7032 7031 struct inode *d_inode; 7033 7032 }; 7034 7033 ··· 7065 7066 BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct bpf_iter__task)); 7066 7067 BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct linux_binprm)); 7067 7068 BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct file)); 7068 - BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct dentry)); 7069 7069 7070 7070 return btf_nested_type_is_trusted(&env->log, reg, field_name, btf_id, "__safe_trusted"); 7071 7071 } ··· 7074 7076 const char *field_name, u32 btf_id) 7075 7077 { 7076 7078 BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct socket)); 7079 + BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct dentry)); 7077 7080 7078 7081 return btf_nested_type_is_trusted(&env->log, reg, field_name, btf_id, 7079 7082 "__safe_trusted_or_null");
+18
tools/testing/selftests/bpf/progs/verifier_vfs_accept.c
··· 2 2 /* Copyright (c) 2024 Google LLC. */ 3 3 4 4 #include <vmlinux.h> 5 + #include <errno.h> 5 6 #include <bpf/bpf_helpers.h> 6 7 #include <bpf/bpf_tracing.h> 7 8 ··· 80 79 path = &file->f_path; 81 80 ret = bpf_path_d_path(path, buf, sizeof(buf)); 82 81 __sink(ret); 82 + return 0; 83 + } 84 + 85 + SEC("lsm.s/inode_rename") 86 + __success 87 + int BPF_PROG(inode_rename, struct inode *old_dir, struct dentry *old_dentry, 88 + struct inode *new_dir, struct dentry *new_dentry, 89 + unsigned int flags) 90 + { 91 + struct inode *inode = new_dentry->d_inode; 92 + ino_t ino; 93 + 94 + if (!inode) 95 + return 0; 96 + ino = inode->i_ino; 97 + if (ino == 0) 98 + return -EACCES; 83 99 return 0; 84 100 } 85 101
+15
tools/testing/selftests/bpf/progs/verifier_vfs_reject.c
··· 2 2 /* Copyright (c) 2024 Google LLC. */ 3 3 4 4 #include <vmlinux.h> 5 + #include <errno.h> 5 6 #include <bpf/bpf_helpers.h> 6 7 #include <bpf/bpf_tracing.h> 7 8 #include <linux/limits.h> ··· 159 158 return 0; 160 159 } 161 160 161 + SEC("lsm.s/inode_rename") 162 + __failure __msg("invalid mem access 'trusted_ptr_or_null_'") 163 + int BPF_PROG(inode_rename, struct inode *old_dir, struct dentry *old_dentry, 164 + struct inode *new_dir, struct dentry *new_dentry, 165 + unsigned int flags) 166 + { 167 + struct inode *inode = new_dentry->d_inode; 168 + ino_t ino; 169 + 170 + ino = inode->i_ino; 171 + if (ino == 0) 172 + return -EACCES; 173 + return 0; 174 + } 162 175 char _license[] SEC("license") = "GPL";