From: Roberto Sassu roberto.sassu@huawei.com
Notes: - This patch set addresses the kernel panic described below, and not the more broad issue of accessing kernel objects whose pointer is passed as parameter by LSM hooks - Alternative approaches trying to limit return values at run-time either in the security subsystem or in the eBPF JIT are not preferred by the respective maintainers - Although all eBPF selftests have been verified to pass, it still might be cumbersome to have an eBPF program being accepted by the eBPF verifier (e.g. ANDing negative numbers causes existing bounds to be lost) - The patch to store whether a register state changed due to an ALU64 or an ALU32 operation might not be correct/complete, a review by eBPF maintainers would be needed - This patch set requires "lsm: make security_socket_getpeersec_stream() sockptr_t safe", in lsm/next - The modification of the LSM infrastructure to define allowed return values for the LSM hooks could be replaced with an eBPF-only fix, with the drawback of having to update the information manually each time a new hook is added; allowing zero or negative values by default could be reasonable, but there are already exceptions of LSM hooks accepting 0 or 1 (ismaclabel) - The patches to fix the LSM infrastructure documentation are separated from this patch set and available here: https://lore.kernel.org/linux-security-module/20221128144240.210110-1-robert...
BPF LSM defines attachment points to allows security modules (eBPF programs with type LSM) to provide their implementation of the desired LSM hooks.
Unfortunately, BPF LSM does not restrict which values security modules can return (for non-void LSM hooks). If they put arbitrary values instead of those stated in include/linux/lsm_hooks.h, they could cause big troubles.
For example, this simple eBPF program:
SEC("lsm/inode_permission") int BPF_PROG(test_int_hook, struct inode *inode, int mask) { return 1; }
causes the following kernel panic:
[ 181.130807] BUG: kernel NULL pointer dereference, address: 0000000000000079 [ 181.131478] #PF: supervisor read access in kernel mode [ 181.131942] #PF: error_code(0x0000) - not-present page [ 181.132407] PGD 0 P4D 0 [ 181.132650] Oops: 0000 [#1] PREEMPT SMP NOPTI [ 181.133054] CPU: 5 PID: 857 Comm: systemd-oomd Tainted: G OE 6.1.0-rc7+ #530 [ 181.133806] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.13.0-1ubuntu1.1 04/01/2014 [ 181.134601] RIP: 0010:do_sys_openat2+0x235/0x300
[...]
[ 181.136682] RSP: 0018:ffffc90001557ee0 EFLAGS: 00010203 [ 181.137154] RAX: 0000000000000001 RBX: ffffc90001557f20 RCX: ffff888112003380 [ 181.137790] RDX: 0000000000000000 RSI: ffffffff8280b026 RDI: ffffc90001557e28 [ 181.138432] RBP: 0000000000000001 R08: 0000000000000001 R09: 0000000000000000 [ 181.139081] R10: ffffffff835097dc R11: 0000000000000000 R12: ffff888106118000 [ 181.139717] R13: 000000000000000c R14: 0000000000000000 R15: 0000000000000000 [ 181.140149] FS: 00007fa6ceb0bb40(0000) GS:ffff88846fb40000(0000) knlGS:0000000000000000 [ 181.140556] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 181.140865] CR2: 0000000000000079 CR3: 0000000135c50000 CR4: 0000000000350ee0 [ 181.141239] Call Trace: [ 181.141373] <TASK> [ 181.141495] do_sys_open+0x34/0x60 [ 181.141678] do_syscall_64+0x3b/0x90 [ 181.141875] entry_SYSCALL_64_after_hwframe+0x63/0xcd
Avoid this situation by statically analyzing the eBPF programs attaching to LSM hooks, and ensure that their return values are compatible with the LSM infrastructure conventions.
First, add a preliminary patch (patch 1) to fix a small code duplication issue.
Extend the eBPF verifier to let BPF LSM determine whether it should check estimated 64 bit values or the 32 bit ones (patch 2). Also, extend the LSM infrastructure to record more precisely the allowed return values depending on the documentation found in include/linux/lsm_hooks.h (patch 3). Add the LSM_RET_NEG, LSM_RET_ZERO, LSM_RET_ONE, LSM_RET_GT_ONE flags to an LSM hook if that hook allows respectively > 0, 0, 1, > 1 return values.
Then, extend BPF LSM to verify that return values, estimated by the verifier by analyzing the eBPF program, fall in the allowed intervals found from the return value flags of the LSM hook being attached to (patch 4).
Finally, add new tests to ensure that the verifier enforces return values correctly (patch 5), and slightly modify existing tests to make them follow the LSM infrastructure conventions (patches 6-7) and are accepted by the verifier.
Changelog:
v1: - Complete the documentation of return values in lsm_hooks.h - Introduce return value flags in the LSM infrastructure - Use those flags instead of the scattered logic (suggested by KP) - Expose a single verification function to the verifier (suggested by KP) - Add new patch to remove duplicated function definition - Add new patch to let BPF LSM determine the appropriate register values to use
Roberto Sassu (7): bpf: Remove superfluous btf_id_set_contains() declaration bpf: Mark ALU32 operations in bpf_reg_state structure lsm: Redefine LSM_HOOK() macro to add return value flags as argument bpf-lsm: Enforce return value limitations on security modules selftests/bpf: Check if return values of LSM programs are allowed selftests/bpf: Prevent positive ret values in test_lsm and verify_pkcs7_sig selftests/bpf: Change return value in test_libbpf_get_fd_by_id_opts.c
include/linux/bpf.h | 1 - include/linux/bpf_lsm.h | 11 +- include/linux/bpf_verifier.h | 1 + include/linux/lsm_hook_defs.h | 780 ++++++++++-------- include/linux/lsm_hooks.h | 9 +- kernel/bpf/bpf_lsm.c | 81 +- kernel/bpf/verifier.c | 17 +- security/bpf/hooks.c | 2 +- security/security.c | 4 +- tools/testing/selftests/bpf/progs/lsm.c | 4 + .../bpf/progs/test_libbpf_get_fd_by_id_opts.c | 7 +- .../bpf/progs/test_verify_pkcs7_sig.c | 11 +- .../testing/selftests/bpf/verifier/lsm_ret.c | 148 ++++ 13 files changed, 729 insertions(+), 347 deletions(-) create mode 100644 tools/testing/selftests/bpf/verifier/lsm_ret.c
From: Roberto Sassu roberto.sassu@huawei.com
Commit 8ffa5cc142137 ("bpf: Recognize lock and list fields in allocated objects") moved btf_id_set_contains() and btf_id8_set_contains() implementations from kernel/bpf/verifier.c to include/linux/btf.h.
Thus, remove the declaration in include/linux/bpf.h.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- include/linux/bpf.h | 1 - 1 file changed, 1 deletion(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 4920ac252754..681614f1fa8c 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -2792,7 +2792,6 @@ void *bpf_arch_text_copy(void *dst, void *src, size_t len); int bpf_arch_text_invalidate(void *dst, size_t len);
struct btf_id_set; -bool btf_id_set_contains(const struct btf_id_set *set, u32 id);
#define MAX_BPRINTF_VARARGS 12
From: Roberto Sassu roberto.sassu@huawei.com
BPF LSM needs a reliable source of information to determine if the return value given by eBPF programs is acceptable or not. At the moment, choosing either the 64 bit or the 32 bit one does not seem to be an option (selftests fail).
If we choose the 64 bit one, the following happens.
14: 61 10 00 00 00 00 00 00 r0 = *(u32 *)(r1 + 0) 15: 74 00 00 00 15 00 00 00 w0 >>= 21 16: 54 00 00 00 01 00 00 00 w0 &= 1 17: 04 00 00 00 ff ff ff ff w0 += -1
This is the last part of test_deny_namespace. After #16, the register values are:
smin_value = 0x0, smax_value = 0x1, s32_min_value = 0x0, s32_max_value = 0x1,
After #17, they become:
smin_value = 0x0, smax_value = 0xffffffff, s32_min_value = 0xffffffff, s32_max_value = 0x0
where only the 32 bit values are correct.
If we choose the 32 bit ones, the following happens.
0000000000000000 <check_access>: 0: 79 12 00 00 00 00 00 00 r2 = *(u64 *)(r1 + 0) 1: 79 10 08 00 00 00 00 00 r0 = *(u64 *)(r1 + 8) 2: 67 00 00 00 3e 00 00 00 r0 <<= 62 3: c7 00 00 00 3f 00 00 00 r0 s>>= 63
This is part of test_libbpf_get_fd_by_id_opts (no_alu32 version). In this case, 64 bit register values should be used (for the 32 bit ones, there is no precise information from the verifier).
As the examples above suggest that which register values to use depends on the specific case, mark ALU32 operations in bpf_reg_state structure, so that BPF LSM can choose the proper ones.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- include/linux/bpf_verifier.h | 1 + kernel/bpf/verifier.c | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 70d06a99f0b8..29c9cf6b0d01 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -181,6 +181,7 @@ struct bpf_reg_state { enum bpf_reg_liveness live; /* if (!precise && SCALAR_VALUE) min/max/tnum don't affect safety */ bool precise; + bool alu32; };
enum bpf_stack_slot_type { diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 8c5f0adbbde3..edce85c425a2 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -10524,9 +10524,13 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env, break; }
+ dst_reg->alu32 = false; + /* ALU32 ops are zero extended into 64bit register */ - if (alu32) + if (alu32) { zext_32_to_64(dst_reg); + dst_reg->alu32 = true; + } reg_bounds_sync(dst_reg); return 0; } @@ -10700,6 +10704,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) *dst_reg = *src_reg; dst_reg->live |= REG_LIVE_WRITTEN; dst_reg->subreg_def = DEF_NOT_SUBREG; + dst_reg->alu32 = false; } else { /* R1 = (u32) R2 */ if (is_pointer_value(env, insn->src_reg)) { @@ -10716,6 +10721,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) dst_reg->id = 0; dst_reg->live |= REG_LIVE_WRITTEN; dst_reg->subreg_def = env->insn_idx + 1; + dst_reg->alu32 = true; } else { mark_reg_unknown(env, regs, insn->dst_reg); @@ -10733,9 +10739,11 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) if (BPF_CLASS(insn->code) == BPF_ALU64) { __mark_reg_known(regs + insn->dst_reg, insn->imm); + regs[insn->dst_reg].alu32 = false; } else { __mark_reg_known(regs + insn->dst_reg, (u32)insn->imm); + regs[insn->dst_reg].alu32 = true; } }
On Wed, Dec 7, 2022 at 9:25 AM Roberto Sassu roberto.sassu@huaweicloud.com wrote:
From: Roberto Sassu roberto.sassu@huawei.com
BPF LSM needs a reliable source of information to determine if the return value given by eBPF programs is acceptable or not. At the moment, choosing either the 64 bit or the 32 bit one does not seem to be an option (selftests fail).
If we choose the 64 bit one, the following happens.
14: 61 10 00 00 00 00 00 00 r0 = *(u32 *)(r1 + 0) 15: 74 00 00 00 15 00 00 00 w0 >>= 21 16: 54 00 00 00 01 00 00 00 w0 &= 1 17: 04 00 00 00 ff ff ff ff w0 += -1
This is the last part of test_deny_namespace. After #16, the register values are:
smin_value = 0x0, smax_value = 0x1, s32_min_value = 0x0, s32_max_value = 0x1,
After #17, they become:
smin_value = 0x0, smax_value = 0xffffffff, s32_min_value = 0xffffffff, s32_max_value = 0x0
where only the 32 bit values are correct.
If we choose the 32 bit ones, the following happens.
0000000000000000 <check_access>: 0: 79 12 00 00 00 00 00 00 r2 = *(u64 *)(r1 + 0) 1: 79 10 08 00 00 00 00 00 r0 = *(u64 *)(r1 + 8) 2: 67 00 00 00 3e 00 00 00 r0 <<= 62 3: c7 00 00 00 3f 00 00 00 r0 s>>= 63
This is part of test_libbpf_get_fd_by_id_opts (no_alu32 version). In this case, 64 bit register values should be used (for the 32 bit ones, there is no precise information from the verifier).
As the examples above suggest that which register values to use depends on the specific case, mark ALU32 operations in bpf_reg_state structure, so that BPF LSM can choose the proper ones.
I have a hard time understanding what is the problem you're trying to solve and what is the proposed fix.
The patch is trying to remember the bitness of the last operation, but what for? The registers are 64-bit. There are 32-bit operations, but they always update the upper 32-bits of the register. reg_bounds_sync() updates 32 and 64 bit bounds regardless whether the previous operation was on 32 or 64 bit. It seems you're trying to hack around something that breaks patch 3 which also looks fishy. Please explain the problem first with a concrete example.
On Sat, 2022-12-10 at 18:28 -0800, Alexei Starovoitov wrote:
On Wed, Dec 7, 2022 at 9:25 AM Roberto Sassu roberto.sassu@huaweicloud.com wrote:
From: Roberto Sassu roberto.sassu@huawei.com
BPF LSM needs a reliable source of information to determine if the return value given by eBPF programs is acceptable or not. At the moment, choosing either the 64 bit or the 32 bit one does not seem to be an option (selftests fail).
If we choose the 64 bit one, the following happens.
14: 61 10 00 00 00 00 00 00 r0 = *(u32 *)(r1 + 0) 15: 74 00 00 00 15 00 00 00 w0 >>= 21 16: 54 00 00 00 01 00 00 00 w0 &= 1 17: 04 00 00 00 ff ff ff ff w0 += -1
This is the last part of test_deny_namespace. After #16, the register values are:
smin_value = 0x0, smax_value = 0x1, s32_min_value = 0x0, s32_max_value = 0x1,
After #17, they become:
smin_value = 0x0, smax_value = 0xffffffff, s32_min_value = 0xffffffff, s32_max_value = 0x0
where only the 32 bit values are correct.
If we choose the 32 bit ones, the following happens.
0000000000000000 <check_access>: 0: 79 12 00 00 00 00 00 00 r2 = *(u64 *)(r1 + 0) 1: 79 10 08 00 00 00 00 00 r0 = *(u64 *)(r1 + 8) 2: 67 00 00 00 3e 00 00 00 r0 <<= 62 3: c7 00 00 00 3f 00 00 00 r0 s>>= 63
This is part of test_libbpf_get_fd_by_id_opts (no_alu32 version). In this case, 64 bit register values should be used (for the 32 bit ones, there is no precise information from the verifier).
As the examples above suggest that which register values to use depends on the specific case, mark ALU32 operations in bpf_reg_state structure, so that BPF LSM can choose the proper ones.
I have a hard time understanding what is the problem you're trying to solve and what is the proposed fix.
The problem is allowing BPF LSM programs to return positive values when LSM hooks expect zero or negative values. Those values could be converted to a pointer, and escape the IS_ERR() check.
The challenge is to ensure that the verifier prediction of R0 is accurate, so that the eBPF program is not unnecessarily rejected.
The patch is trying to remember the bitness of the last operation, but what for? The registers are 64-bit. There are 32-bit operations, but they always update the upper 32-bits of the register. reg_bounds_sync() updates 32 and 64 bit bounds regardless whether the previous operation was on 32 or 64 bit.
Ok, yes. I also thought that using the 64 bit register should be ok, but selftests fail.
Regarding your comment, I have not seen reg_bounds_sync() for the case R = imm.
It seems you're trying to hack around something that breaks patch 3 which also looks fishy.
I thought it was a good idea that changes in the LSM infrastructure are automatically reflected in the boundaries that BPF LSM should enforce.
If not, I'm open to new ideas. If we should use BTF ID sets, I'm fine with it.
Please explain the problem first with a concrete example.
Ok, I have a simple one:
$ llvm-objdump -d test_bpf_cookie.bpf.o
0000000000000000 <test_int_hook>:
[...]
8: 85 00 00 00 0e 00 00 00 call 14 9: b4 06 00 00 ff ff ff ff w6 = -1 10: 5e 08 07 00 00 00 00 00 if w8 != w0 goto +7 <LBB11_3> 11: bf 71 00 00 00 00 00 00 r1 = r7 12: 85 00 00 00 ae 00 00 00 call 174 13: 18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll 15: 79 12 00 00 00 00 00 00 r2 = *(u64 *)(r1 + 0) 16: 4f 02 00 00 00 00 00 00 r2 |= r0 17: 7b 21 00 00 00 00 00 00 *(u64 *)(r1 + 0) = r2
smin_value = 0xffffffff, smax_value = 0xffffffff, s32_min_value = 0xffffffff, s32_max_value = 0xffffffff,
This is what I see at the time the BPF LSM check should be done.
How this should be properly handled?
Thanks
Roberto
On Mon, Dec 12, 2022 at 4:45 AM Roberto Sassu roberto.sassu@huaweicloud.com wrote:
On Sat, 2022-12-10 at 18:28 -0800, Alexei Starovoitov wrote:
On Wed, Dec 7, 2022 at 9:25 AM Roberto Sassu roberto.sassu@huaweicloud.com wrote:
From: Roberto Sassu roberto.sassu@huawei.com
BPF LSM needs a reliable source of information to determine if the return value given by eBPF programs is acceptable or not. At the moment, choosing either the 64 bit or the 32 bit one does not seem to be an option (selftests fail).
If we choose the 64 bit one, the following happens.
14: 61 10 00 00 00 00 00 00 r0 = *(u32 *)(r1 + 0) 15: 74 00 00 00 15 00 00 00 w0 >>= 21 16: 54 00 00 00 01 00 00 00 w0 &= 1 17: 04 00 00 00 ff ff ff ff w0 += -1
This is the last part of test_deny_namespace. After #16, the register values are:
smin_value = 0x0, smax_value = 0x1, s32_min_value = 0x0, s32_max_value = 0x1,
After #17, they become:
smin_value = 0x0, smax_value = 0xffffffff, s32_min_value = 0xffffffff, s32_max_value = 0x0
where only the 32 bit values are correct.
If we choose the 32 bit ones, the following happens.
0000000000000000 <check_access>: 0: 79 12 00 00 00 00 00 00 r2 = *(u64 *)(r1 + 0) 1: 79 10 08 00 00 00 00 00 r0 = *(u64 *)(r1 + 8) 2: 67 00 00 00 3e 00 00 00 r0 <<= 62 3: c7 00 00 00 3f 00 00 00 r0 s>>= 63
This is part of test_libbpf_get_fd_by_id_opts (no_alu32 version). In this case, 64 bit register values should be used (for the 32 bit ones, there is no precise information from the verifier).
As the examples above suggest that which register values to use depends on the specific case, mark ALU32 operations in bpf_reg_state structure, so that BPF LSM can choose the proper ones.
I have a hard time understanding what is the problem you're trying to solve and what is the proposed fix.
The problem is allowing BPF LSM programs to return positive values when LSM hooks expect zero or negative values. Those values could be converted to a pointer, and escape the IS_ERR() check.
The bigger goal is clear.
The challenge is to ensure that the verifier prediction of R0 is accurate, so that the eBPF program is not unnecessarily rejected.
There is a code in the verifier already that checks ret values. lsm restrictions should fit right in.
The patch is trying to remember the bitness of the last operation, but what for? The registers are 64-bit. There are 32-bit operations, but they always update the upper 32-bits of the register. reg_bounds_sync() updates 32 and 64 bit bounds regardless whether the previous operation was on 32 or 64 bit.
Ok, yes. I also thought that using the 64 bit register should be ok, but selftests fail.
maybe selftests are buggy? they fail with patch 3 alone without patch 2 ? please explain exactly the problem.
Regarding your comment, I have not seen reg_bounds_sync() for the case R = imm.
because it's unnecessary there.
It seems you're trying to hack around something that breaks patch 3 which also looks fishy.
I thought it was a good idea that changes in the LSM infrastructure are automatically reflected in the boundaries that BPF LSM should enforce.
That's fine. Encoding restrictions in lsm_hook_defs.h is the cleanest approach.
If not, I'm open to new ideas. If we should use BTF ID sets, I'm fine with it.
Please explain the problem first with a concrete example.
Ok, I have a simple one:
$ llvm-objdump -d test_bpf_cookie.bpf.o
0000000000000000 <test_int_hook>:
[...]
8: 85 00 00 00 0e 00 00 00 call 14 9: b4 06 00 00 ff ff ff ff w6 = -1 10: 5e 08 07 00 00 00 00 00 if w8 != w0 goto +7 <LBB11_3> 11: bf 71 00 00 00 00 00 00 r1 = r7 12: 85 00 00 00 ae 00 00 00 call 174 13: 18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll 15: 79 12 00 00 00 00 00 00 r2 = *(u64 *)(r1 + 0) 16: 4f 02 00 00 00 00 00 00 r2 |= r0 17: 7b 21 00 00 00 00 00 00 *(u64 *)(r1 + 0) = r2
smin_value = 0xffffffff, smax_value = 0xffffffff, s32_min_value = 0xffffffff, s32_max_value = 0xffffffff,
and this applies where? what reg are you talking about? Where is the issue?
This is what I see at the time the BPF LSM check should be done.
How this should be properly handled?
The patch 3 should be fine alone. I don't see a need for patch 2 yet.
On Mon, 2022-12-12 at 09:04 -0800, Alexei Starovoitov wrote:
On Mon, Dec 12, 2022 at 4:45 AM Roberto Sassu roberto.sassu@huaweicloud.com wrote:
On Sat, 2022-12-10 at 18:28 -0800, Alexei Starovoitov wrote:
On Wed, Dec 7, 2022 at 9:25 AM Roberto Sassu roberto.sassu@huaweicloud.com wrote:
From: Roberto Sassu roberto.sassu@huawei.com
BPF LSM needs a reliable source of information to determine if the return value given by eBPF programs is acceptable or not. At the moment, choosing either the 64 bit or the 32 bit one does not seem to be an option (selftests fail).
If we choose the 64 bit one, the following happens.
14: 61 10 00 00 00 00 00 00 r0 = *(u32 *)(r1 + 0) 15: 74 00 00 00 15 00 00 00 w0 >>= 21 16: 54 00 00 00 01 00 00 00 w0 &= 1 17: 04 00 00 00 ff ff ff ff w0 += -1
This is the last part of test_deny_namespace. After #16, the register values are:
smin_value = 0x0, smax_value = 0x1, s32_min_value = 0x0, s32_max_value = 0x1,
After #17, they become:
smin_value = 0x0, smax_value = 0xffffffff, s32_min_value = 0xffffffff, s32_max_value = 0x0
where only the 32 bit values are correct.
If we choose the 32 bit ones, the following happens.
0000000000000000 <check_access>: 0: 79 12 00 00 00 00 00 00 r2 = *(u64 *)(r1 + 0) 1: 79 10 08 00 00 00 00 00 r0 = *(u64 *)(r1 + 8) 2: 67 00 00 00 3e 00 00 00 r0 <<= 62 3: c7 00 00 00 3f 00 00 00 r0 s>>= 63
This is part of test_libbpf_get_fd_by_id_opts (no_alu32 version). In this case, 64 bit register values should be used (for the 32 bit ones, there is no precise information from the verifier).
As the examples above suggest that which register values to use depends on the specific case, mark ALU32 operations in bpf_reg_state structure, so that BPF LSM can choose the proper ones.
I have a hard time understanding what is the problem you're trying to solve and what is the proposed fix.
The problem is allowing BPF LSM programs to return positive values when LSM hooks expect zero or negative values. Those values could be converted to a pointer, and escape the IS_ERR() check.
The bigger goal is clear.
The challenge is to ensure that the verifier prediction of R0 is accurate, so that the eBPF program is not unnecessarily rejected.
There is a code in the verifier already that checks ret values. lsm restrictions should fit right in.
The patch is trying to remember the bitness of the last operation, but what for? The registers are 64-bit. There are 32-bit operations, but they always update the upper 32-bits of the register. reg_bounds_sync() updates 32 and 64 bit bounds regardless whether the previous operation was on 32 or 64 bit.
Ok, yes. I also thought that using the 64 bit register should be ok, but selftests fail.
maybe selftests are buggy? they fail with patch 3 alone without patch 2 ? please explain exactly the problem.
Ok, I let it run getting what the verifier provides (smin/smax).
smin_value = 0xffffffff, smax_value = 0xffffffff, s32_min_value = 0xffffffff, s32_max_value = 0xffffffff, Invalid R0, cannot return > 1 #10 bpf_cookie:FAIL
smin_value = 0x0, smax_value = 0xffffffff, s32_min_value = 0xffffffff, s32_max_value = 0x0, Invalid R0, cannot return 1 #58/1 deny_namespace/unpriv_userns_create_no_bpf:FAIL #58 deny_namespace:FAIL
smin_value = 0x0, smax_value = 0xffffffff, s32_min_value = 0xffffffff, s32_max_value = 0x0, Invalid R0, cannot return 1 #100 libbpf_get_fd_by_id_opts:FAIL
smin_value = 0xfffffffe, smax_value = 0xfffffffe, s32_min_value = 0xfffffffe, s32_max_value = 0xfffffffe, #114 lookup_key:FAIL
smin_value = 0xffffffff, smax_value = 0xffffffff, s32_min_value = 0xffffffff, s32_max_value = 0xffffffff, Invalid R0, cannot return > 1 #210 test_ima:FAIL
smin_value = 0xffffffff, smax_value = 0xffffffff, s32_min_value = 0xffffffff, s32_max_value = 0xffffffff, Invalid R0, cannot return > 1 #211 test_local_storage:FAIL
smin_value = 0xffffffff, smax_value = 0xffffffff, s32_min_value = 0xffffffff, s32_max_value = 0xffffffff, Invalid R0, cannot return > 1 #212 test_lsm:FAIL
As you can see, these tests fail because smin or smax are positive values.
I kept the selftest patches. In test_lsm, for example, ret is a parameter, populated by previous eBPF programs. In this case, I added an additional check to explicitly reject positive values.
Regarding your comment, I have not seen reg_bounds_sync() for the case R = imm.
because it's unnecessary there.
__mark_reg_known(regs + insn->dst_reg, (u32)insn->imm);
This prevents smin/smax from being negative. But I know that this was patched by Jann Horn. Remembering the endianness of the operation, makes it clear what register value you should use.
It seems you're trying to hack around something that breaks patch 3 which also looks fishy.
I thought it was a good idea that changes in the LSM infrastructure are automatically reflected in the boundaries that BPF LSM should enforce.
That's fine. Encoding restrictions in lsm_hook_defs.h is the cleanest approach.
If not, I'm open to new ideas. If we should use BTF ID sets, I'm fine with it.
Please explain the problem first with a concrete example.
Ok, I have a simple one:
$ llvm-objdump -d test_bpf_cookie.bpf.o
0000000000000000 <test_int_hook>:
[...]
8: 85 00 00 00 0e 00 00 00 call 14 9: b4 06 00 00 ff ff ff ff w6 = -1 10: 5e 08 07 00 00 00 00 00 if w8 != w0 goto +7 <LBB11_3> 11: bf 71 00 00 00 00 00 00 r1 = r7 12: 85 00 00 00 ae 00 00 00 call 174 13: 18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll 15: 79 12 00 00 00 00 00 00 r2 = *(u64 *)(r1 + 0) 16: 4f 02 00 00 00 00 00 00 r2 |= r0 17: 7b 21 00 00 00 00 00 00 *(u64 *)(r1 + 0) = r2
smin_value = 0xffffffff, smax_value = 0xffffffff, s32_min_value = 0xffffffff, s32_max_value = 0xffffffff,
and this applies where?
This is in check_return_code(), for BPF_PROG_TYPE_LSM.
what reg are you talking about?
R0.
Where is the issue?
s32_min_value/s32_max_value are the values we should get.
Roberto
From: Roberto Sassu roberto.sassu@huawei.com
Define four return value flags (LSM_RET_NEG, LSM_RET_ZERO, LSM_RET_ONE, LSM_RET_GT_ONE), one for each interval of interest (<0, 0, 1, >1).
Redefine the LSM_HOOK() macro to add return value flags as argument, and set the correct flags for each LSM hook.
Implementors of new LSM hooks should do the same as well.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- include/linux/bpf_lsm.h | 2 +- include/linux/lsm_hook_defs.h | 780 ++++++++++++++++++++-------------- include/linux/lsm_hooks.h | 9 +- kernel/bpf/bpf_lsm.c | 5 +- security/bpf/hooks.c | 2 +- security/security.c | 4 +- 6 files changed, 466 insertions(+), 336 deletions(-)
diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h index 1de7ece5d36d..2f5757085dfd 100644 --- a/include/linux/bpf_lsm.h +++ b/include/linux/bpf_lsm.h @@ -13,7 +13,7 @@
#ifdef CONFIG_BPF_LSM
-#define LSM_HOOK(RET, DEFAULT, NAME, ...) \ +#define LSM_HOOK(RET, DEFAULT, RET_FLAGS, NAME, ...) \ RET bpf_lsm_##NAME(__VA_ARGS__); #include <linux/lsm_hook_defs.h> #undef LSM_HOOK diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index 6abde829b6e5..0b1442b26627 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -18,395 +18,519 @@ * The macro LSM_HOOK is used to define the data structures required by * the LSM framework using the pattern: * - * LSM_HOOK(<return_type>, <default_value>, <hook_name>, args...) + * LSM_HOOK(<return_type>, <default_value>, <return_flags>, <hook_name>, args...) * * struct security_hook_heads { - * #define LSM_HOOK(RET, DEFAULT, NAME, ...) struct hlist_head NAME; + * #define LSM_HOOK(RET, DEFAULT, RET_FLAGS, NAME, ...) struct hlist_head NAME; * #include <linux/lsm_hook_defs.h> * #undef LSM_HOOK * }; */ -LSM_HOOK(int, 0, binder_set_context_mgr, const struct cred *mgr) -LSM_HOOK(int, 0, binder_transaction, const struct cred *from, - const struct cred *to) -LSM_HOOK(int, 0, binder_transfer_binder, const struct cred *from, - const struct cred *to) -LSM_HOOK(int, 0, binder_transfer_file, const struct cred *from, - const struct cred *to, struct file *file) -LSM_HOOK(int, 0, ptrace_access_check, struct task_struct *child, - unsigned int mode) -LSM_HOOK(int, 0, ptrace_traceme, struct task_struct *parent) -LSM_HOOK(int, 0, capget, struct task_struct *target, kernel_cap_t *effective, - kernel_cap_t *inheritable, kernel_cap_t *permitted) -LSM_HOOK(int, 0, capset, struct cred *new, const struct cred *old, - const kernel_cap_t *effective, const kernel_cap_t *inheritable, - const kernel_cap_t *permitted) -LSM_HOOK(int, 0, capable, const struct cred *cred, struct user_namespace *ns, - int cap, unsigned int opts) -LSM_HOOK(int, 0, quotactl, int cmds, int type, int id, struct super_block *sb) -LSM_HOOK(int, 0, quota_on, struct dentry *dentry) -LSM_HOOK(int, 0, syslog, int type) -LSM_HOOK(int, 0, settime, const struct timespec64 *ts, - const struct timezone *tz) -LSM_HOOK(int, 0, vm_enough_memory, struct mm_struct *mm, long pages) -LSM_HOOK(int, 0, bprm_creds_for_exec, struct linux_binprm *bprm) -LSM_HOOK(int, 0, bprm_creds_from_file, struct linux_binprm *bprm, struct file *file) -LSM_HOOK(int, 0, bprm_check_security, struct linux_binprm *bprm) -LSM_HOOK(void, LSM_RET_VOID, bprm_committing_creds, struct linux_binprm *bprm) -LSM_HOOK(void, LSM_RET_VOID, bprm_committed_creds, struct linux_binprm *bprm) -LSM_HOOK(int, 0, fs_context_dup, struct fs_context *fc, - struct fs_context *src_sc) -LSM_HOOK(int, -ENOPARAM, fs_context_parse_param, struct fs_context *fc, - struct fs_parameter *param) -LSM_HOOK(int, 0, sb_alloc_security, struct super_block *sb) -LSM_HOOK(void, LSM_RET_VOID, sb_delete, struct super_block *sb) -LSM_HOOK(void, LSM_RET_VOID, sb_free_security, struct super_block *sb) -LSM_HOOK(void, LSM_RET_VOID, sb_free_mnt_opts, void *mnt_opts) -LSM_HOOK(int, 0, sb_eat_lsm_opts, char *orig, void **mnt_opts) -LSM_HOOK(int, 0, sb_mnt_opts_compat, struct super_block *sb, void *mnt_opts) -LSM_HOOK(int, 0, sb_remount, struct super_block *sb, void *mnt_opts) -LSM_HOOK(int, 0, sb_kern_mount, struct super_block *sb) -LSM_HOOK(int, 0, sb_show_options, struct seq_file *m, struct super_block *sb) -LSM_HOOK(int, 0, sb_statfs, struct dentry *dentry) -LSM_HOOK(int, 0, sb_mount, const char *dev_name, const struct path *path, - const char *type, unsigned long flags, void *data) -LSM_HOOK(int, 0, sb_umount, struct vfsmount *mnt, int flags) -LSM_HOOK(int, 0, sb_pivotroot, const struct path *old_path, - const struct path *new_path) -LSM_HOOK(int, 0, sb_set_mnt_opts, struct super_block *sb, void *mnt_opts, - unsigned long kern_flags, unsigned long *set_kern_flags) -LSM_HOOK(int, 0, sb_clone_mnt_opts, const struct super_block *oldsb, - struct super_block *newsb, unsigned long kern_flags, + +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, binder_set_context_mgr, + const struct cred *mgr) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, binder_transaction, + const struct cred *from, const struct cred *to) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, binder_transfer_binder, + const struct cred *from, const struct cred *to) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, binder_transfer_file, + const struct cred *from, const struct cred *to, struct file *file) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, ptrace_access_check, + struct task_struct *child, unsigned int mode) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, ptrace_traceme, + struct task_struct *parent) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, capget, struct task_struct *target, + kernel_cap_t *effective, kernel_cap_t *inheritable, + kernel_cap_t *permitted) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, capset, struct cred *new, + const struct cred *old, const kernel_cap_t *effective, + const kernel_cap_t *inheritable, const kernel_cap_t *permitted) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, capable, const struct cred *cred, + struct user_namespace *ns, int cap, unsigned int opts) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, quotactl, int cmds, int type, + int id, struct super_block *sb) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, quota_on, struct dentry *dentry) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, syslog, int type) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, settime, + const struct timespec64 *ts, const struct timezone *tz) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO | LSM_RET_ONE | LSM_RET_GT_ONE, + vm_enough_memory, struct mm_struct *mm, long pages) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, bprm_creds_for_exec, + struct linux_binprm *bprm) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, bprm_creds_from_file, + struct linux_binprm *bprm, struct file *file) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, bprm_check_security, + struct linux_binprm *bprm) +LSM_HOOK(void, LSM_RET_VOID, 0, bprm_committing_creds, struct linux_binprm *bprm) +LSM_HOOK(void, LSM_RET_VOID, 0, bprm_committed_creds, struct linux_binprm *bprm) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, fs_context_dup, + struct fs_context *fc, struct fs_context *src_sc) +LSM_HOOK(int, -ENOPARAM, LSM_RET_NEG | LSM_RET_ZERO, fs_context_parse_param, + struct fs_context *fc, struct fs_parameter *param) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, sb_alloc_security, + struct super_block *sb) +LSM_HOOK(void, LSM_RET_VOID, 0, sb_delete, struct super_block *sb) +LSM_HOOK(void, LSM_RET_VOID, 0, sb_free_security, struct super_block *sb) +LSM_HOOK(void, LSM_RET_VOID, 0, sb_free_mnt_opts, void *mnt_opts) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, sb_eat_lsm_opts, char *orig, + void **mnt_opts) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, sb_mnt_opts_compat, + struct super_block *sb, void *mnt_opts) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, sb_remount, struct super_block *sb, + void *mnt_opts) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, sb_kern_mount, + struct super_block *sb) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, sb_show_options, + struct seq_file *m, struct super_block *sb) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, sb_statfs, struct dentry *dentry) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, sb_mount, const char *dev_name, + const struct path *path, const char *type, unsigned long flags, + void *data) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, sb_umount, struct vfsmount *mnt, + int flags) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, sb_pivotroot, + const struct path *old_path, const struct path *new_path) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, sb_set_mnt_opts, + struct super_block *sb, void *mnt_opts, unsigned long kern_flags, unsigned long *set_kern_flags) -LSM_HOOK(int, 0, move_mount, const struct path *from_path, - const struct path *to_path) -LSM_HOOK(int, -EOPNOTSUPP, dentry_init_security, struct dentry *dentry, - int mode, const struct qstr *name, const char **xattr_name, - void **ctx, u32 *ctxlen) -LSM_HOOK(int, 0, dentry_create_files_as, struct dentry *dentry, int mode, - struct qstr *name, const struct cred *old, struct cred *new) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, sb_clone_mnt_opts, + const struct super_block *oldsb, struct super_block *newsb, + unsigned long kern_flags, unsigned long *set_kern_flags) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, move_mount, + const struct path *from_path, const struct path *to_path) +LSM_HOOK(int, -EOPNOTSUPP, LSM_RET_NEG | LSM_RET_ZERO, dentry_init_security, + struct dentry *dentry, int mode, const struct qstr *name, + const char **xattr_name, void **ctx, u32 *ctxlen) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, dentry_create_files_as, + struct dentry *dentry, int mode, struct qstr *name, + const struct cred *old, struct cred *new)
#ifdef CONFIG_SECURITY_PATH -LSM_HOOK(int, 0, path_unlink, const struct path *dir, struct dentry *dentry) -LSM_HOOK(int, 0, path_mkdir, const struct path *dir, struct dentry *dentry, - umode_t mode) -LSM_HOOK(int, 0, path_rmdir, const struct path *dir, struct dentry *dentry) -LSM_HOOK(int, 0, path_mknod, const struct path *dir, struct dentry *dentry, - umode_t mode, unsigned int dev) -LSM_HOOK(int, 0, path_truncate, const struct path *path) -LSM_HOOK(int, 0, path_symlink, const struct path *dir, struct dentry *dentry, - const char *old_name) -LSM_HOOK(int, 0, path_link, struct dentry *old_dentry, - const struct path *new_dir, struct dentry *new_dentry) -LSM_HOOK(int, 0, path_rename, const struct path *old_dir, +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, path_unlink, + const struct path *dir, struct dentry *dentry) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, path_mkdir, const struct path *dir, + struct dentry *dentry, umode_t mode) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, path_rmdir, const struct path *dir, + struct dentry *dentry) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, path_mknod, const struct path *dir, + struct dentry *dentry, umode_t mode, unsigned int dev) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, path_truncate, + const struct path *path) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, path_symlink, + const struct path *dir, struct dentry *dentry, const char *old_name) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, path_link, struct dentry *old_dentry, const struct path *new_dir, - struct dentry *new_dentry, unsigned int flags) -LSM_HOOK(int, 0, path_chmod, const struct path *path, umode_t mode) -LSM_HOOK(int, 0, path_chown, const struct path *path, kuid_t uid, kgid_t gid) -LSM_HOOK(int, 0, path_chroot, const struct path *path) + struct dentry *new_dentry) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, path_rename, + const struct path *old_dir, struct dentry *old_dentry, + const struct path *new_dir, struct dentry *new_dentry, + unsigned int flags) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, path_chmod, + const struct path *path, umode_t mode) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, path_chown, + const struct path *path, kuid_t uid, kgid_t gid) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, path_chroot, + const struct path *path) #endif /* CONFIG_SECURITY_PATH */
/* Needed for inode based security check */ -LSM_HOOK(int, 0, path_notify, const struct path *path, u64 mask, - unsigned int obj_type) -LSM_HOOK(int, 0, inode_alloc_security, struct inode *inode) -LSM_HOOK(void, LSM_RET_VOID, inode_free_security, struct inode *inode) -LSM_HOOK(int, 0, inode_init_security, struct inode *inode, - struct inode *dir, const struct qstr *qstr, const char **name, - void **value, size_t *len) -LSM_HOOK(int, 0, inode_init_security_anon, struct inode *inode, - const struct qstr *name, const struct inode *context_inode) -LSM_HOOK(int, 0, inode_create, struct inode *dir, struct dentry *dentry, - umode_t mode) -LSM_HOOK(int, 0, inode_link, struct dentry *old_dentry, struct inode *dir, +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, path_notify, + const struct path *path, u64 mask, unsigned int obj_type) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, inode_alloc_security, + struct inode *inode) +LSM_HOOK(void, LSM_RET_VOID, 0, inode_free_security, struct inode *inode) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, inode_init_security, + struct inode *inode, struct inode *dir, const struct qstr *qstr, + const char **name, void **value, size_t *len) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, inode_init_security_anon, + struct inode *inode, const struct qstr *name, + const struct inode *context_inode) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, inode_create, struct inode *dir, + struct dentry *dentry, umode_t mode) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, inode_link, + struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) -LSM_HOOK(int, 0, inode_unlink, struct inode *dir, struct dentry *dentry) -LSM_HOOK(int, 0, inode_symlink, struct inode *dir, struct dentry *dentry, - const char *old_name) -LSM_HOOK(int, 0, inode_mkdir, struct inode *dir, struct dentry *dentry, - umode_t mode) -LSM_HOOK(int, 0, inode_rmdir, struct inode *dir, struct dentry *dentry) -LSM_HOOK(int, 0, inode_mknod, struct inode *dir, struct dentry *dentry, - umode_t mode, dev_t dev) -LSM_HOOK(int, 0, inode_rename, struct inode *old_dir, struct dentry *old_dentry, +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, inode_unlink, struct inode *dir, + struct dentry *dentry) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, inode_symlink, struct inode *dir, + struct dentry *dentry, const char *old_name) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, inode_mkdir, struct inode *dir, + struct dentry *dentry, umode_t mode) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, inode_rmdir, struct inode *dir, + struct dentry *dentry) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, inode_mknod, struct inode *dir, + struct dentry *dentry, umode_t mode, dev_t dev) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, inode_rename, + struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) -LSM_HOOK(int, 0, inode_readlink, struct dentry *dentry) -LSM_HOOK(int, 0, inode_follow_link, struct dentry *dentry, struct inode *inode, - bool rcu) -LSM_HOOK(int, 0, inode_permission, struct inode *inode, int mask) -LSM_HOOK(int, 0, inode_setattr, struct dentry *dentry, struct iattr *attr) -LSM_HOOK(int, 0, inode_getattr, const struct path *path) -LSM_HOOK(int, 0, inode_setxattr, struct user_namespace *mnt_userns, - struct dentry *dentry, const char *name, const void *value, - size_t size, int flags) -LSM_HOOK(void, LSM_RET_VOID, inode_post_setxattr, struct dentry *dentry, +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, inode_readlink, + struct dentry *dentry) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, inode_follow_link, + struct dentry *dentry, struct inode *inode, bool rcu) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, inode_permission, + struct inode *inode, int mask) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, inode_setattr, + struct dentry *dentry, struct iattr *attr) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, inode_getattr, + const struct path *path) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, inode_setxattr, + struct user_namespace *mnt_userns, struct dentry *dentry, + const char *name, const void *value, size_t size, int flags) +LSM_HOOK(void, LSM_RET_VOID, 0, inode_post_setxattr, struct dentry *dentry, const char *name, const void *value, size_t size, int flags) -LSM_HOOK(int, 0, inode_getxattr, struct dentry *dentry, const char *name) -LSM_HOOK(int, 0, inode_listxattr, struct dentry *dentry) -LSM_HOOK(int, 0, inode_removexattr, struct user_namespace *mnt_userns, +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, inode_getxattr, struct dentry *dentry, const char *name) -LSM_HOOK(int, 0, inode_need_killpriv, struct dentry *dentry) -LSM_HOOK(int, 0, inode_killpriv, struct user_namespace *mnt_userns, +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, inode_listxattr, struct dentry *dentry) -LSM_HOOK(int, -EOPNOTSUPP, inode_getsecurity, struct user_namespace *mnt_userns, +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, inode_removexattr, + struct user_namespace *mnt_userns, struct dentry *dentry, + const char *name) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO | LSM_RET_ONE | LSM_RET_GT_ONE, + inode_need_killpriv, struct dentry *dentry) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, inode_killpriv, + struct user_namespace *mnt_userns, struct dentry *dentry) +LSM_HOOK(int, -EOPNOTSUPP, + LSM_RET_NEG | LSM_RET_ZERO | LSM_RET_ONE | LSM_RET_GT_ONE, + inode_getsecurity, struct user_namespace *mnt_userns, struct inode *inode, const char *name, void **buffer, bool alloc) -LSM_HOOK(int, -EOPNOTSUPP, inode_setsecurity, struct inode *inode, - const char *name, const void *value, size_t size, int flags) -LSM_HOOK(int, 0, inode_listsecurity, struct inode *inode, char *buffer, +LSM_HOOK(int, -EOPNOTSUPP, LSM_RET_NEG | LSM_RET_ZERO, inode_setsecurity, + struct inode *inode, const char *name, const void *value, size_t size, + int flags) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO | LSM_RET_ONE | LSM_RET_GT_ONE, + inode_listsecurity, struct inode *inode, char *buffer, size_t buffer_size) -LSM_HOOK(void, LSM_RET_VOID, inode_getsecid, struct inode *inode, u32 *secid) -LSM_HOOK(int, 0, inode_copy_up, struct dentry *src, struct cred **new) -LSM_HOOK(int, -EOPNOTSUPP, inode_copy_up_xattr, const char *name) -LSM_HOOK(int, 0, kernfs_init_security, struct kernfs_node *kn_dir, - struct kernfs_node *kn) -LSM_HOOK(int, 0, file_permission, struct file *file, int mask) -LSM_HOOK(int, 0, file_alloc_security, struct file *file) -LSM_HOOK(void, LSM_RET_VOID, file_free_security, struct file *file) -LSM_HOOK(int, 0, file_ioctl, struct file *file, unsigned int cmd, - unsigned long arg) -LSM_HOOK(int, 0, mmap_addr, unsigned long addr) -LSM_HOOK(int, 0, mmap_file, struct file *file, unsigned long reqprot, - unsigned long prot, unsigned long flags) -LSM_HOOK(int, 0, file_mprotect, struct vm_area_struct *vma, - unsigned long reqprot, unsigned long prot) -LSM_HOOK(int, 0, file_lock, struct file *file, unsigned int cmd) -LSM_HOOK(int, 0, file_fcntl, struct file *file, unsigned int cmd, - unsigned long arg) -LSM_HOOK(void, LSM_RET_VOID, file_set_fowner, struct file *file) -LSM_HOOK(int, 0, file_send_sigiotask, struct task_struct *tsk, - struct fown_struct *fown, int sig) -LSM_HOOK(int, 0, file_receive, struct file *file) -LSM_HOOK(int, 0, file_open, struct file *file) -LSM_HOOK(int, 0, task_alloc, struct task_struct *task, - unsigned long clone_flags) -LSM_HOOK(void, LSM_RET_VOID, task_free, struct task_struct *task) -LSM_HOOK(int, 0, cred_alloc_blank, struct cred *cred, gfp_t gfp) -LSM_HOOK(void, LSM_RET_VOID, cred_free, struct cred *cred) -LSM_HOOK(int, 0, cred_prepare, struct cred *new, const struct cred *old, - gfp_t gfp) -LSM_HOOK(void, LSM_RET_VOID, cred_transfer, struct cred *new, +LSM_HOOK(void, LSM_RET_VOID, 0, inode_getsecid, struct inode *inode, u32 *secid) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, inode_copy_up, struct dentry *src, + struct cred **new) +LSM_HOOK(int, -EOPNOTSUPP, LSM_RET_NEG | LSM_RET_ZERO | LSM_RET_ONE, + inode_copy_up_xattr, const char *name) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, kernfs_init_security, + struct kernfs_node *kn_dir, struct kernfs_node *kn) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, file_permission, struct file *file, + int mask) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, file_alloc_security, + struct file *file) +LSM_HOOK(void, LSM_RET_VOID, 0, file_free_security, struct file *file) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, file_ioctl, struct file *file, + unsigned int cmd, unsigned long arg) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, mmap_addr, unsigned long addr) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, mmap_file, struct file *file, + unsigned long reqprot, unsigned long prot, unsigned long flags) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, file_mprotect, + struct vm_area_struct *vma, unsigned long reqprot, unsigned long prot) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, file_lock, struct file *file, + unsigned int cmd) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, file_fcntl, struct file *file, + unsigned int cmd, unsigned long arg) +LSM_HOOK(void, LSM_RET_VOID, 0, file_set_fowner, struct file *file) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, file_send_sigiotask, + struct task_struct *tsk, struct fown_struct *fown, int sig) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, file_receive, struct file *file) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, file_open, struct file *file) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, task_alloc, + struct task_struct *task, unsigned long clone_flags) +LSM_HOOK(void, LSM_RET_VOID, 0, task_free, struct task_struct *task) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, cred_alloc_blank, + struct cred *cred, gfp_t gfp) +LSM_HOOK(void, LSM_RET_VOID, 0, cred_free, struct cred *cred) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, cred_prepare, struct cred *new, + const struct cred *old, gfp_t gfp) +LSM_HOOK(void, LSM_RET_VOID, 0, cred_transfer, struct cred *new, const struct cred *old) -LSM_HOOK(void, LSM_RET_VOID, cred_getsecid, const struct cred *c, u32 *secid) -LSM_HOOK(int, 0, kernel_act_as, struct cred *new, u32 secid) -LSM_HOOK(int, 0, kernel_create_files_as, struct cred *new, struct inode *inode) -LSM_HOOK(int, 0, kernel_module_request, char *kmod_name) -LSM_HOOK(int, 0, kernel_load_data, enum kernel_load_data_id id, bool contents) -LSM_HOOK(int, 0, kernel_post_load_data, char *buf, loff_t size, - enum kernel_load_data_id id, char *description) -LSM_HOOK(int, 0, kernel_read_file, struct file *file, - enum kernel_read_file_id id, bool contents) -LSM_HOOK(int, 0, kernel_post_read_file, struct file *file, char *buf, - loff_t size, enum kernel_read_file_id id) -LSM_HOOK(int, 0, task_fix_setuid, struct cred *new, const struct cred *old, - int flags) -LSM_HOOK(int, 0, task_fix_setgid, struct cred *new, const struct cred * old, - int flags) -LSM_HOOK(int, 0, task_fix_setgroups, struct cred *new, const struct cred * old) -LSM_HOOK(int, 0, task_setpgid, struct task_struct *p, pid_t pgid) -LSM_HOOK(int, 0, task_getpgid, struct task_struct *p) -LSM_HOOK(int, 0, task_getsid, struct task_struct *p) -LSM_HOOK(void, LSM_RET_VOID, current_getsecid_subj, u32 *secid) -LSM_HOOK(void, LSM_RET_VOID, task_getsecid_obj, +LSM_HOOK(void, LSM_RET_VOID, 0, cred_getsecid, const struct cred *c, u32 *secid) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, kernel_act_as, struct cred *new, + u32 secid) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, kernel_create_files_as, + struct cred *new, struct inode *inode) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, kernel_module_request, + char *kmod_name) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, kernel_load_data, + enum kernel_load_data_id id, bool contents) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, kernel_post_load_data, char *buf, + loff_t size, enum kernel_load_data_id id, char *description) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, kernel_read_file, + struct file *file, enum kernel_read_file_id id, bool contents) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, kernel_post_read_file, + struct file *file, char *buf, loff_t size, enum kernel_read_file_id id) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, task_fix_setuid, struct cred *new, + const struct cred *old, int flags) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, task_fix_setgid, struct cred *new, + const struct cred *old, int flags) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, task_fix_setgroups, + struct cred *new, const struct cred *old) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, task_setpgid, + struct task_struct *p, pid_t pgid) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, task_getpgid, + struct task_struct *p) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, task_getsid, struct task_struct *p) +LSM_HOOK(void, LSM_RET_VOID, 0, current_getsecid_subj, u32 *secid) +LSM_HOOK(void, LSM_RET_VOID, 0, task_getsecid_obj, struct task_struct *p, u32 *secid) -LSM_HOOK(int, 0, task_setnice, struct task_struct *p, int nice) -LSM_HOOK(int, 0, task_setioprio, struct task_struct *p, int ioprio) -LSM_HOOK(int, 0, task_getioprio, struct task_struct *p) -LSM_HOOK(int, 0, task_prlimit, const struct cred *cred, - const struct cred *tcred, unsigned int flags) -LSM_HOOK(int, 0, task_setrlimit, struct task_struct *p, unsigned int resource, - struct rlimit *new_rlim) -LSM_HOOK(int, 0, task_setscheduler, struct task_struct *p) -LSM_HOOK(int, 0, task_getscheduler, struct task_struct *p) -LSM_HOOK(int, 0, task_movememory, struct task_struct *p) -LSM_HOOK(int, 0, task_kill, struct task_struct *p, struct kernel_siginfo *info, - int sig, const struct cred *cred) -LSM_HOOK(int, -ENOSYS, task_prctl, int option, unsigned long arg2, - unsigned long arg3, unsigned long arg4, unsigned long arg5) -LSM_HOOK(void, LSM_RET_VOID, task_to_inode, struct task_struct *p, +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, task_setnice, + struct task_struct *p, int nice) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, task_setioprio, + struct task_struct *p, int ioprio) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, task_getioprio, + struct task_struct *p) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, task_prlimit, + const struct cred *cred, const struct cred *tcred, unsigned int flags) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, task_setrlimit, + struct task_struct *p, unsigned int resource, struct rlimit *new_rlim) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, task_setscheduler, + struct task_struct *p) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, task_getscheduler, + struct task_struct *p) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, task_movememory, + struct task_struct *p) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, task_kill, struct task_struct *p, + struct kernel_siginfo *info, int sig, const struct cred *cred) +LSM_HOOK(int, -ENOSYS, + LSM_RET_NEG | LSM_RET_ZERO | LSM_RET_ONE | LSM_RET_GT_ONE, task_prctl, + int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, + unsigned long arg5) +LSM_HOOK(void, LSM_RET_VOID, 0, task_to_inode, struct task_struct *p, struct inode *inode) -LSM_HOOK(int, 0, userns_create, const struct cred *cred) -LSM_HOOK(int, 0, ipc_permission, struct kern_ipc_perm *ipcp, short flag) -LSM_HOOK(void, LSM_RET_VOID, ipc_getsecid, struct kern_ipc_perm *ipcp, +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, userns_create, + const struct cred *cred) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, ipc_permission, + struct kern_ipc_perm *ipcp, short flag) +LSM_HOOK(void, LSM_RET_VOID, 0, ipc_getsecid, struct kern_ipc_perm *ipcp, u32 *secid) -LSM_HOOK(int, 0, msg_msg_alloc_security, struct msg_msg *msg) -LSM_HOOK(void, LSM_RET_VOID, msg_msg_free_security, struct msg_msg *msg) -LSM_HOOK(int, 0, msg_queue_alloc_security, struct kern_ipc_perm *perm) -LSM_HOOK(void, LSM_RET_VOID, msg_queue_free_security, +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, msg_msg_alloc_security, + struct msg_msg *msg) +LSM_HOOK(void, LSM_RET_VOID, 0, msg_msg_free_security, struct msg_msg *msg) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, msg_queue_alloc_security, + struct kern_ipc_perm *perm) +LSM_HOOK(void, LSM_RET_VOID, 0, msg_queue_free_security, + struct kern_ipc_perm *perm) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, msg_queue_associate, + struct kern_ipc_perm *perm, int msqflg) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, msg_queue_msgctl, + struct kern_ipc_perm *perm, int cmd) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, msg_queue_msgsnd, + struct kern_ipc_perm *perm, struct msg_msg *msg, int msqflg) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, msg_queue_msgrcv, + struct kern_ipc_perm *perm, struct msg_msg *msg, + struct task_struct *target, long type, int mode) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, shm_alloc_security, struct kern_ipc_perm *perm) -LSM_HOOK(int, 0, msg_queue_associate, struct kern_ipc_perm *perm, int msqflg) -LSM_HOOK(int, 0, msg_queue_msgctl, struct kern_ipc_perm *perm, int cmd) -LSM_HOOK(int, 0, msg_queue_msgsnd, struct kern_ipc_perm *perm, - struct msg_msg *msg, int msqflg) -LSM_HOOK(int, 0, msg_queue_msgrcv, struct kern_ipc_perm *perm, - struct msg_msg *msg, struct task_struct *target, long type, int mode) -LSM_HOOK(int, 0, shm_alloc_security, struct kern_ipc_perm *perm) -LSM_HOOK(void, LSM_RET_VOID, shm_free_security, struct kern_ipc_perm *perm) -LSM_HOOK(int, 0, shm_associate, struct kern_ipc_perm *perm, int shmflg) -LSM_HOOK(int, 0, shm_shmctl, struct kern_ipc_perm *perm, int cmd) -LSM_HOOK(int, 0, shm_shmat, struct kern_ipc_perm *perm, char __user *shmaddr, - int shmflg) -LSM_HOOK(int, 0, sem_alloc_security, struct kern_ipc_perm *perm) -LSM_HOOK(void, LSM_RET_VOID, sem_free_security, struct kern_ipc_perm *perm) -LSM_HOOK(int, 0, sem_associate, struct kern_ipc_perm *perm, int semflg) -LSM_HOOK(int, 0, sem_semctl, struct kern_ipc_perm *perm, int cmd) -LSM_HOOK(int, 0, sem_semop, struct kern_ipc_perm *perm, struct sembuf *sops, - unsigned nsops, int alter) -LSM_HOOK(int, 0, netlink_send, struct sock *sk, struct sk_buff *skb) -LSM_HOOK(void, LSM_RET_VOID, d_instantiate, struct dentry *dentry, +LSM_HOOK(void, LSM_RET_VOID, 0, shm_free_security, struct kern_ipc_perm *perm) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, shm_associate, + struct kern_ipc_perm *perm, int shmflg) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, shm_shmctl, + struct kern_ipc_perm *perm, int cmd) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, shm_shmat, + struct kern_ipc_perm *perm, char __user *shmaddr, int shmflg) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, sem_alloc_security, + struct kern_ipc_perm *perm) +LSM_HOOK(void, LSM_RET_VOID, 0, sem_free_security, struct kern_ipc_perm *perm) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, sem_associate, + struct kern_ipc_perm *perm, int semflg) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, sem_semctl, + struct kern_ipc_perm *perm, int cmd) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, sem_semop, + struct kern_ipc_perm *perm, struct sembuf *sops, unsigned nsops, + int alter) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, netlink_send, struct sock *sk, + struct sk_buff *skb) +LSM_HOOK(void, LSM_RET_VOID, 0, d_instantiate, struct dentry *dentry, struct inode *inode) -LSM_HOOK(int, -EINVAL, getprocattr, struct task_struct *p, const char *name, - char **value) -LSM_HOOK(int, -EINVAL, setprocattr, const char *name, void *value, size_t size) -LSM_HOOK(int, 0, ismaclabel, const char *name) -LSM_HOOK(int, -EOPNOTSUPP, secid_to_secctx, u32 secid, char **secdata, - u32 *seclen) -LSM_HOOK(int, 0, secctx_to_secid, const char *secdata, u32 seclen, u32 *secid) -LSM_HOOK(void, LSM_RET_VOID, release_secctx, char *secdata, u32 seclen) -LSM_HOOK(void, LSM_RET_VOID, inode_invalidate_secctx, struct inode *inode) -LSM_HOOK(int, 0, inode_notifysecctx, struct inode *inode, void *ctx, u32 ctxlen) -LSM_HOOK(int, 0, inode_setsecctx, struct dentry *dentry, void *ctx, u32 ctxlen) -LSM_HOOK(int, 0, inode_getsecctx, struct inode *inode, void **ctx, - u32 *ctxlen) +LSM_HOOK(int, -EINVAL, + LSM_RET_NEG | LSM_RET_ZERO | LSM_RET_ONE | LSM_RET_GT_ONE, getprocattr, + struct task_struct *p, const char *name, char **value) +LSM_HOOK(int, -EINVAL, + LSM_RET_NEG | LSM_RET_ZERO | LSM_RET_ONE | LSM_RET_GT_ONE, setprocattr, + const char *name, void *value, size_t size) +LSM_HOOK(int, 0, LSM_RET_ZERO | LSM_RET_ONE, ismaclabel, const char *name) +LSM_HOOK(int, -EOPNOTSUPP, LSM_RET_NEG | LSM_RET_ZERO, secid_to_secctx, + u32 secid, char **secdata, u32 *seclen) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, secctx_to_secid, + const char *secdata, u32 seclen, u32 *secid) +LSM_HOOK(void, LSM_RET_VOID, 0, release_secctx, char *secdata, u32 seclen) +LSM_HOOK(void, LSM_RET_VOID, 0, inode_invalidate_secctx, struct inode *inode) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, inode_notifysecctx, + struct inode *inode, void *ctx, u32 ctxlen) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, inode_setsecctx, + struct dentry *dentry, void *ctx, u32 ctxlen) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, inode_getsecctx, + struct inode *inode, void **ctx, u32 *ctxlen)
#if defined(CONFIG_SECURITY) && defined(CONFIG_WATCH_QUEUE) -LSM_HOOK(int, 0, post_notification, const struct cred *w_cred, - const struct cred *cred, struct watch_notification *n) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, post_notification, + const struct cred *w_cred, const struct cred *cred, + struct watch_notification *n) #endif /* CONFIG_SECURITY && CONFIG_WATCH_QUEUE */
#if defined(CONFIG_SECURITY) && defined(CONFIG_KEY_NOTIFICATIONS) -LSM_HOOK(int, 0, watch_key, struct key *key) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, watch_key, struct key *key) #endif /* CONFIG_SECURITY && CONFIG_KEY_NOTIFICATIONS */
#ifdef CONFIG_SECURITY_NETWORK -LSM_HOOK(int, 0, unix_stream_connect, struct sock *sock, struct sock *other, - struct sock *newsk) -LSM_HOOK(int, 0, unix_may_send, struct socket *sock, struct socket *other) -LSM_HOOK(int, 0, socket_create, int family, int type, int protocol, int kern) -LSM_HOOK(int, 0, socket_post_create, struct socket *sock, int family, int type, - int protocol, int kern) -LSM_HOOK(int, 0, socket_socketpair, struct socket *socka, struct socket *sockb) -LSM_HOOK(int, 0, socket_bind, struct socket *sock, struct sockaddr *address, - int addrlen) -LSM_HOOK(int, 0, socket_connect, struct socket *sock, struct sockaddr *address, - int addrlen) -LSM_HOOK(int, 0, socket_listen, struct socket *sock, int backlog) -LSM_HOOK(int, 0, socket_accept, struct socket *sock, struct socket *newsock) -LSM_HOOK(int, 0, socket_sendmsg, struct socket *sock, struct msghdr *msg, - int size) -LSM_HOOK(int, 0, socket_recvmsg, struct socket *sock, struct msghdr *msg, - int size, int flags) -LSM_HOOK(int, 0, socket_getsockname, struct socket *sock) -LSM_HOOK(int, 0, socket_getpeername, struct socket *sock) -LSM_HOOK(int, 0, socket_getsockopt, struct socket *sock, int level, int optname) -LSM_HOOK(int, 0, socket_setsockopt, struct socket *sock, int level, int optname) -LSM_HOOK(int, 0, socket_shutdown, struct socket *sock, int how) -LSM_HOOK(int, 0, socket_sock_rcv_skb, struct sock *sk, struct sk_buff *skb) -LSM_HOOK(int, 0, socket_getpeersec_stream, struct socket *sock, - sockptr_t optval, sockptr_t optlen, unsigned int len) -LSM_HOOK(int, 0, socket_getpeersec_dgram, struct socket *sock, - struct sk_buff *skb, u32 *secid) -LSM_HOOK(int, 0, sk_alloc_security, struct sock *sk, int family, gfp_t priority) -LSM_HOOK(void, LSM_RET_VOID, sk_free_security, struct sock *sk) -LSM_HOOK(void, LSM_RET_VOID, sk_clone_security, const struct sock *sk, +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, unix_stream_connect, + struct sock *sock, struct sock *other, struct sock *newsk) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, unix_may_send, struct socket *sock, + struct socket *other) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, socket_create, int family, + int type, int protocol, int kern) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, socket_post_create, + struct socket *sock, int family, int type, int protocol, int kern) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, socket_socketpair, + struct socket *socka, struct socket *sockb) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, socket_bind, struct socket *sock, + struct sockaddr *address, int addrlen) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, socket_connect, + struct socket *sock, struct sockaddr *address, int addrlen) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, socket_listen, struct socket *sock, + int backlog) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, socket_accept, struct socket *sock, + struct socket *newsock) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, socket_sendmsg, + struct socket *sock, struct msghdr *msg, int size) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, socket_recvmsg, + struct socket *sock, struct msghdr *msg, int size, int flags) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, socket_getsockname, + struct socket *sock) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, socket_getpeername, + struct socket *sock) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, socket_getsockopt, + struct socket *sock, int level, int optname) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, socket_setsockopt, + struct socket *sock, int level, int optname) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, socket_shutdown, + struct socket *sock, int how) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, socket_sock_rcv_skb, + struct sock *sk, struct sk_buff *skb) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, socket_getpeersec_stream, + struct socket *sock, sockptr_t optval, sockptr_t optlen, + unsigned int len) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, socket_getpeersec_dgram, + struct socket *sock, struct sk_buff *skb, u32 *secid) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, sk_alloc_security, struct sock *sk, + int family, gfp_t priority) +LSM_HOOK(void, LSM_RET_VOID, 0, sk_free_security, struct sock *sk) +LSM_HOOK(void, LSM_RET_VOID, 0, sk_clone_security, const struct sock *sk, struct sock *newsk) -LSM_HOOK(void, LSM_RET_VOID, sk_getsecid, struct sock *sk, u32 *secid) -LSM_HOOK(void, LSM_RET_VOID, sock_graft, struct sock *sk, struct socket *parent) -LSM_HOOK(int, 0, inet_conn_request, const struct sock *sk, struct sk_buff *skb, - struct request_sock *req) -LSM_HOOK(void, LSM_RET_VOID, inet_csk_clone, struct sock *newsk, +LSM_HOOK(void, LSM_RET_VOID, 0, sk_getsecid, struct sock *sk, u32 *secid) +LSM_HOOK(void, LSM_RET_VOID, 0, sock_graft, struct sock *sk, struct socket *parent) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, inet_conn_request, + const struct sock *sk, struct sk_buff *skb, struct request_sock *req) +LSM_HOOK(void, LSM_RET_VOID, 0, inet_csk_clone, struct sock *newsk, const struct request_sock *req) -LSM_HOOK(void, LSM_RET_VOID, inet_conn_established, struct sock *sk, +LSM_HOOK(void, LSM_RET_VOID, 0, inet_conn_established, struct sock *sk, struct sk_buff *skb) -LSM_HOOK(int, 0, secmark_relabel_packet, u32 secid) -LSM_HOOK(void, LSM_RET_VOID, secmark_refcount_inc, void) -LSM_HOOK(void, LSM_RET_VOID, secmark_refcount_dec, void) -LSM_HOOK(void, LSM_RET_VOID, req_classify_flow, const struct request_sock *req, +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, secmark_relabel_packet, u32 secid) +LSM_HOOK(void, LSM_RET_VOID, 0, secmark_refcount_inc, void) +LSM_HOOK(void, LSM_RET_VOID, 0, secmark_refcount_dec, void) +LSM_HOOK(void, LSM_RET_VOID, 0, req_classify_flow, const struct request_sock *req, struct flowi_common *flic) -LSM_HOOK(int, 0, tun_dev_alloc_security, void **security) -LSM_HOOK(void, LSM_RET_VOID, tun_dev_free_security, void *security) -LSM_HOOK(int, 0, tun_dev_create, void) -LSM_HOOK(int, 0, tun_dev_attach_queue, void *security) -LSM_HOOK(int, 0, tun_dev_attach, struct sock *sk, void *security) -LSM_HOOK(int, 0, tun_dev_open, void *security) -LSM_HOOK(int, 0, sctp_assoc_request, struct sctp_association *asoc, - struct sk_buff *skb) -LSM_HOOK(int, 0, sctp_bind_connect, struct sock *sk, int optname, - struct sockaddr *address, int addrlen) -LSM_HOOK(void, LSM_RET_VOID, sctp_sk_clone, struct sctp_association *asoc, +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, tun_dev_alloc_security, + void **security) +LSM_HOOK(void, LSM_RET_VOID, 0, tun_dev_free_security, void *security) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, tun_dev_create, void) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, tun_dev_attach_queue, + void *security) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, tun_dev_attach, struct sock *sk, + void *security) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, tun_dev_open, void *security) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, sctp_assoc_request, + struct sctp_association *asoc, struct sk_buff *skb) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, sctp_bind_connect, struct sock *sk, + int optname, struct sockaddr *address, int addrlen) +LSM_HOOK(void, LSM_RET_VOID, 0, sctp_sk_clone, struct sctp_association *asoc, struct sock *sk, struct sock *newsk) -LSM_HOOK(int, 0, sctp_assoc_established, struct sctp_association *asoc, - struct sk_buff *skb) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, sctp_assoc_established, + struct sctp_association *asoc, struct sk_buff *skb) #endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_INFINIBAND -LSM_HOOK(int, 0, ib_pkey_access, void *sec, u64 subnet_prefix, u16 pkey) -LSM_HOOK(int, 0, ib_endport_manage_subnet, void *sec, const char *dev_name, - u8 port_num) -LSM_HOOK(int, 0, ib_alloc_security, void **sec) -LSM_HOOK(void, LSM_RET_VOID, ib_free_security, void *sec) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, ib_pkey_access, void *sec, + u64 subnet_prefix, u16 pkey) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, ib_endport_manage_subnet, + void *sec, const char *dev_name, u8 port_num) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, ib_alloc_security, void **sec) +LSM_HOOK(void, LSM_RET_VOID, 0, ib_free_security, void *sec) #endif /* CONFIG_SECURITY_INFINIBAND */
#ifdef CONFIG_SECURITY_NETWORK_XFRM -LSM_HOOK(int, 0, xfrm_policy_alloc_security, struct xfrm_sec_ctx **ctxp, - struct xfrm_user_sec_ctx *sec_ctx, gfp_t gfp) -LSM_HOOK(int, 0, xfrm_policy_clone_security, struct xfrm_sec_ctx *old_ctx, - struct xfrm_sec_ctx **new_ctx) -LSM_HOOK(void, LSM_RET_VOID, xfrm_policy_free_security, +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, xfrm_policy_alloc_security, + struct xfrm_sec_ctx **ctxp, struct xfrm_user_sec_ctx *sec_ctx, + gfp_t gfp) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, xfrm_policy_clone_security, + struct xfrm_sec_ctx *old_ctx, struct xfrm_sec_ctx **new_ctx) +LSM_HOOK(void, LSM_RET_VOID, 0, xfrm_policy_free_security, + struct xfrm_sec_ctx *ctx) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, xfrm_policy_delete_security, struct xfrm_sec_ctx *ctx) -LSM_HOOK(int, 0, xfrm_policy_delete_security, struct xfrm_sec_ctx *ctx) -LSM_HOOK(int, 0, xfrm_state_alloc, struct xfrm_state *x, - struct xfrm_user_sec_ctx *sec_ctx) -LSM_HOOK(int, 0, xfrm_state_alloc_acquire, struct xfrm_state *x, - struct xfrm_sec_ctx *polsec, u32 secid) -LSM_HOOK(void, LSM_RET_VOID, xfrm_state_free_security, struct xfrm_state *x) -LSM_HOOK(int, 0, xfrm_state_delete_security, struct xfrm_state *x) -LSM_HOOK(int, 0, xfrm_policy_lookup, struct xfrm_sec_ctx *ctx, u32 fl_secid) -LSM_HOOK(int, 1, xfrm_state_pol_flow_match, struct xfrm_state *x, - struct xfrm_policy *xp, const struct flowi_common *flic) -LSM_HOOK(int, 0, xfrm_decode_session, struct sk_buff *skb, u32 *secid, - int ckall) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, xfrm_state_alloc, + struct xfrm_state *x, struct xfrm_user_sec_ctx *sec_ctx) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, xfrm_state_alloc_acquire, + struct xfrm_state *x, struct xfrm_sec_ctx *polsec, u32 secid) +LSM_HOOK(void, LSM_RET_VOID, 0, xfrm_state_free_security, struct xfrm_state *x) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, xfrm_state_delete_security, + struct xfrm_state *x) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, xfrm_policy_lookup, + struct xfrm_sec_ctx *ctx, u32 fl_secid) +LSM_HOOK(int, 1, LSM_RET_ZERO | LSM_RET_ONE, xfrm_state_pol_flow_match, + struct xfrm_state *x, struct xfrm_policy *xp, + const struct flowi_common *flic) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, xfrm_decode_session, + struct sk_buff *skb, u32 *secid, int ckall) #endif /* CONFIG_SECURITY_NETWORK_XFRM */
/* key management security hooks */ #ifdef CONFIG_KEYS -LSM_HOOK(int, 0, key_alloc, struct key *key, const struct cred *cred, - unsigned long flags) -LSM_HOOK(void, LSM_RET_VOID, key_free, struct key *key) -LSM_HOOK(int, 0, key_permission, key_ref_t key_ref, const struct cred *cred, - enum key_need_perm need_perm) -LSM_HOOK(int, 0, key_getsecurity, struct key *key, char **_buffer) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, key_alloc, struct key *key, + const struct cred *cred, unsigned long flags) +LSM_HOOK(void, LSM_RET_VOID, 0, key_free, struct key *key) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, key_permission, key_ref_t key_ref, + const struct cred *cred, enum key_need_perm need_perm) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO | LSM_RET_ONE | LSM_RET_GT_ONE, + key_getsecurity, struct key *key, char **_buffer) #endif /* CONFIG_KEYS */
#ifdef CONFIG_AUDIT -LSM_HOOK(int, 0, audit_rule_init, u32 field, u32 op, char *rulestr, - void **lsmrule) -LSM_HOOK(int, 0, audit_rule_known, struct audit_krule *krule) -LSM_HOOK(int, 0, audit_rule_match, u32 secid, u32 field, u32 op, void *lsmrule) -LSM_HOOK(void, LSM_RET_VOID, audit_rule_free, void *lsmrule) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, audit_rule_init, u32 field, u32 op, + char *rulestr, void **lsmrule) +LSM_HOOK(int, 0, LSM_RET_ZERO | LSM_RET_ONE, audit_rule_known, + struct audit_krule *krule) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO | LSM_RET_ONE, audit_rule_match, + u32 secid, u32 field, u32 op, void *lsmrule) +LSM_HOOK(void, LSM_RET_VOID, 0, audit_rule_free, void *lsmrule) #endif /* CONFIG_AUDIT */
#ifdef CONFIG_BPF_SYSCALL -LSM_HOOK(int, 0, bpf, int cmd, union bpf_attr *attr, unsigned int size) -LSM_HOOK(int, 0, bpf_map, struct bpf_map *map, fmode_t fmode) -LSM_HOOK(int, 0, bpf_prog, struct bpf_prog *prog) -LSM_HOOK(int, 0, bpf_map_alloc_security, struct bpf_map *map) -LSM_HOOK(void, LSM_RET_VOID, bpf_map_free_security, struct bpf_map *map) -LSM_HOOK(int, 0, bpf_prog_alloc_security, struct bpf_prog_aux *aux) -LSM_HOOK(void, LSM_RET_VOID, bpf_prog_free_security, struct bpf_prog_aux *aux) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, bpf, int cmd, union bpf_attr *attr, + unsigned int size) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, bpf_map, struct bpf_map *map, + fmode_t fmode) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, bpf_prog, struct bpf_prog *prog) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, bpf_map_alloc_security, + struct bpf_map *map) +LSM_HOOK(void, LSM_RET_VOID, 0, bpf_map_free_security, struct bpf_map *map) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, bpf_prog_alloc_security, + struct bpf_prog_aux *aux) +LSM_HOOK(void, LSM_RET_VOID, 0, bpf_prog_free_security, struct bpf_prog_aux *aux) #endif /* CONFIG_BPF_SYSCALL */
-LSM_HOOK(int, 0, locked_down, enum lockdown_reason what) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, locked_down, + enum lockdown_reason what)
#ifdef CONFIG_PERF_EVENTS -LSM_HOOK(int, 0, perf_event_open, struct perf_event_attr *attr, int type) -LSM_HOOK(int, 0, perf_event_alloc, struct perf_event *event) -LSM_HOOK(void, LSM_RET_VOID, perf_event_free, struct perf_event *event) -LSM_HOOK(int, 0, perf_event_read, struct perf_event *event) -LSM_HOOK(int, 0, perf_event_write, struct perf_event *event) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, perf_event_open, + struct perf_event_attr *attr, int type) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, perf_event_alloc, + struct perf_event *event) +LSM_HOOK(void, LSM_RET_VOID, 0, perf_event_free, struct perf_event *event) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, perf_event_read, + struct perf_event *event) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, perf_event_write, + struct perf_event *event) #endif /* CONFIG_PERF_EVENTS */
#ifdef CONFIG_IO_URING -LSM_HOOK(int, 0, uring_override_creds, const struct cred *new) -LSM_HOOK(int, 0, uring_sqpoll, void) -LSM_HOOK(int, 0, uring_cmd, struct io_uring_cmd *ioucmd) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, uring_override_creds, + const struct cred *new) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, uring_sqpoll, void) +LSM_HOOK(int, 0, LSM_RET_NEG | LSM_RET_ZERO, uring_cmd, + struct io_uring_cmd *ioucmd) #endif /* CONFIG_IO_URING */ diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 883f0f252f06..d1859b8ab9a3 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1591,13 +1591,13 @@ * */ union security_list_options { - #define LSM_HOOK(RET, DEFAULT, NAME, ...) RET (*NAME)(__VA_ARGS__); + #define LSM_HOOK(RET, DEFAULT, RET_FLAGS, NAME, ...) RET (*NAME)(__VA_ARGS__); #include "lsm_hook_defs.h" #undef LSM_HOOK };
struct security_hook_heads { - #define LSM_HOOK(RET, DEFAULT, NAME, ...) struct hlist_head NAME; + #define LSM_HOOK(RET, DEFAULT, RET_FLAGS, NAME, ...) struct hlist_head NAME; #include "lsm_hook_defs.h" #undef LSM_HOOK } __randomize_layout; @@ -1632,6 +1632,11 @@ struct lsm_blob_sizes { */ #define LSM_RET_VOID ((void) 0)
+#define LSM_RET_NEG 0x01 +#define LSM_RET_ZERO 0x02 +#define LSM_RET_ONE 0x04 +#define LSM_RET_GT_ONE 0x08 + /* * Initializing a security_hook_list structure takes * up a lot of space in a source file. This macro takes diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c index 9ea42a45da47..98f810f661a6 100644 --- a/kernel/bpf/bpf_lsm.c +++ b/kernel/bpf/bpf_lsm.c @@ -21,7 +21,7 @@ /* For every LSM hook that allows attachment of BPF programs, declare a nop * function where a BPF program can be attached. */ -#define LSM_HOOK(RET, DEFAULT, NAME, ...) \ +#define LSM_HOOK(RET, DEFAULT, RET_FLAGS, NAME, ...) \ noinline RET bpf_lsm_##NAME(__VA_ARGS__) \ { \ return DEFAULT; \ @@ -30,7 +30,8 @@ noinline RET bpf_lsm_##NAME(__VA_ARGS__) \ #include <linux/lsm_hook_defs.h> #undef LSM_HOOK
-#define LSM_HOOK(RET, DEFAULT, NAME, ...) BTF_ID(func, bpf_lsm_##NAME) +#define LSM_HOOK(RET, DEFAULT, RET_FLAGS, NAME, ...) \ + BTF_ID(func, bpf_lsm_##NAME) BTF_SET_START(bpf_lsm_hooks) #include <linux/lsm_hook_defs.h> #undef LSM_HOOK diff --git a/security/bpf/hooks.c b/security/bpf/hooks.c index e5971fa74fd7..a2a3b2be345f 100644 --- a/security/bpf/hooks.c +++ b/security/bpf/hooks.c @@ -7,7 +7,7 @@ #include <linux/bpf_lsm.h>
static struct security_hook_list bpf_lsm_hooks[] __lsm_ro_after_init = { - #define LSM_HOOK(RET, DEFAULT, NAME, ...) \ + #define LSM_HOOK(RET, DEFAULT, RET_FLAGS, NAME, ...) \ LSM_HOOK_INIT(NAME, bpf_lsm_##NAME), #include <linux/lsm_hook_defs.h> #undef LSM_HOOK diff --git a/security/security.c b/security/security.c index f27c885ee98d..357cd845d1ed 100644 --- a/security/security.c +++ b/security/security.c @@ -371,7 +371,7 @@ int __init early_security_init(void) { struct lsm_info *lsm;
-#define LSM_HOOK(RET, DEFAULT, NAME, ...) \ +#define LSM_HOOK(RET, DEFAULT, RET_FLAGS, NAME, ...) \ INIT_HLIST_HEAD(&security_hook_heads.NAME); #include "linux/lsm_hook_defs.h" #undef LSM_HOOK @@ -710,7 +710,7 @@ static int lsm_superblock_alloc(struct super_block *sb) #define DECLARE_LSM_RET_DEFAULT_void(DEFAULT, NAME) #define DECLARE_LSM_RET_DEFAULT_int(DEFAULT, NAME) \ static const int __maybe_unused LSM_RET_DEFAULT(NAME) = (DEFAULT); -#define LSM_HOOK(RET, DEFAULT, NAME, ...) \ +#define LSM_HOOK(RET, DEFAULT, RET_FLAGS, NAME, ...) \ DECLARE_LSM_RET_DEFAULT_##RET(DEFAULT, NAME)
#include <linux/lsm_hook_defs.h>
From: Roberto Sassu roberto.sassu@huawei.com
With the patch for the LSM infrastructure to redefine the LSM_HOOK() macro and to introduce the return value flags, it becomes straightforward for eBPF to leverage this information to enforce return values limitations on eBPF programs implementing security hooks.
Update the bpf_lsm_hooks BTF ID set, by including the return value flags. Then, introduce bpf_lsm_is_ret_value_allowed(), which determines in which intervals the R0 register (which contains the return value) falls, and checks if the corresponding return value flag is set for those intervals.
In addition, for the interval including zero, ensure that the hook is not inode_init_security, otherwise report that zero is not allowed. By LSM conventions, LSMs should return zero only if they set an xattr, which currently eBPF programs cannot do.
Finally, expose the new function and add a call to it in the verifier.
Cc: stable@vger.kernel.org # 5.7.x Fixes: 9d3fdea789c8 ("bpf: lsm: Provide attachment points for BPF LSM programs") Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- include/linux/bpf_lsm.h | 9 +++++ kernel/bpf/bpf_lsm.c | 78 ++++++++++++++++++++++++++++++++++++++--- kernel/bpf/verifier.c | 7 ++-- 3 files changed, 87 insertions(+), 7 deletions(-)
diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h index 2f5757085dfd..0ce5948f3662 100644 --- a/include/linux/bpf_lsm.h +++ b/include/linux/bpf_lsm.h @@ -29,6 +29,8 @@ int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
bool bpf_lsm_is_sleepable_hook(u32 btf_id); bool bpf_lsm_is_trusted(const struct bpf_prog *prog); +bool bpf_lsm_is_ret_value_allowed(struct bpf_verifier_log *vlog, + struct bpf_reg_state *reg, u32 btf_id);
static inline struct bpf_storage_blob *bpf_inode( const struct inode *inode) @@ -57,6 +59,13 @@ static inline bool bpf_lsm_is_trusted(const struct bpf_prog *prog) return false; }
+static inline bool bpf_lsm_is_ret_value_allowed(struct bpf_verifier_log *vlog, + struct bpf_reg_state *reg, + u32 btf_id) +{ + return false; +} + static inline int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog, const struct bpf_prog *prog) { diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c index 98f810f661a6..39ddafc06021 100644 --- a/kernel/bpf/bpf_lsm.c +++ b/kernel/bpf/bpf_lsm.c @@ -31,11 +31,11 @@ noinline RET bpf_lsm_##NAME(__VA_ARGS__) \ #undef LSM_HOOK
#define LSM_HOOK(RET, DEFAULT, RET_FLAGS, NAME, ...) \ - BTF_ID(func, bpf_lsm_##NAME) -BTF_SET_START(bpf_lsm_hooks) + BTF_ID_FLAGS(func, bpf_lsm_##NAME, RET_FLAGS) +BTF_SET8_START(bpf_lsm_hooks) #include <linux/lsm_hook_defs.h> #undef LSM_HOOK -BTF_SET_END(bpf_lsm_hooks) +BTF_SET8_END(bpf_lsm_hooks)
/* List of LSM hooks that should operate on 'current' cgroup regardless * of function signature. @@ -105,7 +105,7 @@ int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog, return -EINVAL; }
- if (!btf_id_set_contains(&bpf_lsm_hooks, prog->aux->attach_btf_id)) { + if (!btf_id_set8_contains(&bpf_lsm_hooks, prog->aux->attach_btf_id)) { bpf_log(vlog, "attach_btf_id %u points to wrong type name %s\n", prog->aux->attach_btf_id, prog->aux->attach_func_name); return -EINVAL; @@ -367,6 +367,76 @@ bool bpf_lsm_is_trusted(const struct bpf_prog *prog) return !btf_id_set_contains(&untrusted_lsm_hooks, prog->aux->attach_btf_id); }
+BTF_SET_START(zero_forbidden_lsm_hooks) +BTF_ID(func, bpf_lsm_inode_init_security) +BTF_SET_END(zero_forbidden_lsm_hooks) + +bool bpf_lsm_is_ret_value_allowed(struct bpf_verifier_log *vlog, + struct bpf_reg_state *reg, u32 btf_id) +{ + u32 *id = btf_id_set8_contains(&bpf_lsm_hooks, btf_id); + s64 smin_value = reg->smin_value; + s64 smax_value = reg->smax_value; + u32 *ret_flags = id + 1; + + /* See no_alu32/test_bpf_cookie.bpf.o for how return -EPERM is compiled: + * + * 11: 18 06 00 00 ff ff ff ff 00 00 00 00 00 00 00 00 r6 = 4294967295 ll + * 13: 67 00 00 00 20 00 00 00 r0 <<= 32 + * 14: 77 00 00 00 20 00 00 00 r0 >>= 32 + * 15: 5d 08 07 00 00 00 00 00 if r8 != r0 goto +7 <LBB11_3> + * + * This causes predicted values to be: + * smin_value = 0xffffffff, smax_value = 0xffffffff, + * s32_min_value = 0xffffffff, s32_max_value = 0xffffffff, + * + * despite it is an ALU64 operation. So, checking reg->alu32 is not + * enough. Then, if after casting the 64 bit values they are equal to + * the 32 bit ones, use the latter ones (the LSM infrastructure takes + * an int). + */ + if ((reg->s32_min_value == (u32)smin_value && + reg->s32_max_value == (u32)smax_value) || reg->alu32) { + smin_value = reg->s32_min_value; + smax_value = reg->s32_max_value; + } + + /* Interval includes < 0 values. */ + if (smin_value < 0) { + if (!(*ret_flags & LSM_RET_NEG)) { + bpf_log(vlog, "Invalid R0, cannot return < 0\n"); + return false; + } + } + + /* Interval includes 0. */ + if (smin_value <= 0 && smax_value >= 0) { + if (!(*ret_flags & LSM_RET_ZERO) || + btf_id_set_contains(&zero_forbidden_lsm_hooks, btf_id)) { + bpf_log(vlog, "Invalid R0, cannot return 0\n"); + return false; + } + } + + /* Interval includes 1. */ + if (smin_value <= 1 && smax_value >= 1) { + if (!(*ret_flags & LSM_RET_ONE)) { + bpf_log(vlog, "Invalid R0, cannot return 1\n"); + return false; + } + } + + /* Interval includes > 1 values. */ + if (smax_value > 1) { + if (!(*ret_flags & LSM_RET_GT_ONE)) { + bpf_log(vlog, "Invalid R0, cannot return > 1\n"); + return false; + } + } + + return true; +} + const struct bpf_prog_ops lsm_prog_ops = { };
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index edce85c425a2..5d13b7f42238 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -12064,9 +12064,10 @@ static int check_return_code(struct bpf_verifier_env *env)
case BPF_PROG_TYPE_LSM: if (env->prog->expected_attach_type != BPF_LSM_CGROUP) { - /* Regular BPF_PROG_TYPE_LSM programs can return - * any value. - */ + if (!bpf_lsm_is_ret_value_allowed(&env->log, reg, + env->prog->aux->attach_btf_id)) + return -EINVAL; + return 0; } if (!env->prog->aux->attach_func_proto->type) {
From: Roberto Sassu roberto.sassu@huawei.com
Ensure that the eBPF verifier allows to load only LSM programs that return an allowed value depending on the LSM hook they attach to.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- .../testing/selftests/bpf/verifier/lsm_ret.c | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 tools/testing/selftests/bpf/verifier/lsm_ret.c
diff --git a/tools/testing/selftests/bpf/verifier/lsm_ret.c b/tools/testing/selftests/bpf/verifier/lsm_ret.c new file mode 100644 index 000000000000..c9c9cee8e406 --- /dev/null +++ b/tools/testing/selftests/bpf/verifier/lsm_ret.c @@ -0,0 +1,148 @@ +{ + "lsm return value: positive not allowed, return -EPERM", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, -EPERM), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_LSM, + .kfunc = "inode_permission", + .expected_attach_type = BPF_LSM_MAC, + .result = ACCEPT, +}, +{ + "lsm return value: positive not allowed, return zero", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_LSM, + .kfunc = "inode_permission", + .expected_attach_type = BPF_LSM_MAC, + .result = ACCEPT, +}, +{ + "lsm return value: positive not allowed, return one", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_LSM, + .kfunc = "inode_permission", + .expected_attach_type = BPF_LSM_MAC, + .errstr = "Invalid R0, cannot return 1", + .result = REJECT, +}, +{ + "lsm return value: zero/positive not allowed, return -EPERM", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, -EPERM), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_LSM, + .kfunc = "inode_init_security", + .expected_attach_type = BPF_LSM_MAC, + .result = ACCEPT, +}, +{ + "lsm return value: zero/positive not allowed, return zero", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_LSM, + .kfunc = "inode_init_security", + .expected_attach_type = BPF_LSM_MAC, + .errstr = "Invalid R0, cannot return 0", + .result = REJECT, +}, +{ + "lsm return value: zero/positive not allowed, return one", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_LSM, + .kfunc = "inode_init_security", + .expected_attach_type = BPF_LSM_MAC, + .errstr = "Invalid R0, cannot return 1", + .result = REJECT, +}, +{ + "lsm return value: positive allowed, return one", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_LSM, + .kfunc = "getprocattr", + .expected_attach_type = BPF_LSM_MAC, + .result = ACCEPT, +}, +{ + "lsm return value: positive allowed, return two", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 2), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_LSM, + .kfunc = "getprocattr", + .expected_attach_type = BPF_LSM_MAC, + .result = ACCEPT, +}, +{ + "lsm return value: only one allowed, return one", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_LSM, + .kfunc = "audit_rule_match", + .expected_attach_type = BPF_LSM_MAC, + .result = ACCEPT, +}, +{ + "lsm return value: only one allowed, return two", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 2), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_LSM, + .kfunc = "audit_rule_match", + .expected_attach_type = BPF_LSM_MAC, + .errstr = "Invalid R0, cannot return > 1", + .result = REJECT, +}, +{ + "lsm return value: negative not allowed, return -EPERM", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, -EPERM), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_LSM, + .kfunc = "ismaclabel", + .expected_attach_type = BPF_LSM_MAC, + .errstr = "Invalid R0, cannot return < 0", + .result = REJECT, +}, +{ + "lsm return value: negative not allowed, return zero", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_LSM, + .kfunc = "ismaclabel", + .expected_attach_type = BPF_LSM_MAC, + .result = ACCEPT, +}, +{ + "lsm return value: negative not allowed, return one", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_LSM, + .kfunc = "ismaclabel", + .expected_attach_type = BPF_LSM_MAC, + .result = ACCEPT, +},
From: Roberto Sassu roberto.sassu@huawei.com
Modify test_lsm and verify_pkcs7_sig to ensure that they don't return positive values.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- tools/testing/selftests/bpf/progs/lsm.c | 4 ++++ .../selftests/bpf/progs/test_verify_pkcs7_sig.c | 11 +++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/bpf/progs/lsm.c b/tools/testing/selftests/bpf/progs/lsm.c index d8d8af623bc2..42252750d866 100644 --- a/tools/testing/selftests/bpf/progs/lsm.c +++ b/tools/testing/selftests/bpf/progs/lsm.c @@ -88,6 +88,10 @@ SEC("lsm/file_mprotect") int BPF_PROG(test_int_hook, struct vm_area_struct *vma, unsigned long reqprot, unsigned long prot, int ret) { + /* file_mprotect hook must return zero or negative values. */ + if (ret > 0) + ret = -EINVAL; + if (ret != 0) return ret;
diff --git a/tools/testing/selftests/bpf/progs/test_verify_pkcs7_sig.c b/tools/testing/selftests/bpf/progs/test_verify_pkcs7_sig.c index ce419304ff1f..d3cab4370f29 100644 --- a/tools/testing/selftests/bpf/progs/test_verify_pkcs7_sig.c +++ b/tools/testing/selftests/bpf/progs/test_verify_pkcs7_sig.c @@ -42,14 +42,14 @@ struct { char _license[] SEC("license") = "GPL";
SEC("lsm.s/bpf") -int BPF_PROG(bpf, int cmd, union bpf_attr *attr, unsigned int size) +s64 BPF_PROG(bpf, int cmd, union bpf_attr *attr, unsigned int size) { struct bpf_dynptr data_ptr, sig_ptr; struct data *data_val; struct bpf_key *trusted_keyring; __u32 pid; __u64 value; - int ret, zero = 0; + s64 ret, zero = 0;
pid = bpf_get_current_pid_tgid() >> 32; if (pid != monitored_pid) @@ -86,5 +86,12 @@ int BPF_PROG(bpf, int cmd, union bpf_attr *attr, unsigned int size)
bpf_key_put(trusted_keyring);
+ /* + * bpf hook must return zero or negative values, use s64 to propagate + * the bounds to R0. + */ + if (ret > 0) + return -EINVAL; + return ret; }
From: Roberto Sassu roberto.sassu@huawei.com
In the no_alu32 version, the eBPF assembly looks like:
0: b7 00 00 00 00 00 00 00 r0 = 0 1: 79 12 00 00 00 00 00 00 r2 = *(u64 *)(r1 + 0) 2: 18 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r3 = 0 ll 4: 5d 32 04 00 00 00 00 00 if r2 != r3 goto +4 <LBB0_2> 5: 79 10 08 00 00 00 00 00 r0 = *(u64 *)(r1 + 8) 6: 67 00 00 00 3e 00 00 00 r0 <<= 62 7: c7 00 00 00 3f 00 00 00 r0 s>>= 63 8: 57 00 00 00 f3 ff ff ff r0 &= -13
Unfortunately, ANDing of negative numbers is not yet supported in the verifier. As a consequence, current bounds are lost in the AND operation, resulting in estimating a positive return value, even if there isn't.
For now, return -EPERM instead of -EACCES, to remove the AND operation and let the verifier know that the return value is legitimate (negative or zero).
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- .../selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c b/tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c index f5ac5f3e8919..a143dbbd5573 100644 --- a/tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c +++ b/tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c @@ -29,8 +29,13 @@ int BPF_PROG(check_access, struct bpf_map *map, fmode_t fmode) if (map != (struct bpf_map *)&data_input) return 0;
+ /* + * Prefer -EPERM to -EACCES to avoid ANDing negative numbers in the + * no_alu32 version, which results in the current register bounds to + * be lost. + */ if (fmode & FMODE_WRITE) - return -EACCES; + return -EPERM;
return 0; }
linux-kselftest-mirror@lists.linaro.org