Skip to content
This repository was archived by the owner on Nov 8, 2023. It is now read-only.

Commit 5d99e19

Browse files
Xu Kuohaianakryiko
authored andcommitted
bpf, lsm: Add check for BPF LSM return value
A bpf prog returning a positive number attached to file_alloc_security hook makes kernel panic. This happens because file system can not filter out the positive number returned by the LSM prog using IS_ERR, and misinterprets this positive number as a file pointer. Given that hook file_alloc_security never returned positive number before the introduction of BPF LSM, and other BPF LSM hooks may encounter similar issues, this patch adds LSM return value check in verifier, to ensure no unexpected value is returned. Fixes: 520b7aa ("bpf: lsm: Initialize the BPF LSM hooks") Reported-by: Xin Liu <[email protected]> Signed-off-by: Xu Kuohai <[email protected]> Acked-by: Eduard Zingerman <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]> Signed-off-by: Andrii Nakryiko <[email protected]>
1 parent 21c7063 commit 5d99e19

File tree

5 files changed

+97
-11
lines changed

5 files changed

+97
-11
lines changed

include/linux/bpf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -927,6 +927,7 @@ struct bpf_insn_access_aux {
927927
};
928928
};
929929
struct bpf_verifier_log *log; /* for verbose logs */
930+
bool is_retval; /* is accessing function return value ? */
930931
};
931932

932933
static inline void

include/linux/bpf_lsm.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include <linux/sched.h>
1111
#include <linux/bpf.h>
12+
#include <linux/bpf_verifier.h>
1213
#include <linux/lsm_hooks.h>
1314

1415
#ifdef CONFIG_BPF_LSM
@@ -45,6 +46,8 @@ void bpf_inode_storage_free(struct inode *inode);
4546

4647
void bpf_lsm_find_cgroup_shim(const struct bpf_prog *prog, bpf_func_t *bpf_func);
4748

49+
int bpf_lsm_get_retval_range(const struct bpf_prog *prog,
50+
struct bpf_retval_range *range);
4851
#else /* !CONFIG_BPF_LSM */
4952

5053
static inline bool bpf_lsm_is_sleepable_hook(u32 btf_id)
@@ -78,6 +81,11 @@ static inline void bpf_lsm_find_cgroup_shim(const struct bpf_prog *prog,
7881
{
7982
}
8083

84+
static inline int bpf_lsm_get_retval_range(const struct bpf_prog *prog,
85+
struct bpf_retval_range *range)
86+
{
87+
return -EOPNOTSUPP;
88+
}
8189
#endif /* CONFIG_BPF_LSM */
8290

8391
#endif /* _LINUX_BPF_LSM_H */

kernel/bpf/bpf_lsm.c

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
#include <linux/lsm_hooks.h>
1212
#include <linux/bpf_lsm.h>
1313
#include <linux/kallsyms.h>
14-
#include <linux/bpf_verifier.h>
1514
#include <net/bpf_sk_storage.h>
1615
#include <linux/bpf_local_storage.h>
1716
#include <linux/btf_ids.h>
@@ -417,3 +416,36 @@ const struct bpf_verifier_ops lsm_verifier_ops = {
417416
.get_func_proto = bpf_lsm_func_proto,
418417
.is_valid_access = btf_ctx_access,
419418
};
419+
420+
/* hooks return 0 or 1 */
421+
BTF_SET_START(bool_lsm_hooks)
422+
#ifdef CONFIG_SECURITY_NETWORK_XFRM
423+
BTF_ID(func, bpf_lsm_xfrm_state_pol_flow_match)
424+
#endif
425+
#ifdef CONFIG_AUDIT
426+
BTF_ID(func, bpf_lsm_audit_rule_known)
427+
#endif
428+
BTF_ID(func, bpf_lsm_inode_xattr_skipcap)
429+
BTF_SET_END(bool_lsm_hooks)
430+
431+
int bpf_lsm_get_retval_range(const struct bpf_prog *prog,
432+
struct bpf_retval_range *retval_range)
433+
{
434+
/* no return value range for void hooks */
435+
if (!prog->aux->attach_func_proto->type)
436+
return -EINVAL;
437+
438+
if (btf_id_set_contains(&bool_lsm_hooks, prog->aux->attach_btf_id)) {
439+
retval_range->minval = 0;
440+
retval_range->maxval = 1;
441+
} else {
442+
/* All other available LSM hooks, except task_prctl, return 0
443+
* on success and negative error code on failure.
444+
* To keep things simple, we only allow bpf progs to return 0
445+
* or negative errno for task_prctl too.
446+
*/
447+
retval_range->minval = -MAX_ERRNO;
448+
retval_range->maxval = 0;
449+
}
450+
return 0;
451+
}

kernel/bpf/btf.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6416,8 +6416,11 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
64166416

64176417
if (arg == nr_args) {
64186418
switch (prog->expected_attach_type) {
6419-
case BPF_LSM_CGROUP:
64206419
case BPF_LSM_MAC:
6420+
/* mark we are accessing the return value */
6421+
info->is_retval = true;
6422+
fallthrough;
6423+
case BPF_LSM_CGROUP:
64216424
case BPF_TRACE_FEXIT:
64226425
/* When LSM programs are attached to void LSM hooks
64236426
* they use FEXIT trampolines and when attached to

kernel/bpf/verifier.c

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2334,6 +2334,25 @@ static void mark_reg_unknown(struct bpf_verifier_env *env,
23342334
__mark_reg_unknown(env, regs + regno);
23352335
}
23362336

2337+
static int __mark_reg_s32_range(struct bpf_verifier_env *env,
2338+
struct bpf_reg_state *regs,
2339+
u32 regno,
2340+
s32 s32_min,
2341+
s32 s32_max)
2342+
{
2343+
struct bpf_reg_state *reg = regs + regno;
2344+
2345+
reg->s32_min_value = max_t(s32, reg->s32_min_value, s32_min);
2346+
reg->s32_max_value = min_t(s32, reg->s32_max_value, s32_max);
2347+
2348+
reg->smin_value = max_t(s64, reg->smin_value, s32_min);
2349+
reg->smax_value = min_t(s64, reg->smax_value, s32_max);
2350+
2351+
reg_bounds_sync(reg);
2352+
2353+
return reg_bounds_sanity_check(env, reg, "s32_range");
2354+
}
2355+
23372356
static void __mark_reg_not_init(const struct bpf_verifier_env *env,
23382357
struct bpf_reg_state *reg)
23392358
{
@@ -5607,11 +5626,12 @@ static int check_packet_access(struct bpf_verifier_env *env, u32 regno, int off,
56075626
/* check access to 'struct bpf_context' fields. Supports fixed offsets only */
56085627
static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off, int size,
56095628
enum bpf_access_type t, enum bpf_reg_type *reg_type,
5610-
struct btf **btf, u32 *btf_id)
5629+
struct btf **btf, u32 *btf_id, bool *is_retval)
56115630
{
56125631
struct bpf_insn_access_aux info = {
56135632
.reg_type = *reg_type,
56145633
.log = &env->log,
5634+
.is_retval = false,
56155635
};
56165636

56175637
if (env->ops->is_valid_access &&
@@ -5624,6 +5644,7 @@ static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off,
56245644
* type of narrower access.
56255645
*/
56265646
*reg_type = info.reg_type;
5647+
*is_retval = info.is_retval;
56275648

56285649
if (base_type(*reg_type) == PTR_TO_BTF_ID) {
56295650
*btf = info.btf;
@@ -6792,6 +6813,17 @@ static int check_stack_access_within_bounds(
67926813
return grow_stack_state(env, state, -min_off /* size */);
67936814
}
67946815

6816+
static bool get_func_retval_range(struct bpf_prog *prog,
6817+
struct bpf_retval_range *range)
6818+
{
6819+
if (prog->type == BPF_PROG_TYPE_LSM &&
6820+
prog->expected_attach_type == BPF_LSM_MAC &&
6821+
!bpf_lsm_get_retval_range(prog, range)) {
6822+
return true;
6823+
}
6824+
return false;
6825+
}
6826+
67956827
/* check whether memory at (regno + off) is accessible for t = (read | write)
67966828
* if t==write, value_regno is a register which value is stored into memory
67976829
* if t==read, value_regno is a register which will receive the value from memory
@@ -6896,6 +6928,8 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
68966928
if (!err && value_regno >= 0 && (t == BPF_READ || rdonly_mem))
68976929
mark_reg_unknown(env, regs, value_regno);
68986930
} else if (reg->type == PTR_TO_CTX) {
6931+
bool is_retval = false;
6932+
struct bpf_retval_range range;
68996933
enum bpf_reg_type reg_type = SCALAR_VALUE;
69006934
struct btf *btf = NULL;
69016935
u32 btf_id = 0;
@@ -6911,7 +6945,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
69116945
return err;
69126946

69136947
err = check_ctx_access(env, insn_idx, off, size, t, &reg_type, &btf,
6914-
&btf_id);
6948+
&btf_id, &is_retval);
69156949
if (err)
69166950
verbose_linfo(env, insn_idx, "; ");
69176951
if (!err && t == BPF_READ && value_regno >= 0) {
@@ -6920,7 +6954,14 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
69206954
* case, we know the offset is zero.
69216955
*/
69226956
if (reg_type == SCALAR_VALUE) {
6923-
mark_reg_unknown(env, regs, value_regno);
6957+
if (is_retval && get_func_retval_range(env->prog, &range)) {
6958+
err = __mark_reg_s32_range(env, regs, value_regno,
6959+
range.minval, range.maxval);
6960+
if (err)
6961+
return err;
6962+
} else {
6963+
mark_reg_unknown(env, regs, value_regno);
6964+
}
69246965
} else {
69256966
mark_reg_known_zero(env, regs,
69266967
value_regno);
@@ -15762,12 +15803,13 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char
1576215803

1576315804
case BPF_PROG_TYPE_LSM:
1576415805
if (env->prog->expected_attach_type != BPF_LSM_CGROUP) {
15765-
/* Regular BPF_PROG_TYPE_LSM programs can return
15766-
* any value.
15767-
*/
15768-
return 0;
15769-
}
15770-
if (!env->prog->aux->attach_func_proto->type) {
15806+
/* no range found, any return value is allowed */
15807+
if (!get_func_retval_range(env->prog, &range))
15808+
return 0;
15809+
/* no restricted range, any return value is allowed */
15810+
if (range.minval == S32_MIN && range.maxval == S32_MAX)
15811+
return 0;
15812+
} else if (!env->prog->aux->attach_func_proto->type) {
1577115813
/* Make sure programs that attach to void
1577215814
* hooks don't try to modify return value.
1577315815
*/

0 commit comments

Comments
 (0)