Skip to content

Commit 417c564

Browse files
KP Singhpcmoore
KP Singh
authored andcommitted
lsm: replace indirect LSM hook calls with static calls
LSM hooks are currently invoked from a linked list as indirect calls which are invoked using retpolines as a mitigation for speculative attacks (Branch History / Target injection) and add extra overhead which is especially bad in kernel hot paths: security_file_ioctl: 0xff...0320 <+0>: endbr64 0xff...0324 <+4>: push %rbp 0xff...0325 <+5>: push %r15 0xff...0327 <+7>: push %r14 0xff...0329 <+9>: push %rbx 0xff...032a <+10>: mov %rdx,%rbx 0xff...032d <+13>: mov %esi,%ebp 0xff...032f <+15>: mov %rdi,%r14 0xff...0332 <+18>: mov $0xff...7030,%r15 0xff...0339 <+25>: mov (%r15),%r15 0xff...033c <+28>: test %r15,%r15 0xff...033f <+31>: je 0xff...0358 <security_file_ioctl+56> 0xff...0341 <+33>: mov 0x18(%r15),%r11 0xff...0345 <+37>: mov %r14,%rdi 0xff...0348 <+40>: mov %ebp,%esi 0xff...034a <+42>: mov %rbx,%rdx 0xff...034d <+45>: call 0xff...2e0 <__x86_indirect_thunk_array+352> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Indirect calls that use retpolines leading to overhead, not just due to extra instruction but also branch misses. 0xff...0352 <+50>: test %eax,%eax 0xff...0354 <+52>: je 0xff...0339 <security_file_ioctl+25> 0xff...0356 <+54>: jmp 0xff...035a <security_file_ioctl+58> 0xff...0358 <+56>: xor %eax,%eax 0xff...035a <+58>: pop %rbx 0xff...035b <+59>: pop %r14 0xff...035d <+61>: pop %r15 0xff...035f <+63>: pop %rbp 0xff...0360 <+64>: jmp 0xff...47c4 <__x86_return_thunk> The indirect calls are not really needed as one knows the addresses of enabled LSM callbacks at boot time and only the order can possibly change at boot time with the lsm= kernel command line parameter. An array of static calls is defined per LSM hook and the static calls are updated at boot time once the order has been determined. With the hook now exposed as a static call, one can see that the retpolines are no longer there and the LSM callbacks are invoked directly: security_file_ioctl: 0xff...0ca0 <+0>: endbr64 0xff...0ca4 <+4>: nopl 0x0(%rax,%rax,1) 0xff...0ca9 <+9>: push %rbp 0xff...0caa <+10>: push %r14 0xff...0cac <+12>: push %rbx 0xff...0cad <+13>: mov %rdx,%rbx 0xff...0cb0 <+16>: mov %esi,%ebp 0xff...0cb2 <+18>: mov %rdi,%r14 0xff...0cb5 <+21>: jmp 0xff...0cc7 <security_file_ioctl+39> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Static key enabled for SELinux 0xffffffff818f0cb7 <+23>: jmp 0xff...0cde <security_file_ioctl+62> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Static key enabled for BPF LSM. This is something that is changed to default to false to avoid the existing side effect issues of BPF LSM [1] in a subsequent patch. 0xff...0cb9 <+25>: xor %eax,%eax 0xff...0cbb <+27>: xchg %ax,%ax 0xff...0cbd <+29>: pop %rbx 0xff...0cbe <+30>: pop %r14 0xff...0cc0 <+32>: pop %rbp 0xff...0cc1 <+33>: cs jmp 0xff...0000 <__x86_return_thunk> 0xff...0cc7 <+39>: endbr64 0xff...0ccb <+43>: mov %r14,%rdi 0xff...0cce <+46>: mov %ebp,%esi 0xff...0cd0 <+48>: mov %rbx,%rdx 0xff...0cd3 <+51>: call 0xff...3230 <selinux_file_ioctl> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Direct call to SELinux. 0xff...0cd8 <+56>: test %eax,%eax 0xff...0cda <+58>: jne 0xff...0cbd <security_file_ioctl+29> 0xff...0cdc <+60>: jmp 0xff...0cb7 <security_file_ioctl+23> 0xff...0cde <+62>: endbr64 0xff...0ce2 <+66>: mov %r14,%rdi 0xff...0ce5 <+69>: mov %ebp,%esi 0xff...0ce7 <+71>: mov %rbx,%rdx 0xff...0cea <+74>: call 0xff...e220 <bpf_lsm_file_ioctl> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Direct call to BPF LSM. 0xff...0cef <+79>: test %eax,%eax 0xff...0cf1 <+81>: jne 0xff...0cbd <security_file_ioctl+29> 0xff...0cf3 <+83>: jmp 0xff...0cb9 <security_file_ioctl+25> 0xff...0cf5 <+85>: endbr64 0xff...0cf9 <+89>: mov %r14,%rdi 0xff...0cfc <+92>: mov %ebp,%esi 0xff...0cfe <+94>: mov %rbx,%rdx 0xff...0d01 <+97>: pop %rbx 0xff...0d02 <+98>: pop %r14 0xff...0d04 <+100>: pop %rbp 0xff...0d05 <+101>: ret 0xff...0d06 <+102>: int3 0xff...0d07 <+103>: int3 0xff...0d08 <+104>: int3 0xff...0d09 <+105>: int3 While this patch uses static_branch_unlikely indicating that an LSM hook is likely to be not present. In most cases this is still a better choice as even when an LSM with one hook is added, empty slots are created for all LSM hooks (especially when many LSMs that do not initialize most hooks are present on the system). There are some hooks that don't use the call_int_hook or call_void_hook. These hooks are updated to use a new macro called lsm_for_each_hook where the lsm_callback is directly invoked as an indirect call. Below are results of the relevant Unixbench system benchmarks with BPF LSM and SELinux enabled with default policies enabled with and without these patches. Benchmark Delta(%): (+ is better) ========================================================================== Execl Throughput +1.9356 File Write 1024 bufsize 2000 maxblocks +6.5953 Pipe Throughput +9.5499 Pipe-based Context Switching +3.0209 Process Creation +2.3246 Shell Scripts (1 concurrent) +1.4975 System Call Overhead +2.7815 System Benchmarks Index Score (Partial Only): +3.4859 In the best case, some syscalls like eventfd_create benefitted to about ~10%. Tested-by: Guenter Roeck <[email protected]> Reviewed-by: Casey Schaufler <[email protected]> Reviewed-by: Kees Cook <[email protected]> Acked-by: Song Liu <[email protected]> Acked-by: Andrii Nakryiko <[email protected]> Signed-off-by: KP Singh <[email protected]> Signed-off-by: Paul Moore <[email protected]>
1 parent d51e783 commit 417c564

File tree

2 files changed

+198
-73
lines changed

2 files changed

+198
-73
lines changed

Diff for: include/linux/lsm_hooks.h

+43-9
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,47 @@
3030
#include <linux/init.h>
3131
#include <linux/rculist.h>
3232
#include <linux/xattr.h>
33+
#include <linux/static_call.h>
34+
#include <linux/unroll.h>
35+
#include <linux/jump_label.h>
36+
#include <linux/lsm_count.h>
3337

3438
union security_list_options {
3539
#define LSM_HOOK(RET, DEFAULT, NAME, ...) RET (*NAME)(__VA_ARGS__);
3640
#include "lsm_hook_defs.h"
3741
#undef LSM_HOOK
42+
void *lsm_func_addr;
3843
};
3944

40-
struct security_hook_heads {
41-
#define LSM_HOOK(RET, DEFAULT, NAME, ...) struct hlist_head NAME;
42-
#include "lsm_hook_defs.h"
43-
#undef LSM_HOOK
45+
/*
46+
* @key: static call key as defined by STATIC_CALL_KEY
47+
* @trampoline: static call trampoline as defined by STATIC_CALL_TRAMP
48+
* @hl: The security_hook_list as initialized by the owning LSM.
49+
* @active: Enabled when the static call has an LSM hook associated.
50+
*/
51+
struct lsm_static_call {
52+
struct static_call_key *key;
53+
void *trampoline;
54+
struct security_hook_list *hl;
55+
/* this needs to be true or false based on what the key defaults to */
56+
struct static_key_false *active;
4457
} __randomize_layout;
4558

59+
/*
60+
* Table of the static calls for each LSM hook.
61+
* Once the LSMs are initialized, their callbacks will be copied to these
62+
* tables such that the calls are filled backwards (from last to first).
63+
* This way, we can jump directly to the first used static call, and execute
64+
* all of them after. This essentially makes the entry point
65+
* dynamic to adapt the number of static calls to the number of callbacks.
66+
*/
67+
struct lsm_static_calls_table {
68+
#define LSM_HOOK(RET, DEFAULT, NAME, ...) \
69+
struct lsm_static_call NAME[MAX_LSM_COUNT];
70+
#include <linux/lsm_hook_defs.h>
71+
#undef LSM_HOOK
72+
} __packed __randomize_layout;
73+
4674
/**
4775
* struct lsm_id - Identify a Linux Security Module.
4876
* @lsm: name of the LSM, must be approved by the LSM maintainers
@@ -58,10 +86,14 @@ struct lsm_id {
5886
/*
5987
* Security module hook list structure.
6088
* For use with generic list macros for common operations.
89+
*
90+
* struct security_hook_list - Contents of a cacheable, mappable object.
91+
* @scalls: The beginning of the array of static calls assigned to this hook.
92+
* @hook: The callback for the hook.
93+
* @lsm: The name of the lsm that owns this hook.
6194
*/
6295
struct security_hook_list {
63-
struct hlist_node list;
64-
struct hlist_head *head;
96+
struct lsm_static_call *scalls;
6597
union security_list_options hook;
6698
const struct lsm_id *lsmid;
6799
} __randomize_layout;
@@ -98,8 +130,11 @@ struct lsm_blob_sizes {
98130
* care of the common case and reduces the amount of
99131
* text involved.
100132
*/
101-
#define LSM_HOOK_INIT(HEAD, HOOK) \
102-
{ .head = &security_hook_heads.HEAD, .hook = { .HEAD = HOOK } }
133+
#define LSM_HOOK_INIT(NAME, HOOK) \
134+
{ \
135+
.scalls = static_calls_table.NAME, \
136+
.hook = { .NAME = HOOK } \
137+
}
103138

104139
extern void security_add_hooks(struct security_hook_list *hooks, int count,
105140
const struct lsm_id *lsmid);
@@ -134,7 +169,6 @@ struct lsm_info {
134169

135170
/* DO NOT tamper with these variables outside of the LSM framework */
136171
extern char *lsm_names;
137-
extern struct security_hook_heads security_hook_heads;
138172
extern struct lsm_static_calls_table static_calls_table __ro_after_init;
139173
extern struct lsm_info __start_lsm_info[], __end_lsm_info[];
140174
extern struct lsm_info __start_early_lsm_info[], __end_early_lsm_info[];

0 commit comments

Comments
 (0)