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

Configure Feed

Select the types of activity you want to include in your feed.

bpf: lsm: Implement attach, detach and execution

JITed BPF programs are dynamically attached to the LSM hooks
using BPF trampolines. The trampoline prologue generates code to handle
conversion of the signature of the hook to the appropriate BPF context.

The allocated trampoline programs are attached to the nop functions
initialized as LSM hooks.

BPF_PROG_TYPE_LSM programs must have a GPL compatible license and
and need CAP_SYS_ADMIN (required for loading eBPF programs).

Upon attachment:

* A BPF fexit trampoline is used for LSM hooks with a void return type.
* A BPF fmod_ret trampoline is used for LSM hooks which return an
int. The attached programs can override the return value of the
bpf LSM hook to indicate a MAC Policy decision.

Signed-off-by: KP Singh <kpsingh@google.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Reviewed-by: Brendan Jackman <jackmanb@google.com>
Reviewed-by: Florent Revest <revest@google.com>
Acked-by: Andrii Nakryiko <andriin@fb.com>
Acked-by: James Morris <jamorris@linux.microsoft.com>
Link: https://lore.kernel.org/bpf/20200329004356.27286-5-kpsingh@chromium.org

authored by

KP Singh and committed by
Daniel Borkmann
9e4e01df 9d3fdea7

+116 -27
+11
include/linux/bpf_lsm.h
··· 17 17 #include <linux/lsm_hook_defs.h> 18 18 #undef LSM_HOOK 19 19 20 + int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog, 21 + const struct bpf_prog *prog); 22 + 23 + #else /* !CONFIG_BPF_LSM */ 24 + 25 + static inline int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog, 26 + const struct bpf_prog *prog) 27 + { 28 + return -EOPNOTSUPP; 29 + } 30 + 20 31 #endif /* CONFIG_BPF_LSM */ 21 32 22 33 #endif /* _LINUX_BPF_LSM_H */
+23
kernel/bpf/bpf_lsm.c
··· 9 9 #include <linux/btf.h> 10 10 #include <linux/lsm_hooks.h> 11 11 #include <linux/bpf_lsm.h> 12 + #include <linux/kallsyms.h> 13 + #include <linux/bpf_verifier.h> 12 14 13 15 /* For every LSM hook that allows attachment of BPF programs, declare a nop 14 16 * function where a BPF program can be attached. ··· 23 21 24 22 #include <linux/lsm_hook_defs.h> 25 23 #undef LSM_HOOK 24 + 25 + #define BPF_LSM_SYM_PREFX "bpf_lsm_" 26 + 27 + int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog, 28 + const struct bpf_prog *prog) 29 + { 30 + if (!prog->gpl_compatible) { 31 + bpf_log(vlog, 32 + "LSM programs must have a GPL compatible license\n"); 33 + return -EINVAL; 34 + } 35 + 36 + if (strncmp(BPF_LSM_SYM_PREFX, prog->aux->attach_func_name, 37 + sizeof(BPF_LSM_SYM_PREFX) - 1)) { 38 + bpf_log(vlog, "attach_btf_id %u points to wrong type name %s\n", 39 + prog->aux->attach_btf_id, prog->aux->attach_func_name); 40 + return -EINVAL; 41 + } 42 + 43 + return 0; 44 + } 26 45 27 46 const struct bpf_prog_ops lsm_prog_ops = { 28 47 };
+15 -1
kernel/bpf/btf.c
··· 3710 3710 } 3711 3711 3712 3712 if (arg == nr_args) { 3713 - if (prog->expected_attach_type == BPF_TRACE_FEXIT) { 3713 + if (prog->expected_attach_type == BPF_TRACE_FEXIT || 3714 + prog->expected_attach_type == BPF_LSM_MAC) { 3715 + /* When LSM programs are attached to void LSM hooks 3716 + * they use FEXIT trampolines and when attached to 3717 + * int LSM hooks, they use MODIFY_RETURN trampolines. 3718 + * 3719 + * While the LSM programs are BPF_MODIFY_RETURN-like 3720 + * the check: 3721 + * 3722 + * if (ret_type != 'int') 3723 + * return -EINVAL; 3724 + * 3725 + * is _not_ done here. This is still safe as LSM hooks 3726 + * have only void and int return types. 3727 + */ 3714 3728 if (!t) 3715 3729 return true; 3716 3730 t = btf_type_by_id(btf, t->type);
+39 -18
kernel/bpf/syscall.c
··· 25 25 #include <linux/nospec.h> 26 26 #include <linux/audit.h> 27 27 #include <uapi/linux/btf.h> 28 + #include <linux/bpf_lsm.h> 28 29 29 30 #define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || \ 30 31 (map)->map_type == BPF_MAP_TYPE_CGROUP_ARRAY || \ ··· 1936 1935 1937 1936 switch (prog_type) { 1938 1937 case BPF_PROG_TYPE_TRACING: 1938 + case BPF_PROG_TYPE_LSM: 1939 1939 case BPF_PROG_TYPE_STRUCT_OPS: 1940 1940 case BPF_PROG_TYPE_EXT: 1941 1941 break; ··· 2368 2366 struct file *link_file; 2369 2367 int link_fd, err; 2370 2368 2371 - if (prog->expected_attach_type != BPF_TRACE_FENTRY && 2372 - prog->expected_attach_type != BPF_TRACE_FEXIT && 2373 - prog->expected_attach_type != BPF_MODIFY_RETURN && 2374 - prog->type != BPF_PROG_TYPE_EXT) { 2369 + switch (prog->type) { 2370 + case BPF_PROG_TYPE_TRACING: 2371 + if (prog->expected_attach_type != BPF_TRACE_FENTRY && 2372 + prog->expected_attach_type != BPF_TRACE_FEXIT && 2373 + prog->expected_attach_type != BPF_MODIFY_RETURN) { 2374 + err = -EINVAL; 2375 + goto out_put_prog; 2376 + } 2377 + break; 2378 + case BPF_PROG_TYPE_EXT: 2379 + if (prog->expected_attach_type != 0) { 2380 + err = -EINVAL; 2381 + goto out_put_prog; 2382 + } 2383 + break; 2384 + case BPF_PROG_TYPE_LSM: 2385 + if (prog->expected_attach_type != BPF_LSM_MAC) { 2386 + err = -EINVAL; 2387 + goto out_put_prog; 2388 + } 2389 + break; 2390 + default: 2375 2391 err = -EINVAL; 2376 2392 goto out_put_prog; 2377 2393 } ··· 2468 2448 if (IS_ERR(prog)) 2469 2449 return PTR_ERR(prog); 2470 2450 2471 - if (prog->type != BPF_PROG_TYPE_RAW_TRACEPOINT && 2472 - prog->type != BPF_PROG_TYPE_TRACING && 2473 - prog->type != BPF_PROG_TYPE_EXT && 2474 - prog->type != BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE) { 2475 - err = -EINVAL; 2476 - goto out_put_prog; 2477 - } 2478 - 2479 - if (prog->type == BPF_PROG_TYPE_TRACING || 2480 - prog->type == BPF_PROG_TYPE_EXT) { 2451 + switch (prog->type) { 2452 + case BPF_PROG_TYPE_TRACING: 2453 + case BPF_PROG_TYPE_EXT: 2454 + case BPF_PROG_TYPE_LSM: 2481 2455 if (attr->raw_tracepoint.name) { 2482 2456 /* The attach point for this category of programs 2483 2457 * should be specified via btf_id during program load. ··· 2479 2465 err = -EINVAL; 2480 2466 goto out_put_prog; 2481 2467 } 2482 - if (prog->expected_attach_type == BPF_TRACE_RAW_TP) 2468 + if (prog->type == BPF_PROG_TYPE_TRACING && 2469 + prog->expected_attach_type == BPF_TRACE_RAW_TP) { 2483 2470 tp_name = prog->aux->attach_func_name; 2484 - else 2485 - return bpf_tracing_prog_attach(prog); 2486 - } else { 2471 + break; 2472 + } 2473 + return bpf_tracing_prog_attach(prog); 2474 + case BPF_PROG_TYPE_RAW_TRACEPOINT: 2475 + case BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE: 2487 2476 if (strncpy_from_user(buf, 2488 2477 u64_to_user_ptr(attr->raw_tracepoint.name), 2489 2478 sizeof(buf) - 1) < 0) { ··· 2495 2478 } 2496 2479 buf[sizeof(buf) - 1] = 0; 2497 2480 tp_name = buf; 2481 + break; 2482 + default: 2483 + err = -EINVAL; 2484 + goto out_put_prog; 2498 2485 } 2499 2486 2500 2487 btp = bpf_get_raw_tracepoint(tp_name);
+13 -4
kernel/bpf/trampoline.c
··· 6 6 #include <linux/ftrace.h> 7 7 #include <linux/rbtree_latch.h> 8 8 #include <linux/perf_event.h> 9 + #include <linux/btf.h> 9 10 10 11 /* dummy _ops. The verifier will operate on target program's ops. */ 11 12 const struct bpf_verifier_ops bpf_extension_verifier_ops = { ··· 234 233 return err; 235 234 } 236 235 237 - static enum bpf_tramp_prog_type bpf_attach_type_to_tramp(enum bpf_attach_type t) 236 + static enum bpf_tramp_prog_type bpf_attach_type_to_tramp(struct bpf_prog *prog) 238 237 { 239 - switch (t) { 238 + switch (prog->expected_attach_type) { 240 239 case BPF_TRACE_FENTRY: 241 240 return BPF_TRAMP_FENTRY; 242 241 case BPF_MODIFY_RETURN: 243 242 return BPF_TRAMP_MODIFY_RETURN; 244 243 case BPF_TRACE_FEXIT: 245 244 return BPF_TRAMP_FEXIT; 245 + case BPF_LSM_MAC: 246 + if (!prog->aux->attach_func_proto->type) 247 + /* The function returns void, we cannot modify its 248 + * return value. 249 + */ 250 + return BPF_TRAMP_FEXIT; 251 + else 252 + return BPF_TRAMP_MODIFY_RETURN; 246 253 default: 247 254 return BPF_TRAMP_REPLACE; 248 255 } ··· 264 255 int cnt; 265 256 266 257 tr = prog->aux->trampoline; 267 - kind = bpf_attach_type_to_tramp(prog->expected_attach_type); 258 + kind = bpf_attach_type_to_tramp(prog); 268 259 mutex_lock(&tr->mutex); 269 260 if (tr->extension_prog) { 270 261 /* cannot attach fentry/fexit if extension prog is attached. ··· 314 305 int err; 315 306 316 307 tr = prog->aux->trampoline; 317 - kind = bpf_attach_type_to_tramp(prog->expected_attach_type); 308 + kind = bpf_attach_type_to_tramp(prog); 318 309 mutex_lock(&tr->mutex); 319 310 if (kind == BPF_TRAMP_REPLACE) { 320 311 WARN_ON_ONCE(!tr->extension_prog);
+15 -4
kernel/bpf/verifier.c
··· 20 20 #include <linux/perf_event.h> 21 21 #include <linux/ctype.h> 22 22 #include <linux/error-injection.h> 23 + #include <linux/bpf_lsm.h> 23 24 24 25 #include "disasm.h" 25 26 ··· 6493 6492 struct tnum range = tnum_range(0, 1); 6494 6493 int err; 6495 6494 6496 - /* The struct_ops func-ptr's return type could be "void" */ 6497 - if (env->prog->type == BPF_PROG_TYPE_STRUCT_OPS && 6495 + /* LSM and struct_ops func-ptr's return type could be "void" */ 6496 + if ((env->prog->type == BPF_PROG_TYPE_STRUCT_OPS || 6497 + env->prog->type == BPF_PROG_TYPE_LSM) && 6498 6498 !prog->aux->attach_func_proto->type) 6499 6499 return 0; 6500 6500 ··· 9925 9923 if (prog->type == BPF_PROG_TYPE_STRUCT_OPS) 9926 9924 return check_struct_ops_btf_id(env); 9927 9925 9928 - if (prog->type != BPF_PROG_TYPE_TRACING && !prog_extension) 9926 + if (prog->type != BPF_PROG_TYPE_TRACING && 9927 + prog->type != BPF_PROG_TYPE_LSM && 9928 + !prog_extension) 9929 9929 return 0; 9930 9930 9931 9931 if (!btf_id) { ··· 10058 10054 return -EINVAL; 10059 10055 /* fallthrough */ 10060 10056 case BPF_MODIFY_RETURN: 10057 + case BPF_LSM_MAC: 10061 10058 case BPF_TRACE_FENTRY: 10062 10059 case BPF_TRACE_FEXIT: 10060 + prog->aux->attach_func_name = tname; 10061 + if (prog->type == BPF_PROG_TYPE_LSM) { 10062 + ret = bpf_lsm_verify_prog(&env->log, prog); 10063 + if (ret < 0) 10064 + return ret; 10065 + } 10066 + 10063 10067 if (!btf_type_is_func(t)) { 10064 10068 verbose(env, "attach_btf_id %u is not a function\n", 10065 10069 btf_id); ··· 10082 10070 tr = bpf_trampoline_lookup(key); 10083 10071 if (!tr) 10084 10072 return -ENOMEM; 10085 - prog->aux->attach_func_name = tname; 10086 10073 /* t is either vmlinux type or another program's type */ 10087 10074 prog->aux->attach_func_proto = t; 10088 10075 mutex_lock(&tr->mutex);