Re: [PATCH bpf-next v5 2/2] bpf: expose bpf_d_path helper to vfs_* and security_* functions
From: Andrii Nakryiko <hidden>
Date: 2021-08-04 22:44:37
On Wed, Aug 4, 2021 at 3:35 PM Daniel Borkmann [off-list ref] wrote:
On 7/27/21 3:25 PM, Hengqi Chen wrote:quoted
Add vfs_* and security_* to bpf_d_path allowlist, so that we can use bpf_d_path helper to extract full file path from these functions' arguments. This will help tools like BCC's filetop[1]/filelife to get full file path. [1] https://github.com/iovisor/bcc/issues/3527 Acked-by: Yonghong Song <redacted> Signed-off-by: Hengqi Chen <hengqi.chen@gmail.com> --- kernel/trace/bpf_trace.c | 60 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 4 deletions(-)diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index c5e0b6a64091..e7b24abcf3bf 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c@@ -849,18 +849,70 @@ BPF_CALL_3(bpf_d_path, struct path *, path, char *, buf, u32, sz) BTF_SET_START(btf_allowlist_d_path) #ifdef CONFIG_SECURITY +BTF_ID(func, security_bprm_check) +BTF_ID(func, security_bprm_committed_creds) +BTF_ID(func, security_bprm_committing_creds) +BTF_ID(func, security_bprm_creds_for_exec) +BTF_ID(func, security_bprm_creds_from_file) +BTF_ID(func, security_file_alloc)Did you actually try these out, e.g. attaching BPF progs invoking bpf_d_path() to all these, then generate some workload like kernel build for testing? I presume not, since something like security_file_alloc() would crash the kernel. Right before it's called in __alloc_file() we fetch a struct file from kmemcache, and only populate f->f_cred there. Most LSMs, for example, only populate their secblob through the callback. If you call bpf_d_path(&file->f_path, ...) with it, you'll crash in d_path() when path->dentry->d_op is checked.. given f->f_path is all zeroed structure at that point. Please do your due diligence and invest each of them manually, maybe the best way is to hack up small selftests for each enabled function that our CI can test run? Bit of a one-time effort, but at least it ensures that those additions are sane & checked.
I think it's actually a pretty fun exercise and a good selftest to have. We can have a selftest which will attach a simple BPF program just to grab a contents of btf_allowlist_d_path (with typeless ksyms, for instance). Then for each BTF ID in there, as a subtest, attach another BPF object with fentry BPF program doing something with d_path. Hengqi, you'd need to have few variants for each possible position of file or path struct (e.g., file as first arg; as second arg; etc, same for hooks working with path directly), but I don't think that's going to be a lot of them. So as Daniel said, a bit of a work, but we'll have a much better confidence that we are not accidentally opening a big kernel crashing loophole.
quoted
+BTF_ID(func, security_file_fcntl) +BTF_ID(func, security_file_free) +BTF_ID(func, security_file_ioctl) +BTF_ID(func, security_file_lock) +BTF_ID(func, security_file_open) BTF_ID(func, security_file_permission) +BTF_ID(func, security_file_receive) +BTF_ID(func, security_file_set_fowner) BTF_ID(func, security_inode_getattr) -BTF_ID(func, security_file_open) +BTF_ID(func, security_sb_mount) #endif #ifdef CONFIG_SECURITY_PATH +BTF_ID(func, security_path_chmod) +BTF_ID(func, security_path_chown) +BTF_ID(func, security_path_chroot) +BTF_ID(func, security_path_link) +BTF_ID(func, security_path_mkdir) +BTF_ID(func, security_path_mknod) +BTF_ID(func, security_path_notify) +BTF_ID(func, security_path_rename) +BTF_ID(func, security_path_rmdir) +BTF_ID(func, security_path_symlink) BTF_ID(func, security_path_truncate) +BTF_ID(func, security_path_unlink) #endif -BTF_ID(func, vfs_truncate) -BTF_ID(func, vfs_fallocate) BTF_ID(func, dentry_open) -BTF_ID(func, vfs_getattr) BTF_ID(func, filp_close) +BTF_ID(func, vfs_cancel_lock) +BTF_ID(func, vfs_clone_file_range) +BTF_ID(func, vfs_copy_file_range) +BTF_ID(func, vfs_dedupe_file_range) +BTF_ID(func, vfs_dedupe_file_range_one) +BTF_ID(func, vfs_fadvise) +BTF_ID(func, vfs_fallocate) +BTF_ID(func, vfs_fchmod) +BTF_ID(func, vfs_fchown) +BTF_ID(func, vfs_fsync) +BTF_ID(func, vfs_fsync_range) +BTF_ID(func, vfs_getattr) +BTF_ID(func, vfs_getattr_nosec) +BTF_ID(func, vfs_iocb_iter_read) +BTF_ID(func, vfs_iocb_iter_write) +BTF_ID(func, vfs_ioctl) +BTF_ID(func, vfs_iter_read) +BTF_ID(func, vfs_iter_write) +BTF_ID(func, vfs_llseek) +BTF_ID(func, vfs_lock_file) +BTF_ID(func, vfs_open) +BTF_ID(func, vfs_read) +BTF_ID(func, vfs_readv) +BTF_ID(func, vfs_setlease) +BTF_ID(func, vfs_setpos) +BTF_ID(func, vfs_statfs) +BTF_ID(func, vfs_test_lock) +BTF_ID(func, vfs_truncate) +BTF_ID(func, vfs_utimes) +BTF_ID(func, vfs_write) +BTF_ID(func, vfs_writev) BTF_SET_END(btf_allowlist_d_path) static bool bpf_d_path_allowed(const struct bpf_prog *prog)