History

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.

KP Singh committed 6 years ago

Commit 9e4e01d1 parent 9d3fdea6 files · +116 −27Browse files

Unified · Split

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