From ca8ae2066f8852a118e0885ab22d22e603001481 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= Date: Wed, 29 Mar 2017 01:30:33 +0200 Subject: [PATCH 07/12] bpf: Define handle_fs and add a new helper bpf_handle_fs_get_mode() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an eBPF function bpf_handle_fs_get_mode(handle_fs) to get the mode of a an abstract object wrapping either a file, a dentry, a path, or an inode. Changes since v5: * cosmetic fixes and rebase Changes since v4: * use a file abstraction (handle) to wrap inode, dentry, path and file structs * remove bpf_landlock_cmp_fs_beneath() * rename the BPF helper and move it to kernel/bpf/ * tighten helpers accessible by a Landlock rule Changes since v3: * remove bpf_landlock_cmp_fs_prop() (suggested by Alexie Starovoitov) * add hooks dealing with struct inode and struct path pointers: inode_permission and inode_getattr * add abstraction over eBPF helper arguments thanks to wrapping structs * add bpf_landlock_get_fs_mode() helper to check file type and mode * merge WARN_ON() (suggested by Kees Cook) * fix and update bpf_helpers.h * use BPF_CALL_* for eBPF helpers (suggested by Alexie Starovoitov) * make handle arraymap safe (RCU) and remove buggy synchronize_rcu() * factor out the arraymay walk * use size_t to index array (suggested by Jann Horn) Changes since v2: * add MNT_INTERNAL check to only add file handle from user-visible FS (e.g. no anonymous inode) * replace struct file* with struct path* in map_landlock_handle * add BPF protos * fix bpf_landlock_cmp_fs_prop_with_struct_file() Signed-off-by: Mickaël Salaün Cc: Alexei Starovoitov Cc: Andy Lutomirski Cc: Daniel Borkmann Cc: David S. Miller Cc: James Morris Cc: Kees Cook Cc: Serge E. Hallyn Cc: Jann Horn (cherry picked from commit 7cb1d72a1cca9442bc0b9c3eeff621b9d1709296) --- include/linux/bpf.h | 33 +++++++++++++++++++++ include/uapi/linux/bpf.h | 17 +++++++++++ kernel/bpf/Makefile | 2 +- kernel/bpf/helpers_fs.c | 52 ++++++++++++++++++++++++++++++++ kernel/bpf/verifier.c | 6 ++++ samples/bpf/bpf_helpers.h | 2 ++ security/landlock/init.c | 6 ++++ tools/include/uapi/linux/bpf.h | 67 ++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 kernel/bpf/helpers_fs.c diff --git a/include/linux/bpf.h b/include/linux/bpf.h index d4b9ca479f79..d66843a2aafb 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -13,6 +13,11 @@ #include #include +/* FS helpers */ +#include /* struct dentry */ +#include /* struct file, struct inode */ +#include /* struct path */ + struct perf_event; struct bpf_map; @@ -80,6 +85,8 @@ enum bpf_arg_type { ARG_PTR_TO_CTX, /* pointer to context */ ARG_ANYTHING, /* any (initialized) argument is ok */ + + ARG_CONST_PTR_TO_HANDLE_FS, /* pointer to an abstract FS struct */ }; /* type of values returned from helper functions */ @@ -146,6 +153,9 @@ enum bpf_reg_type { * map element. */ PTR_TO_MAP_VALUE_ADJ, + + /* FS helpers */ + CONST_PTR_TO_HANDLE_FS, }; struct bpf_prog; @@ -215,6 +225,26 @@ struct bpf_event_entry { struct rcu_head rcu; }; +/* FS helpers */ +enum bpf_handle_fs_type { + BPF_HANDLE_FS_TYPE_NONE, + BPF_HANDLE_FS_TYPE_FILE, + BPF_HANDLE_FS_TYPE_INODE, + BPF_HANDLE_FS_TYPE_PATH, + BPF_HANDLE_FS_TYPE_DENTRY, +}; + +struct bpf_handle_fs { + enum bpf_handle_fs_type type; + union { + struct file *file; + struct inode *inode; + const struct path *path; + struct dentry *dentry; + }; +}; + + u64 bpf_tail_call(u64 ctx, u64 r2, u64 index, u64 r4, u64 r5); u64 bpf_get_stackid(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5); @@ -331,6 +361,9 @@ extern const struct bpf_func_proto bpf_skb_vlan_push_proto; extern const struct bpf_func_proto bpf_skb_vlan_pop_proto; extern const struct bpf_func_proto bpf_get_stackid_proto; +/* FS helpers */ +extern const struct bpf_func_proto bpf_handle_fs_get_mode_proto; + /* Shared helpers among cBPF and eBPF. */ void bpf_user_rnd_init_once(void); u64 bpf_user_rnd_u32(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5); diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index f190be4f609f..6aebf7144e93 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -443,6 +443,23 @@ enum bpf_func_id { */ BPF_FUNC_set_hash_invalid, + BPF_FUNC_get_numa_node_id, + BPF_FUNC_skb_change_head, + BPF_FUNC_xdp_adjust_head, + BPF_FUNC_probe_read_str, + BPF_FUNC_get_socket_cookie, + BPF_FUNC_get_socket_uid, + + /** + * s64 bpf_handle_fs_get_mode(handle_fs) + * Get the mode of a struct bpf_handle_fs + * fs: struct bpf_handle_fs address + * Return: + * >= 0 file mode + * < 0 error + */ + BPF_FUNC_handle_fs_get_mode, + __BPF_FUNC_MAX_ID, }; diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile index eed911d091da..8fffb30ac7a1 100644 --- a/kernel/bpf/Makefile +++ b/kernel/bpf/Makefile @@ -1,6 +1,6 @@ obj-y := core.o -obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o +obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o helpers_fs.o obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o ifeq ($(CONFIG_PERF_EVENTS),y) obj-$(CONFIG_BPF_SYSCALL) += stackmap.o diff --git a/kernel/bpf/helpers_fs.c b/kernel/bpf/helpers_fs.c new file mode 100644 index 000000000000..d524d382adeb --- /dev/null +++ b/kernel/bpf/helpers_fs.c @@ -0,0 +1,52 @@ +/* + * BPF filesystem helpers + * + * Copyright © 2017 Mickaël Salaün + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + */ + +#include /* struct bpf_handle_fs */ +#include +#include /* BPF_CALL*() */ + +BPF_CALL_1(bpf_handle_fs_get_mode, struct bpf_handle_fs *, handle_fs) +{ + if (WARN_ON(!handle_fs)) + return -EFAULT; + if (!handle_fs->file) { + /* file can be null for anonymous mmap */ + WARN_ON(handle_fs->type != BPF_HANDLE_FS_TYPE_FILE); + return -ENOENT; + } + switch (handle_fs->type) { + case BPF_HANDLE_FS_TYPE_FILE: + if (WARN_ON(!handle_fs->file->f_inode)) + return -ENOENT; + return handle_fs->file->f_inode->i_mode; + case BPF_HANDLE_FS_TYPE_INODE: + return handle_fs->inode->i_mode; + case BPF_HANDLE_FS_TYPE_PATH: + if (WARN_ON(!handle_fs->path->dentry || + !handle_fs->path->dentry->d_inode)) + return -ENOENT; + return handle_fs->path->dentry->d_inode->i_mode; + case BPF_HANDLE_FS_TYPE_DENTRY: + if (WARN_ON(!handle_fs->dentry->d_inode)) + return -ENOENT; + return handle_fs->dentry->d_inode->i_mode; + case BPF_HANDLE_FS_TYPE_NONE: + default: + WARN_ON(1); + return -EFAULT; + } +} + +const struct bpf_func_proto bpf_handle_fs_get_mode_proto = { + .func = bpf_handle_fs_get_mode, + .gpl_only = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_CONST_PTR_TO_HANDLE_FS, +}; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index f5f082de2f7f..7cecf5099207 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -188,6 +188,7 @@ static const char * const reg_type_str[] = { [CONST_IMM] = "imm", [PTR_TO_PACKET] = "pkt", [PTR_TO_PACKET_END] = "pkt_end", + [CONST_PTR_TO_HANDLE_FS] = "handle_fs", }; static void print_verifier_state(struct bpf_verifier_state *state) @@ -520,6 +521,7 @@ static bool is_spillable_regtype(enum bpf_reg_type type) case PTR_TO_PACKET_END: case FRAME_PTR: case CONST_PTR_TO_MAP: + case CONST_PTR_TO_HANDLE_FS: return true; default: return false; @@ -981,6 +983,10 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, expected_type = PTR_TO_CTX; if (type != expected_type) goto err_type; + } else if (arg_type == ARG_CONST_PTR_TO_HANDLE_FS) { + expected_type = CONST_PTR_TO_HANDLE_FS; + if (type != expected_type) + goto err_type; } else if (arg_type == ARG_PTR_TO_STACK || arg_type == ARG_PTR_TO_RAW_STACK) { expected_type = PTR_TO_STACK; diff --git a/samples/bpf/bpf_helpers.h b/samples/bpf/bpf_helpers.h index dadd5161bd91..d962a5d76725 100644 --- a/samples/bpf/bpf_helpers.h +++ b/samples/bpf/bpf_helpers.h @@ -57,6 +57,8 @@ static int (*bpf_skb_set_tunnel_opt)(void *ctx, void *md, int size) = (void *) BPF_FUNC_skb_set_tunnel_opt; static unsigned long long (*bpf_get_prandom_u32)(void) = (void *) BPF_FUNC_get_prandom_u32; +static long long (*bpf_handle_fs_get_mode)(void *handle_fs) = + (void *) BPF_FUNC_handle_fs_get_mode; /* llvm builtin functions that eBPF C program may use to * emit BPF_LD_ABS and BPF_LD_IND instructions diff --git a/security/landlock/init.c b/security/landlock/init.c index 0a97026f1c07..914895d08320 100644 --- a/security/landlock/init.c +++ b/security/landlock/init.c @@ -78,6 +78,12 @@ static inline const struct bpf_func_proto *bpf_landlock_func_proto( case BPF_FUNC_map_lookup_elem: return &bpf_map_lookup_elem_proto; + /* event_fs */ + case BPF_FUNC_handle_fs_get_mode: + if (event_fs) + return &bpf_handle_fs_get_mode_proto; + return NULL; + /* ability_write */ case BPF_FUNC_map_delete_elem: if (ability_write) diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 0167f61cb3ba..6aebf7144e93 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -393,6 +393,73 @@ enum bpf_func_id { */ BPF_FUNC_probe_write_user, + /** + * bpf_current_task_under_cgroup(map, index) - Check cgroup2 membership of current task + * @map: pointer to bpf_map in BPF_MAP_TYPE_CGROUP_ARRAY type + * @index: index of the cgroup in the bpf_map + * Return: + * == 0 current failed the cgroup2 descendant test + * == 1 current succeeded the cgroup2 descendant test + * < 0 error + */ + BPF_FUNC_current_task_under_cgroup, + + /** + * bpf_skb_change_tail(skb, len, flags) + * The helper will resize the skb to the given new size, + * to be used f.e. with control messages. + * @skb: pointer to skb + * @len: new skb length + * @flags: reserved + * Return: 0 on success or negative error + */ + BPF_FUNC_skb_change_tail, + + /** + * bpf_skb_pull_data(skb, len) + * The helper will pull in non-linear data in case the + * skb is non-linear and not all of len are part of the + * linear section. Only needed for read/write with direct + * packet access. + * @skb: pointer to skb + * @len: len to make read/writeable + * Return: 0 on success or negative error + */ + BPF_FUNC_skb_pull_data, + + /** + * bpf_csum_update(skb, csum) + * Adds csum into skb->csum in case of CHECKSUM_COMPLETE. + * @skb: pointer to skb + * @csum: csum to add + * Return: csum on success or negative error + */ + BPF_FUNC_csum_update, + + /** + * bpf_set_hash_invalid(skb) + * Invalidate current skb>hash. + * @skb: pointer to skb + */ + BPF_FUNC_set_hash_invalid, + + BPF_FUNC_get_numa_node_id, + BPF_FUNC_skb_change_head, + BPF_FUNC_xdp_adjust_head, + BPF_FUNC_probe_read_str, + BPF_FUNC_get_socket_cookie, + BPF_FUNC_get_socket_uid, + + /** + * s64 bpf_handle_fs_get_mode(handle_fs) + * Get the mode of a struct bpf_handle_fs + * fs: struct bpf_handle_fs address + * Return: + * >= 0 file mode + * < 0 error + */ + BPF_FUNC_handle_fs_get_mode, + __BPF_FUNC_MAX_ID, }; -- 2.11.0