Direct inode reads can differ from stat() and related system calls, especially on overlayfs. Add a sleepable LSM kfunc to query a trusted path and return struct kstat. Use vfs_getattr_nosec() to avoid calling security_inode_getattr() again. Limit the copy to the smaller of the buffer and the kernel structure. Signed-off-by: Anastasios Papagiannis --- fs/bpf_fs_kfuncs.c | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/fs/bpf_fs_kfuncs.c b/fs/bpf_fs_kfuncs.c index 35ccd98a4de6..97afc9659f1a 100644 --- a/fs/bpf_fs_kfuncs.c +++ b/fs/bpf_fs_kfuncs.c @@ -96,6 +96,40 @@ __bpf_kfunc int bpf_path_d_path(const struct path *path, char *buf, size_t buf__ return len; } +/** + * bpf_path_getattr - get the attributes of the supplied path + * @path: trusted path to get the attributes of + * @request_mask: STATX_* flags of the attributes that the caller wants + * @query_flags: AT_STATX_SYNC_AS_STAT, AT_STATX_FORCE_SYNC or AT_STATX_DONT_SYNC + * @stat__uninit: buffer to return the attributes in + * @stat__sz: size of the supplied buffer + * + * Ask the filesystem for the attributes of *path*. This is vfs_getattr() + * without the call into security_inode_getattr(). + * + * This BPF kfunc may only be called from sleepable BPF LSM programs. + * + * Copies the smaller of @stat__sz and sizeof(struct kstat). Any remaining + * buffer bytes are left untouched. On error, the buffer is unchanged. + * + * Return: Zero on success or a negative error code. + */ +__bpf_kfunc int bpf_path_getattr(const struct path *path, u32 request_mask, + u32 query_flags, struct kstat *stat__uninit, + size_t stat__sz) +{ + struct kstat stat; + int ret; + + ret = vfs_getattr_nosec(path, &stat, request_mask, query_flags); + if (ret) + return ret; + + /* struct kstat in the program's vmlinux.h may have a different size. */ + memcpy(stat__uninit, &stat, min(stat__sz, sizeof(stat))); + return 0; +} + static bool match_security_bpf_prefix(const char *name__str) { return !strncmp(name__str, XATTR_NAME_BPF_LSM, XATTR_NAME_BPF_LSM_LEN); @@ -504,6 +538,7 @@ BTF_KFUNCS_START(bpf_fs_kfunc_set_ids) BTF_ID_FLAGS(func, bpf_get_task_exe_file, KF_ACQUIRE | KF_RET_NULL) BTF_ID_FLAGS(func, bpf_put_file, KF_RELEASE) BTF_ID_FLAGS(func, bpf_path_d_path) +BTF_ID_FLAGS(func, bpf_path_getattr, KF_SLEEPABLE) BTF_ID_FLAGS(func, bpf_get_dentry_xattr, KF_SLEEPABLE) BTF_ID_FLAGS(func, bpf_get_file_xattr, KF_SLEEPABLE) BTF_ID_FLAGS(func, bpf_set_dentry_xattr, KF_SLEEPABLE) @@ -515,8 +550,9 @@ BTF_ID_FLAGS(func, bpf_sock_read_xattr, KF_RCU) #endif BTF_KFUNCS_END(bpf_fs_kfunc_set_ids) -/* Side-effecting kfuncs that stay exclusive to LSM programs. */ +/* Kfuncs that stay exclusive to LSM programs. */ BTF_SET_START(bpf_fs_kfunc_lsm_only_ids) +BTF_ID(func, bpf_path_getattr) BTF_ID(func, bpf_set_dentry_xattr) BTF_ID(func, bpf_remove_dentry_xattr) BTF_SET_END(bpf_fs_kfunc_lsm_only_ids) -- 2.56.0