Skip to content

Commit 9e4e01d

Browse files
sinkapborkmann
authored andcommitted
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 <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]> Reviewed-by: Brendan Jackman <[email protected]> Reviewed-by: Florent Revest <[email protected]> Acked-by: Andrii Nakryiko <[email protected]> Acked-by: James Morris <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent 9d3fdea commit 9e4e01d

File tree

6 files changed

+116
-27
lines changed

6 files changed

+116
-27
lines changed

include/linux/bpf_lsm.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,17 @@
1717
#include <linux/lsm_hook_defs.h>
1818
#undef LSM_HOOK
1919

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+
2031
#endif /* CONFIG_BPF_LSM */
2132

2233
#endif /* _LINUX_BPF_LSM_H */

kernel/bpf/bpf_lsm.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#include <linux/btf.h>
1010
#include <linux/lsm_hooks.h>
1111
#include <linux/bpf_lsm.h>
12+
#include <linux/kallsyms.h>
13+
#include <linux/bpf_verifier.h>
1214

1315
/* For every LSM hook that allows attachment of BPF programs, declare a nop
1416
* function where a BPF program can be attached.
@@ -22,6 +24,27 @@ noinline RET bpf_lsm_##NAME(__VA_ARGS__) \
2224
#include <linux/lsm_hook_defs.h>
2325
#undef LSM_HOOK
2426

27+
#define BPF_LSM_SYM_PREFX "bpf_lsm_"
28+
29+
int 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+
2548
const struct bpf_prog_ops lsm_prog_ops = {
2649
};
2750

kernel/bpf/btf.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3710,7 +3710,21 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
37103710
}
37113711

37123712
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+
*/
37143728
if (!t)
37153729
return true;
37163730
t = btf_type_by_id(btf, t->type);

kernel/bpf/syscall.c

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <linux/nospec.h>
2626
#include <linux/audit.h>
2727
#include <uapi/linux/btf.h>
28+
#include <linux/bpf_lsm.h>
2829

2930
#define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || \
3031
(map)->map_type == BPF_MAP_TYPE_CGROUP_ARRAY || \
@@ -1935,6 +1936,7 @@ bpf_prog_load_check_attach(enum bpf_prog_type prog_type,
19351936

19361937
switch (prog_type) {
19371938
case BPF_PROG_TYPE_TRACING:
1939+
case BPF_PROG_TYPE_LSM:
19381940
case BPF_PROG_TYPE_STRUCT_OPS:
19391941
case BPF_PROG_TYPE_EXT:
19401942
break;
@@ -2366,10 +2368,28 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog)
23662368
struct file *link_file;
23672369
int link_fd, err;
23682370

2369-
if (prog->expected_attach_type != BPF_TRACE_FENTRY &&
2370-
prog->expected_attach_type != BPF_TRACE_FEXIT &&
2371-
prog->expected_attach_type != BPF_MODIFY_RETURN &&
2372-
prog->type != BPF_PROG_TYPE_EXT) {
2371+
switch (prog->type) {
2372+
case BPF_PROG_TYPE_TRACING:
2373+
if (prog->expected_attach_type != BPF_TRACE_FENTRY &&
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:
23732393
err = -EINVAL;
23742394
goto out_put_prog;
23752395
}
@@ -2448,28 +2468,25 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
24482468
if (IS_ERR(prog))
24492469
return PTR_ERR(prog);
24502470

2451-
if (prog->type != BPF_PROG_TYPE_RAW_TRACEPOINT &&
2452-
prog->type != BPF_PROG_TYPE_TRACING &&
2453-
prog->type != BPF_PROG_TYPE_EXT &&
2454-
prog->type != BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE) {
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) {
2471+
switch (prog->type) {
2472+
case BPF_PROG_TYPE_TRACING:
2473+
case BPF_PROG_TYPE_EXT:
2474+
case BPF_PROG_TYPE_LSM:
24612475
if (attr->raw_tracepoint.name) {
24622476
/* The attach point for this category of programs
24632477
* should be specified via btf_id during program load.
24642478
*/
24652479
err = -EINVAL;
24662480
goto out_put_prog;
24672481
}
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) {
24692484
tp_name = prog->aux->attach_func_name;
2470-
else
2471-
return bpf_tracing_prog_attach(prog);
2472-
} else {
2485+
break;
2486+
}
2487+
return bpf_tracing_prog_attach(prog);
2488+
case BPF_PROG_TYPE_RAW_TRACEPOINT:
2489+
case BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE:
24732490
if (strncpy_from_user(buf,
24742491
u64_to_user_ptr(attr->raw_tracepoint.name),
24752492
sizeof(buf) - 1) < 0) {
@@ -2478,6 +2495,10 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
24782495
}
24792496
buf[sizeof(buf) - 1] = 0;
24802497
tp_name = buf;
2498+
break;
2499+
default:
2500+
err = -EINVAL;
2501+
goto out_put_prog;
24812502
}
24822503

24832504
btp = bpf_get_raw_tracepoint(tp_name);

kernel/bpf/trampoline.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <linux/ftrace.h>
77
#include <linux/rbtree_latch.h>
88
#include <linux/perf_event.h>
9+
#include <linux/btf.h>
910

1011
/* dummy _ops. The verifier will operate on target program's ops. */
1112
const struct bpf_verifier_ops bpf_extension_verifier_ops = {
@@ -233,15 +234,23 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr)
233234
return err;
234235
}
235236

236-
static enum bpf_tramp_prog_type bpf_attach_type_to_tramp(enum bpf_attach_type t)
237+
static enum bpf_tramp_prog_type bpf_attach_type_to_tramp(struct bpf_prog *prog)
237238
{
238-
switch (t) {
239+
switch (prog->expected_attach_type) {
239240
case BPF_TRACE_FENTRY:
240241
return BPF_TRAMP_FENTRY;
241242
case BPF_MODIFY_RETURN:
242243
return BPF_TRAMP_MODIFY_RETURN;
243244
case BPF_TRACE_FEXIT:
244245
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;
245254
default:
246255
return BPF_TRAMP_REPLACE;
247256
}
@@ -255,7 +264,7 @@ int bpf_trampoline_link_prog(struct bpf_prog *prog)
255264
int cnt;
256265

257266
tr = prog->aux->trampoline;
258-
kind = bpf_attach_type_to_tramp(prog->expected_attach_type);
267+
kind = bpf_attach_type_to_tramp(prog);
259268
mutex_lock(&tr->mutex);
260269
if (tr->extension_prog) {
261270
/* cannot attach fentry/fexit if extension prog is attached.
@@ -305,7 +314,7 @@ int bpf_trampoline_unlink_prog(struct bpf_prog *prog)
305314
int err;
306315

307316
tr = prog->aux->trampoline;
308-
kind = bpf_attach_type_to_tramp(prog->expected_attach_type);
317+
kind = bpf_attach_type_to_tramp(prog);
309318
mutex_lock(&tr->mutex);
310319
if (kind == BPF_TRAMP_REPLACE) {
311320
WARN_ON_ONCE(!tr->extension_prog);

kernel/bpf/verifier.c

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <linux/perf_event.h>
2121
#include <linux/ctype.h>
2222
#include <linux/error-injection.h>
23+
#include <linux/bpf_lsm.h>
2324

2425
#include "disasm.h"
2526

@@ -6492,8 +6493,9 @@ static int check_return_code(struct bpf_verifier_env *env)
64926493
struct tnum range = tnum_range(0, 1);
64936494
int err;
64946495

6495-
/* The struct_ops func-ptr's return type could be "void" */
6496-
if (env->prog->type == BPF_PROG_TYPE_STRUCT_OPS &&
6496+
/* LSM and struct_ops func-ptr's return type could be "void" */
6497+
if ((env->prog->type == BPF_PROG_TYPE_STRUCT_OPS ||
6498+
env->prog->type == BPF_PROG_TYPE_LSM) &&
64976499
!prog->aux->attach_func_proto->type)
64986500
return 0;
64996501

@@ -9923,7 +9925,9 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
99239925
if (prog->type == BPF_PROG_TYPE_STRUCT_OPS)
99249926
return check_struct_ops_btf_id(env);
99259927

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)
99279931
return 0;
99289932

99299933
if (!btf_id) {
@@ -10054,8 +10058,16 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
1005410058
return -EINVAL;
1005510059
/* fallthrough */
1005610060
case BPF_MODIFY_RETURN:
10061+
case BPF_LSM_MAC:
1005710062
case BPF_TRACE_FENTRY:
1005810063
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+
1005910071
if (!btf_type_is_func(t)) {
1006010072
verbose(env, "attach_btf_id %u is not a function\n",
1006110073
btf_id);
@@ -10070,7 +10082,6 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
1007010082
tr = bpf_trampoline_lookup(key);
1007110083
if (!tr)
1007210084
return -ENOMEM;
10073-
prog->aux->attach_func_name = tname;
1007410085
/* t is either vmlinux type or another program's type */
1007510086
prog->aux->attach_func_proto = t;
1007610087
mutex_lock(&tr->mutex);

0 commit comments

Comments
 (0)