All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 0/5] Reduce overhead of LSMs with static calls
@ 2023-09-28 20:24 KP Singh
  2023-09-28 20:24 ` [PATCH v5 1/5] kernel: Add helper macros for loop unrolling KP Singh
                   ` (6 more replies)
  0 siblings, 7 replies; 24+ messages in thread
From: KP Singh @ 2023-09-28 20:24 UTC (permalink / raw)
  To: linux-security-module, bpf
  Cc: paul, keescook, casey, song, daniel, ast, kpsingh, renauld

# Background

LSM hooks (callbacks) are currently invoked as indirect function calls. These
callbacks are registered into a linked list at boot time as the order of the
LSMs can be configured on the kernel command line with the "lsm=" command line
parameter.

Indirect function calls have a high overhead due to retpoline mitigation for
various speculative execution attacks.

Retpolines remain relevant even with newer generation CPUs as recently
discovered speculative attacks, like Spectre BHB need Retpolines to mitigate
against branch history injection and still need to be used in combination with
newer mitigation features like eIBRS.

This overhead is especially significant for the "bpf" LSM which allows the user
to implement LSM functionality with eBPF program. In order to facilitate this
the "bpf" LSM provides a default callback for all LSM hooks. When enabled,
the "bpf" LSM incurs an unnecessary / avoidable indirect call. This is
especially bad in OS hot paths (e.g. in the networking stack).
This overhead prevents the adoption of bpf LSM on performance critical
systems, and also, in general, slows down all LSMs.

Since we know the address of the enabled LSM callbacks at compile time and only
the order is determined at boot time, the LSM framework can allocate static
calls for each of the possible LSM callbacks and these calls can be updated once
the order is determined at boot.

This series is a respin of the RFC proposed by Paul Renauld (renauld@google.com)
and Brendan Jackman (jackmanb@google.com) [1]

# Performance improvement

With this patch-set some syscalls with lots of LSM hooks in their path
benefitted at an average of ~3% and I/O and Pipe based system calls benefitting
the most.

Here are the 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%.
The full analysis can be viewed at https://kpsingh.ch/lsm-perf

[1] https://lore.kernel.org/linux-security-module/20200820164753.3256899-1-jackmanb@chromium.org/


# BPF LSM Side effects

Patch 4 of the series also addresses the issues with the side effects of the
default value return values of the BPF LSM callbacks and also removes the
overheads associated with them making it deployable at hyperscale.

# v4 -> v5

* Rebase to linux-next/master
* Fixed the case where MAX_LSM_COUNT comes to zero when just CONFIG_SECURITY
  is compiled in without any other LSM enabled as reported here:

  https://lore.kernel.org/bpf/202309271206.d7fb60f9-oliver.sang@intel.com

# v3 -> v4

* Refactor LSM count macros to use COUNT_ARGS
* Change CONFIG_SECURITY_HOOK_LIKELY likely's default value to be based on
  the LSM enabled and have it depend on CONFIG_EXPERT. There are a lot of subtle
  options behind CONFIG_EXPERT and this should, hopefully alleviate concerns
  about yet another knob.
* __randomize_layout for struct lsm_static_call and, in addition to the cover
  letter add performance numbers to 3rd patch and some minor commit message
  updates.
* Rebase to linux-next.

# v2 -> v3

* Fixed a build issue on archs which don't have static calls and enable
  CONFIG_SECURITY.
* Updated the LSM_COUNT macros based on Andrii's suggestions.
* Changed the security_ prefix to lsm_prefix based on Casey's suggestion.
* Inlined static_branch_maybe into lsm_for_each_hook on Kees' feedback.

# v1 -> v2 (based on linux-next, next-20230614)

* Incorporated suggestions from Kees
* Changed the way MAX_LSMs are counted from a binary based generator to a clever header.
* Add CONFIG_SECURITY_HOOK_LIKELY to configure the likelihood of LSM hooks.


KP Singh (5):
  kernel: Add helper macros for loop unrolling
  security: Count the LSMs enabled at compile time
  security: Replace indirect LSM hook calls with static calls
  bpf: Only enable BPF LSM hooks when an LSM program is attached
  security: Add CONFIG_SECURITY_HOOK_LIKELY

 include/linux/bpf.h       |   1 +
 include/linux/bpf_lsm.h   |   5 +
 include/linux/lsm_count.h | 114 ++++++++++++++++++++
 include/linux/lsm_hooks.h |  81 +++++++++++++--
 include/linux/unroll.h    |  36 +++++++
 kernel/bpf/trampoline.c   |  29 +++++-
 security/Kconfig          |  11 ++
 security/bpf/hooks.c      |  25 ++++-
 security/security.c       | 213 +++++++++++++++++++++++++-------------
 9 files changed, 432 insertions(+), 83 deletions(-)
 create mode 100644 include/linux/lsm_count.h
 create mode 100644 include/linux/unroll.h

-- 
2.42.0.582.g8ccd20d70d-goog


^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v5 1/5] kernel: Add helper macros for loop unrolling
  2023-09-28 20:24 [PATCH v5 0/5] Reduce overhead of LSMs with static calls KP Singh
@ 2023-09-28 20:24 ` KP Singh
  2023-09-28 20:24 ` [PATCH v5 2/5] security: Count the LSMs enabled at compile time KP Singh
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 24+ messages in thread
From: KP Singh @ 2023-09-28 20:24 UTC (permalink / raw)
  To: linux-security-module, bpf
  Cc: paul, keescook, casey, song, daniel, ast, kpsingh, renauld

This helps in easily initializing blocks of code (e.g. static calls and
keys).

UNROLL(N, MACRO, __VA_ARGS__) calls MACRO N times with the first
argument as the index of the iteration. This allows string pasting to
create unique tokens for variable names, function calls etc.

As an example:

	#include <linux/unroll.h>

	#define MACRO(N, a, b)            \
		int add_##N(int a, int b) \
		{                         \
			return a + b + N; \
		}

	UNROLL(2, MACRO, x, y)

expands to:

	int add_0(int x, int y)
	{
		return x + y + 0;
	}

	int add_1(int x, int y)
	{
		return x + y + 1;
	}

Reviewed-by: Kees Cook <keescook@chromium.org>
Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
Acked-by: Song Liu <song@kernel.org>
Signed-off-by: KP Singh <kpsingh@kernel.org>
---
 include/linux/unroll.h | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)
 create mode 100644 include/linux/unroll.h

diff --git a/include/linux/unroll.h b/include/linux/unroll.h
new file mode 100644
index 000000000000..d42fd6366373
--- /dev/null
+++ b/include/linux/unroll.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Copyright (C) 2023 Google LLC.
+ */
+
+#ifndef __UNROLL_H
+#define __UNROLL_H
+
+#include <linux/args.h>
+
+#define UNROLL(N, MACRO, args...) CONCATENATE(__UNROLL_, N)(MACRO, args)
+
+#define __UNROLL_0(MACRO, args...)
+#define __UNROLL_1(MACRO, args...)  __UNROLL_0(MACRO, args)  MACRO(0, args)
+#define __UNROLL_2(MACRO, args...)  __UNROLL_1(MACRO, args)  MACRO(1, args)
+#define __UNROLL_3(MACRO, args...)  __UNROLL_2(MACRO, args)  MACRO(2, args)
+#define __UNROLL_4(MACRO, args...)  __UNROLL_3(MACRO, args)  MACRO(3, args)
+#define __UNROLL_5(MACRO, args...)  __UNROLL_4(MACRO, args)  MACRO(4, args)
+#define __UNROLL_6(MACRO, args...)  __UNROLL_5(MACRO, args)  MACRO(5, args)
+#define __UNROLL_7(MACRO, args...)  __UNROLL_6(MACRO, args)  MACRO(6, args)
+#define __UNROLL_8(MACRO, args...)  __UNROLL_7(MACRO, args)  MACRO(7, args)
+#define __UNROLL_9(MACRO, args...)  __UNROLL_8(MACRO, args)  MACRO(8, args)
+#define __UNROLL_10(MACRO, args...) __UNROLL_9(MACRO, args)  MACRO(9, args)
+#define __UNROLL_11(MACRO, args...) __UNROLL_10(MACRO, args) MACRO(10, args)
+#define __UNROLL_12(MACRO, args...) __UNROLL_11(MACRO, args) MACRO(11, args)
+#define __UNROLL_13(MACRO, args...) __UNROLL_12(MACRO, args) MACRO(12, args)
+#define __UNROLL_14(MACRO, args...) __UNROLL_13(MACRO, args) MACRO(13, args)
+#define __UNROLL_15(MACRO, args...) __UNROLL_14(MACRO, args) MACRO(14, args)
+#define __UNROLL_16(MACRO, args...) __UNROLL_15(MACRO, args) MACRO(15, args)
+#define __UNROLL_17(MACRO, args...) __UNROLL_16(MACRO, args) MACRO(16, args)
+#define __UNROLL_18(MACRO, args...) __UNROLL_17(MACRO, args) MACRO(17, args)
+#define __UNROLL_19(MACRO, args...) __UNROLL_18(MACRO, args) MACRO(18, args)
+#define __UNROLL_20(MACRO, args...) __UNROLL_19(MACRO, args) MACRO(19, args)
+
+#endif /* __UNROLL_H */
-- 
2.42.0.582.g8ccd20d70d-goog


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v5 2/5] security: Count the LSMs enabled at compile time
  2023-09-28 20:24 [PATCH v5 0/5] Reduce overhead of LSMs with static calls KP Singh
  2023-09-28 20:24 ` [PATCH v5 1/5] kernel: Add helper macros for loop unrolling KP Singh
@ 2023-09-28 20:24 ` KP Singh
  2023-09-29  0:37   ` Kees Cook
  2023-09-28 20:24 ` [PATCH v5 3/5] security: Replace indirect LSM hook calls with static calls KP Singh
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 24+ messages in thread
From: KP Singh @ 2023-09-28 20:24 UTC (permalink / raw)
  To: linux-security-module, bpf
  Cc: paul, keescook, casey, song, daniel, ast, kpsingh, renauld,
	Kui-Feng Lee, Andrii Nakryiko

These macros are a clever trick to determine a count of the number of
LSMs that are enabled in the config to ascertain the maximum number of
static calls that need to be configured per LSM hook.

Without this one would need to generate static calls for the total
number of LSMs in the kernel (even if they are not compiled) times the
number of LSM hooks which ends up being quite wasteful.

Suggested-by: Kui-Feng Lee <sinquersw@gmail.com>
Suggested-by: Andrii Nakryiko <andrii@kernel.org>
Acked-by: Song Liu <song@kernel.org>
Signed-off-by: KP Singh <kpsingh@kernel.org>
---
 include/linux/lsm_count.h | 114 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 114 insertions(+)
 create mode 100644 include/linux/lsm_count.h

diff --git a/include/linux/lsm_count.h b/include/linux/lsm_count.h
new file mode 100644
index 000000000000..dbb3c8573959
--- /dev/null
+++ b/include/linux/lsm_count.h
@@ -0,0 +1,114 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Copyright (C) 2023 Google LLC.
+ */
+
+#ifndef __LINUX_LSM_COUNT_H
+#define __LINUX_LSM_COUNT_H
+
+#include <linux/args.h>
+
+#ifdef CONFIG_SECURITY
+
+/*
+ * Macros to count the number of LSMs enabled in the kernel at compile time.
+ */
+
+/*
+ * Capabilities is enabled when CONFIG_SECURITY is enabled.
+ */
+#if IS_ENABLED(CONFIG_SECURITY)
+#define CAPABILITIES_ENABLED 1,
+#else
+#define CAPABILITIES_ENABLED
+#endif
+
+#if IS_ENABLED(CONFIG_SECURITY_SELINUX)
+#define SELINUX_ENABLED 1,
+#else
+#define SELINUX_ENABLED
+#endif
+
+#if IS_ENABLED(CONFIG_SECURITY_SMACK)
+#define SMACK_ENABLED 1,
+#else
+#define SMACK_ENABLED
+#endif
+
+#if IS_ENABLED(CONFIG_SECURITY_APPARMOR)
+#define APPARMOR_ENABLED 1,
+#else
+#define APPARMOR_ENABLED
+#endif
+
+#if IS_ENABLED(CONFIG_SECURITY_TOMOYO)
+#define TOMOYO_ENABLED 1,
+#else
+#define TOMOYO_ENABLED
+#endif
+
+#if IS_ENABLED(CONFIG_SECURITY_YAMA)
+#define YAMA_ENABLED 1,
+#else
+#define YAMA_ENABLED
+#endif
+
+#if IS_ENABLED(CONFIG_SECURITY_LOADPIN)
+#define LOADPIN_ENABLED 1,
+#else
+#define LOADPIN_ENABLED
+#endif
+
+#if IS_ENABLED(CONFIG_SECURITY_LOCKDOWN_LSM)
+#define LOCKDOWN_ENABLED 1,
+#else
+#define LOCKDOWN_ENABLED
+#endif
+
+#if IS_ENABLED(CONFIG_SECURITY_SAFESETID)
+#define SAFESETID_ENABLED 1,
+#else
+#define SAFESETID_ENABLED
+#endif
+
+#if IS_ENABLED(CONFIG_BPF_LSM)
+#define BPF_LSM_ENABLED 1,
+#else
+#define BPF_LSM_ENABLED
+#endif
+
+#if IS_ENABLED(CONFIG_SECURITY_LANDLOCK)
+#define LANDLOCK_ENABLED 1,
+#else
+#define LANDLOCK_ENABLED
+#endif
+
+/*
+ *  There is a trailing comma that we need to be accounted for. This is done by
+ *  using a skipped argument in __COUNT_LSMS
+ */
+#define __COUNT_LSMS(skipped_arg, args...) COUNT_ARGS(args...)
+#define COUNT_LSMS(args...) __COUNT_LSMS(args)
+
+#define MAX_LSM_COUNT			\
+	COUNT_LSMS(			\
+		CAPABILITIES_ENABLED	\
+		SELINUX_ENABLED		\
+		SMACK_ENABLED		\
+		APPARMOR_ENABLED	\
+		TOMOYO_ENABLED		\
+		YAMA_ENABLED		\
+		LOADPIN_ENABLED		\
+		LOCKDOWN_ENABLED	\
+		SAFESETID_ENABLED	\
+		BPF_LSM_ENABLED		\
+		LANDLOCK_ENABLED)
+
+#else
+
+#define MAX_LSM_COUNT 0
+
+#endif /* CONFIG_SECURITY */
+
+#endif  /* __LINUX_LSM_COUNT_H */
-- 
2.42.0.582.g8ccd20d70d-goog


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v5 3/5] security: Replace indirect LSM hook calls with static calls
  2023-09-28 20:24 [PATCH v5 0/5] Reduce overhead of LSMs with static calls KP Singh
  2023-09-28 20:24 ` [PATCH v5 1/5] kernel: Add helper macros for loop unrolling KP Singh
  2023-09-28 20:24 ` [PATCH v5 2/5] security: Count the LSMs enabled at compile time KP Singh
@ 2023-09-28 20:24 ` KP Singh
  2023-09-30 16:13   ` kernel test robot
  2023-09-28 20:24 ` [PATCH v5 4/5] bpf: Only enable BPF LSM hooks when an LSM program is attached KP Singh
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 24+ messages in thread
From: KP Singh @ 2023-09-28 20:24 UTC (permalink / raw)
  To: linux-security-module, bpf
  Cc: paul, keescook, casey, song, daniel, ast, kpsingh, renauld

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:
   0xffffffff814f0320 <+0>:	endbr64
   0xffffffff814f0324 <+4>:	push   %rbp
   0xffffffff814f0325 <+5>:	push   %r15
   0xffffffff814f0327 <+7>:	push   %r14
   0xffffffff814f0329 <+9>:	push   %rbx
   0xffffffff814f032a <+10>:	mov    %rdx,%rbx
   0xffffffff814f032d <+13>:	mov    %esi,%ebp
   0xffffffff814f032f <+15>:	mov    %rdi,%r14
   0xffffffff814f0332 <+18>:	mov    $0xffffffff834a7030,%r15
   0xffffffff814f0339 <+25>:	mov    (%r15),%r15
   0xffffffff814f033c <+28>:	test   %r15,%r15
   0xffffffff814f033f <+31>:	je     0xffffffff814f0358 <security_file_ioctl+56>
   0xffffffff814f0341 <+33>:	mov    0x18(%r15),%r11
   0xffffffff814f0345 <+37>:	mov    %r14,%rdi
   0xffffffff814f0348 <+40>:	mov    %ebp,%esi
   0xffffffff814f034a <+42>:	mov    %rbx,%rdx

   0xffffffff814f034d <+45>:	call   0xffffffff81f742e0 <__x86_indirect_thunk_array+352>
   				^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    Indirect calls that use retpolines leading to overhead, not just due
    to extra instruction but also branch misses.

   0xffffffff814f0352 <+50>:	test   %eax,%eax
   0xffffffff814f0354 <+52>:	je     0xffffffff814f0339 <security_file_ioctl+25>
   0xffffffff814f0356 <+54>:	jmp    0xffffffff814f035a <security_file_ioctl+58>
   0xffffffff814f0358 <+56>:	xor    %eax,%eax
   0xffffffff814f035a <+58>:	pop    %rbx
   0xffffffff814f035b <+59>:	pop    %r14
   0xffffffff814f035d <+61>:	pop    %r15
   0xffffffff814f035f <+63>:	pop    %rbp
   0xffffffff814f0360 <+64>:	jmp    0xffffffff81f747c4 <__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.

A static key guards whether an LSM static call is enabled or not,
without this static key, for LSM hooks that return an int, the presence
of the hook that returns a default value can create side-effects which
has resulted in bugs [1].

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:
   0xffffffff818f0ca0 <+0>:	endbr64
   0xffffffff818f0ca4 <+4>:	nopl   0x0(%rax,%rax,1)
   0xffffffff818f0ca9 <+9>:	push   %rbp
   0xffffffff818f0caa <+10>:	push   %r14
   0xffffffff818f0cac <+12>:	push   %rbx
   0xffffffff818f0cad <+13>:	mov    %rdx,%rbx
   0xffffffff818f0cb0 <+16>:	mov    %esi,%ebp
   0xffffffff818f0cb2 <+18>:	mov    %rdi,%r14
   0xffffffff818f0cb5 <+21>:	jmp    0xffffffff818f0cc7 <security_file_ioctl+39>
  				^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   Static key enabled for SELinux

   0xffffffff818f0cb7 <+23>:	jmp    0xffffffff818f0cde <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.

   0xffffffff818f0cb9 <+25>:	xor    %eax,%eax
   0xffffffff818f0cbb <+27>:	xchg   %ax,%ax
   0xffffffff818f0cbd <+29>:	pop    %rbx
   0xffffffff818f0cbe <+30>:	pop    %r14
   0xffffffff818f0cc0 <+32>:	pop    %rbp
   0xffffffff818f0cc1 <+33>:	cs jmp 0xffffffff82c00000 <__x86_return_thunk>
   0xffffffff818f0cc7 <+39>:	endbr64
   0xffffffff818f0ccb <+43>:	mov    %r14,%rdi
   0xffffffff818f0cce <+46>:	mov    %ebp,%esi
   0xffffffff818f0cd0 <+48>:	mov    %rbx,%rdx
   0xffffffff818f0cd3 <+51>:	call   0xffffffff81903230 <selinux_file_ioctl>
   				^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   Direct call to SELinux.

   0xffffffff818f0cd8 <+56>:	test   %eax,%eax
   0xffffffff818f0cda <+58>:	jne    0xffffffff818f0cbd <security_file_ioctl+29>
   0xffffffff818f0cdc <+60>:	jmp    0xffffffff818f0cb7 <security_file_ioctl+23>
   0xffffffff818f0cde <+62>:	endbr64
   0xffffffff818f0ce2 <+66>:	mov    %r14,%rdi
   0xffffffff818f0ce5 <+69>:	mov    %ebp,%esi
   0xffffffff818f0ce7 <+71>:	mov    %rbx,%rdx
   0xffffffff818f0cea <+74>:	call   0xffffffff8141e220 <bpf_lsm_file_ioctl>
   				^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   Direct call to BPF LSM.

   0xffffffff818f0cef <+79>:	test   %eax,%eax
   0xffffffff818f0cf1 <+81>:	jne    0xffffffff818f0cbd <security_file_ioctl+29>
   0xffffffff818f0cf3 <+83>:	jmp    0xffffffff818f0cb9 <security_file_ioctl+25>
   0xffffffff818f0cf5 <+85>:	endbr64
   0xffffffff818f0cf9 <+89>:	mov    %r14,%rdi
   0xffffffff818f0cfc <+92>:	mov    %ebp,%esi
   0xffffffff818f0cfe <+94>:	mov    %rbx,%rdx
   0xffffffff818f0d01 <+97>:	pop    %rbx
   0xffffffff818f0d02 <+98>:	pop    %r14
   0xffffffff818f0d04 <+100>:	pop    %rbp
   0xffffffff818f0d05 <+101>:	ret
   0xffffffff818f0d06 <+102>:	int3
   0xffffffff818f0d07 <+103>:	int3
   0xffffffff818f0d08 <+104>:	int3
   0xffffffff818f0d09 <+105>:	int3

While this patch uses static_branch_unlikely indicating that an LSM hook
is likely to be not present, a subsequent makes it configurable. 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 and
call_void_hook. These hooks are updated to use a new macro called
security_for_each_hook where the lsm_callback is directly invoked as an
indirect call. Currently, there are no performance sensitive hooks that
use the security_for_each_hook macro. However, if, some performance
sensitive hooks are discovered, these can be updated to use static calls
with loop unrolling as well using a custom macro.

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%.

Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Acked-by: Song Liu <song@kernel.org>
Signed-off-by: KP Singh <kpsingh@kernel.org>
---
 include/linux/lsm_hooks.h |  70 +++++++++++--
 security/security.c       | 208 +++++++++++++++++++++++++-------------
 2 files changed, 199 insertions(+), 79 deletions(-)

diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index dcb5e5b5eb13..c77a1859214d 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -29,26 +29,77 @@
 #include <linux/init.h>
 #include <linux/rculist.h>
 #include <linux/xattr.h>
+#include <linux/static_call.h>
+#include <linux/unroll.h>
+#include <linux/jump_label.h>
+#include <linux/lsm_count.h>
+
+#define SECURITY_HOOK_ACTIVE_KEY(HOOK, IDX) security_hook_active_##HOOK##_##IDX
+
+/*
+ * Identifier for the LSM static calls.
+ * HOOK is an LSM hook as defined in linux/lsm_hookdefs.h
+ * IDX is the index of the static call. 0 <= NUM < MAX_LSM_COUNT
+ */
+#define LSM_STATIC_CALL(HOOK, IDX) lsm_static_call_##HOOK##_##IDX
+
+/*
+ * Call the macro M for each LSM hook MAX_LSM_COUNT times.
+ */
+#define LSM_LOOP_UNROLL(M, ...) 		\
+do {						\
+	UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__)	\
+} while (0)
+
+#define LSM_DEFINE_UNROLL(M, ...) UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__)
 
 union security_list_options {
 	#define LSM_HOOK(RET, DEFAULT, NAME, ...) RET (*NAME)(__VA_ARGS__);
 	#include "lsm_hook_defs.h"
 	#undef LSM_HOOK
+	void *lsm_callback;
 };
 
-struct security_hook_heads {
-	#define LSM_HOOK(RET, DEFAULT, NAME, ...) struct hlist_head NAME;
-	#include "lsm_hook_defs.h"
+/*
+ * @key: static call key as defined by STATIC_CALL_KEY
+ * @trampoline: static call trampoline as defined by STATIC_CALL_TRAMP
+ * @hl: The security_hook_list as initialized by the owning LSM.
+ * @active: Enabled when the static call has an LSM hook associated.
+ */
+struct lsm_static_call {
+	struct static_call_key *key;
+	void *trampoline;
+	struct security_hook_list *hl;
+	/* this needs to be true or false based on what the key defaults to */
+	struct static_key_false *active;
+} __randomize_layout;
+
+/*
+ * Table of the static calls for each LSM hook.
+ * Once the LSMs are initialized, their callbacks will be copied to these
+ * tables such that the calls are filled backwards (from last to first).
+ * This way, we can jump directly to the first used static call, and execute
+ * all of them after. This essentially makes the entry point
+ * dynamic to adapt the number of static calls to the number of callbacks.
+ */
+struct lsm_static_calls_table {
+	#define LSM_HOOK(RET, DEFAULT, NAME, ...) \
+		struct lsm_static_call NAME[MAX_LSM_COUNT];
+	#include <linux/lsm_hook_defs.h>
 	#undef LSM_HOOK
 } __randomize_layout;
 
 /*
  * Security module hook list structure.
  * For use with generic list macros for common operations.
+ *
+ * struct security_hook_list - Contents of a cacheable, mappable object.
+ * @scalls: The beginning of the array of static calls assigned to this hook.
+ * @hook: The callback for the hook.
+ * @lsm: The name of the lsm that owns this hook.
  */
 struct security_hook_list {
-	struct hlist_node		list;
-	struct hlist_head		*head;
+	struct lsm_static_call	*scalls;
 	union security_list_options	hook;
 	const char			*lsm;
 } __randomize_layout;
@@ -97,10 +148,12 @@ static inline struct xattr *lsm_get_xattr_slot(struct xattr *xattrs,
  * care of the common case and reduces the amount of
  * text involved.
  */
-#define LSM_HOOK_INIT(HEAD, HOOK) \
-	{ .head = &security_hook_heads.HEAD, .hook = { .HEAD = HOOK } }
+#define LSM_HOOK_INIT(NAME, CALLBACK)			\
+	{						\
+		.scalls = static_calls_table.NAME,	\
+		.hook = { .NAME = CALLBACK }		\
+	}
 
-extern struct security_hook_heads security_hook_heads;
 extern char *lsm_names;
 
 extern void security_add_hooks(struct security_hook_list *hooks, int count,
@@ -138,5 +191,6 @@ extern struct lsm_info __start_early_lsm_info[], __end_early_lsm_info[];
 		__aligned(sizeof(unsigned long))
 
 extern int lsm_inode_alloc(struct inode *inode);
+extern struct lsm_static_calls_table static_calls_table __ro_after_init;
 
 #endif /* ! __LINUX_LSM_HOOKS_H */
diff --git a/security/security.c b/security/security.c
index 7b0052e96806..c2c2cf6b711f 100644
--- a/security/security.c
+++ b/security/security.c
@@ -30,6 +30,8 @@
 #include <linux/string.h>
 #include <linux/msg.h>
 #include <net/flow.h>
+#include <linux/static_call.h>
+#include <linux/jump_label.h>
 
 /* How many LSMs were built into the kernel? */
 #define LSM_COUNT (__end_lsm_info - __start_lsm_info)
@@ -73,7 +75,6 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = {
 	[LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality",
 };
 
-struct security_hook_heads security_hook_heads __ro_after_init;
 static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain);
 
 static struct kmem_cache *lsm_file_cache;
@@ -92,6 +93,51 @@ static __initconst const char *const builtin_lsm_order = CONFIG_LSM;
 static __initdata struct lsm_info **ordered_lsms;
 static __initdata struct lsm_info *exclusive;
 
+
+#ifdef CONFIG_HAVE_STATIC_CALL
+#define LSM_HOOK_TRAMP(NAME, NUM) \
+	&STATIC_CALL_TRAMP(LSM_STATIC_CALL(NAME, NUM))
+#else
+#define LSM_HOOK_TRAMP(NAME, NUM) NULL
+#endif
+
+/*
+ * Define static calls and static keys for each LSM hook.
+ */
+
+#define DEFINE_LSM_STATIC_CALL(NUM, NAME, RET, ...)			\
+	DEFINE_STATIC_CALL_NULL(LSM_STATIC_CALL(NAME, NUM),		\
+				*((RET(*)(__VA_ARGS__))NULL));		\
+	DEFINE_STATIC_KEY_FALSE(SECURITY_HOOK_ACTIVE_KEY(NAME, NUM));
+
+#define LSM_HOOK(RET, DEFAULT, NAME, ...)				\
+	LSM_DEFINE_UNROLL(DEFINE_LSM_STATIC_CALL, NAME, RET, __VA_ARGS__)
+#include <linux/lsm_hook_defs.h>
+#undef LSM_HOOK
+#undef DEFINE_LSM_STATIC_CALL
+
+/*
+ * Initialise a table of static calls for each LSM hook.
+ * DEFINE_STATIC_CALL_NULL invocation above generates a key (STATIC_CALL_KEY)
+ * and a trampoline (STATIC_CALL_TRAMP) which are used to call
+ * __static_call_update when updating the static call.
+ */
+struct lsm_static_calls_table static_calls_table __ro_after_init = {
+#define INIT_LSM_STATIC_CALL(NUM, NAME)					\
+	(struct lsm_static_call) {					\
+		.key = &STATIC_CALL_KEY(LSM_STATIC_CALL(NAME, NUM)),	\
+		.trampoline = LSM_HOOK_TRAMP(NAME, NUM),		\
+		.active = &SECURITY_HOOK_ACTIVE_KEY(NAME, NUM),		\
+	},
+#define LSM_HOOK(RET, DEFAULT, NAME, ...)				\
+	.NAME = {							\
+		LSM_DEFINE_UNROLL(INIT_LSM_STATIC_CALL, NAME)		\
+	},
+#include <linux/lsm_hook_defs.h>
+#undef LSM_HOOK
+#undef INIT_LSM_STATIC_CALL
+};
+
 static __initdata bool debug;
 #define init_debug(...)						\
 	do {							\
@@ -152,7 +198,7 @@ static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from)
 	if (exists_ordered_lsm(lsm))
 		return;
 
-	if (WARN(last_lsm == LSM_COUNT, "%s: out of LSM slots!?\n", from))
+	if (WARN(last_lsm == LSM_COUNT, "%s: out of LSM static calls!?\n", from))
 		return;
 
 	/* Enable this LSM, if it is not already set. */
@@ -325,6 +371,25 @@ static void __init ordered_lsm_parse(const char *order, const char *origin)
 	kfree(sep);
 }
 
+static void __init lsm_static_call_init(struct security_hook_list *hl)
+{
+	struct lsm_static_call *scall = hl->scalls;
+	int i;
+
+	for (i = 0; i < MAX_LSM_COUNT; i++) {
+		/* Update the first static call that is not used yet */
+		if (!scall->hl) {
+			__static_call_update(scall->key, scall->trampoline,
+					     hl->hook.lsm_callback);
+			scall->hl = hl;
+			static_branch_enable(scall->active);
+			return;
+		}
+		scall++;
+	}
+	panic("%s - Ran out of static slots.\n", __func__);
+}
+
 static void __init lsm_early_cred(struct cred *cred);
 static void __init lsm_early_task(struct task_struct *task);
 
@@ -404,11 +469,6 @@ int __init early_security_init(void)
 {
 	struct lsm_info *lsm;
 
-#define LSM_HOOK(RET, DEFAULT, NAME, ...) \
-	INIT_HLIST_HEAD(&security_hook_heads.NAME);
-#include "linux/lsm_hook_defs.h"
-#undef LSM_HOOK
-
 	for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
 		if (!lsm->enabled)
 			lsm->enabled = &lsm_enabled_true;
@@ -524,7 +584,7 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count,
 
 	for (i = 0; i < count; i++) {
 		hooks[i].lsm = lsm;
-		hlist_add_tail_rcu(&hooks[i].list, hooks[i].head);
+		lsm_static_call_init(&hooks[i]);
 	}
 
 	/*
@@ -762,29 +822,41 @@ static int lsm_superblock_alloc(struct super_block *sb)
  * call_int_hook:
  *	This is a hook that returns a value.
  */
+#define __CALL_STATIC_VOID(NUM, HOOK, ...)				     \
+do {									     \
+	if (static_branch_unlikely(&SECURITY_HOOK_ACTIVE_KEY(HOOK, NUM))) {    \
+		static_call(LSM_STATIC_CALL(HOOK, NUM))(__VA_ARGS__);	     \
+	}								     \
+} while (0);
 
-#define call_void_hook(FUNC, ...)				\
-	do {							\
-		struct security_hook_list *P;			\
-								\
-		hlist_for_each_entry(P, &security_hook_heads.FUNC, list) \
-			P->hook.FUNC(__VA_ARGS__);		\
+#define call_void_hook(FUNC, ...)                                 \
+	do {                                                      \
+		LSM_LOOP_UNROLL(__CALL_STATIC_VOID, FUNC, __VA_ARGS__); \
 	} while (0)
 
-#define call_int_hook(FUNC, IRC, ...) ({			\
-	int RC = IRC;						\
-	do {							\
-		struct security_hook_list *P;			\
-								\
-		hlist_for_each_entry(P, &security_hook_heads.FUNC, list) { \
-			RC = P->hook.FUNC(__VA_ARGS__);		\
-			if (RC != 0)				\
-				break;				\
-		}						\
-	} while (0);						\
-	RC;							\
+#define __CALL_STATIC_INT(NUM, R, HOOK, LABEL, ...)			     \
+do {									     \
+	if (static_branch_unlikely(&SECURITY_HOOK_ACTIVE_KEY(HOOK, NUM))) {  \
+		R = static_call(LSM_STATIC_CALL(HOOK, NUM))(__VA_ARGS__);    \
+		if (R != 0)						     \
+			goto LABEL;					     \
+	}								     \
+} while (0);
+
+#define call_int_hook(FUNC, IRC, ...)					\
+({									\
+	__label__ out;							\
+	int RC = IRC;							\
+	LSM_LOOP_UNROLL(__CALL_STATIC_INT, RC, FUNC, out, __VA_ARGS__);	\
+out:									\
+	RC;								\
 })
 
+#define lsm_for_each_hook(scall, NAME)					\
+	for (scall = static_calls_table.NAME;				\
+	     scall - static_calls_table.NAME < MAX_LSM_COUNT; scall++)  \
+		if (static_key_enabled(&scall->active->key))
+
 /* Security operations */
 
 /**
@@ -1020,7 +1092,7 @@ int security_settime64(const struct timespec64 *ts, const struct timezone *tz)
  */
 int security_vm_enough_memory_mm(struct mm_struct *mm, long pages)
 {
-	struct security_hook_list *hp;
+	struct lsm_static_call *scall;
 	int cap_sys_admin = 1;
 	int rc;
 
@@ -1031,8 +1103,8 @@ int security_vm_enough_memory_mm(struct mm_struct *mm, long pages)
 	 * agree that it should be set it will. If any module
 	 * thinks it should not be set it won't.
 	 */
-	hlist_for_each_entry(hp, &security_hook_heads.vm_enough_memory, list) {
-		rc = hp->hook.vm_enough_memory(mm, pages);
+	lsm_for_each_hook(scall, vm_enough_memory) {
+		rc = scall->hl->hook.vm_enough_memory(mm, pages);
 		if (rc <= 0) {
 			cap_sys_admin = 0;
 			break;
@@ -1184,13 +1256,12 @@ int security_fs_context_dup(struct fs_context *fc, struct fs_context *src_fc)
 int security_fs_context_parse_param(struct fs_context *fc,
 				    struct fs_parameter *param)
 {
-	struct security_hook_list *hp;
+	struct lsm_static_call *scall;
 	int trc;
 	int rc = -ENOPARAM;
 
-	hlist_for_each_entry(hp, &security_hook_heads.fs_context_parse_param,
-			     list) {
-		trc = hp->hook.fs_context_parse_param(fc, param);
+	lsm_for_each_hook(scall, fs_context_parse_param) {
+		trc = scall->hl->hook.fs_context_parse_param(fc, param);
 		if (trc == 0)
 			rc = 0;
 		else if (trc != -ENOPARAM)
@@ -1553,19 +1624,19 @@ int security_dentry_init_security(struct dentry *dentry, int mode,
 				  const char **xattr_name, void **ctx,
 				  u32 *ctxlen)
 {
-	struct security_hook_list *hp;
+	struct lsm_static_call *scall;
 	int rc;
 
 	/*
 	 * Only one module will provide a security context.
 	 */
-	hlist_for_each_entry(hp, &security_hook_heads.dentry_init_security,
-			     list) {
-		rc = hp->hook.dentry_init_security(dentry, mode, name,
+	lsm_for_each_hook(scall, dentry_init_security) {
+		rc = scall->hl->hook.dentry_init_security(dentry, mode, name,
 						   xattr_name, ctx, ctxlen);
 		if (rc != LSM_RET_DEFAULT(dentry_init_security))
 			return rc;
 	}
+
 	return LSM_RET_DEFAULT(dentry_init_security);
 }
 EXPORT_SYMBOL(security_dentry_init_security);
@@ -1625,7 +1696,7 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
 				 const struct qstr *qstr,
 				 const initxattrs initxattrs, void *fs_data)
 {
-	struct security_hook_list *hp;
+	struct lsm_static_call *scall;
 	struct xattr *new_xattrs = NULL;
 	int ret = -EOPNOTSUPP, xattr_count = 0;
 
@@ -1643,9 +1714,8 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
 			return -ENOMEM;
 	}
 
-	hlist_for_each_entry(hp, &security_hook_heads.inode_init_security,
-			     list) {
-		ret = hp->hook.inode_init_security(inode, dir, qstr, new_xattrs,
+	lsm_for_each_hook(scall, inode_init_security) {
+		ret = scall->hl->hook.inode_init_security(inode, dir, qstr, new_xattrs,
 						  &xattr_count);
 		if (ret && ret != -EOPNOTSUPP)
 			goto out;
@@ -2405,7 +2475,7 @@ int security_inode_getsecurity(struct mnt_idmap *idmap,
 			       struct inode *inode, const char *name,
 			       void **buffer, bool alloc)
 {
-	struct security_hook_list *hp;
+	struct lsm_static_call *scall;
 	int rc;
 
 	if (unlikely(IS_PRIVATE(inode)))
@@ -2413,9 +2483,8 @@ int security_inode_getsecurity(struct mnt_idmap *idmap,
 	/*
 	 * Only one module will provide an attribute with a given name.
 	 */
-	hlist_for_each_entry(hp, &security_hook_heads.inode_getsecurity, list) {
-		rc = hp->hook.inode_getsecurity(idmap, inode, name, buffer,
-						alloc);
+	lsm_for_each_hook(scall, inode_getsecurity) {
+		rc = scall->hl->hook.inode_getsecurity(idmap, inode, name, buffer, alloc);
 		if (rc != LSM_RET_DEFAULT(inode_getsecurity))
 			return rc;
 	}
@@ -2440,7 +2509,7 @@ int security_inode_getsecurity(struct mnt_idmap *idmap,
 int security_inode_setsecurity(struct inode *inode, const char *name,
 			       const void *value, size_t size, int flags)
 {
-	struct security_hook_list *hp;
+	struct lsm_static_call *scall;
 	int rc;
 
 	if (unlikely(IS_PRIVATE(inode)))
@@ -2448,9 +2517,8 @@ int security_inode_setsecurity(struct inode *inode, const char *name,
 	/*
 	 * Only one module will provide an attribute with a given name.
 	 */
-	hlist_for_each_entry(hp, &security_hook_heads.inode_setsecurity, list) {
-		rc = hp->hook.inode_setsecurity(inode, name, value, size,
-						flags);
+	lsm_for_each_hook(scall, inode_setsecurity) {
+		rc = scall->hl->hook.inode_setsecurity(inode, name, value, size, flags);
 		if (rc != LSM_RET_DEFAULT(inode_setsecurity))
 			return rc;
 	}
@@ -2524,7 +2592,7 @@ EXPORT_SYMBOL(security_inode_copy_up);
  */
 int security_inode_copy_up_xattr(const char *name)
 {
-	struct security_hook_list *hp;
+	struct lsm_static_call *scall;
 	int rc;
 
 	/*
@@ -2532,9 +2600,8 @@ int security_inode_copy_up_xattr(const char *name)
 	 * xattr), -EOPNOTSUPP if it does not know anything about the xattr or
 	 * any other error code in case of an error.
 	 */
-	hlist_for_each_entry(hp,
-			     &security_hook_heads.inode_copy_up_xattr, list) {
-		rc = hp->hook.inode_copy_up_xattr(name);
+	lsm_for_each_hook(scall, inode_copy_up_xattr) {
+		rc = scall->hl->hook.inode_copy_up_xattr(name);
 		if (rc != LSM_RET_DEFAULT(inode_copy_up_xattr))
 			return rc;
 	}
@@ -3414,10 +3481,10 @@ int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
 {
 	int thisrc;
 	int rc = LSM_RET_DEFAULT(task_prctl);
-	struct security_hook_list *hp;
+	struct lsm_static_call *scall;
 
-	hlist_for_each_entry(hp, &security_hook_heads.task_prctl, list) {
-		thisrc = hp->hook.task_prctl(option, arg2, arg3, arg4, arg5);
+	lsm_for_each_hook(scall, task_prctl) {
+		thisrc = scall->hl->hook.task_prctl(option, arg2, arg3, arg4, arg5);
 		if (thisrc != LSM_RET_DEFAULT(task_prctl)) {
 			rc = thisrc;
 			if (thisrc != 0)
@@ -3814,12 +3881,12 @@ EXPORT_SYMBOL(security_d_instantiate);
 int security_getprocattr(struct task_struct *p, const char *lsm,
 			 const char *name, char **value)
 {
-	struct security_hook_list *hp;
+	struct lsm_static_call *scall;
 
-	hlist_for_each_entry(hp, &security_hook_heads.getprocattr, list) {
-		if (lsm != NULL && strcmp(lsm, hp->lsm))
+	lsm_for_each_hook(scall, getprocattr) {
+		if (lsm != NULL && strcmp(lsm, scall->hl->lsm))
 			continue;
-		return hp->hook.getprocattr(p, name, value);
+		return scall->hl->hook.getprocattr(p, name, value);
 	}
 	return LSM_RET_DEFAULT(getprocattr);
 }
@@ -3839,12 +3906,12 @@ int security_getprocattr(struct task_struct *p, const char *lsm,
 int security_setprocattr(const char *lsm, const char *name, void *value,
 			 size_t size)
 {
-	struct security_hook_list *hp;
+	struct lsm_static_call *scall;
 
-	hlist_for_each_entry(hp, &security_hook_heads.setprocattr, list) {
-		if (lsm != NULL && strcmp(lsm, hp->lsm))
+	lsm_for_each_hook(scall, setprocattr) {
+		if (lsm != NULL && strcmp(lsm, scall->hl->lsm))
 			continue;
-		return hp->hook.setprocattr(name, value, size);
+		return scall->hl->hook.setprocattr(name, value, size);
 	}
 	return LSM_RET_DEFAULT(setprocattr);
 }
@@ -3896,15 +3963,15 @@ EXPORT_SYMBOL(security_ismaclabel);
  */
 int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
 {
-	struct security_hook_list *hp;
+	struct lsm_static_call *scall;
 	int rc;
 
 	/*
 	 * Currently, only one LSM can implement secid_to_secctx (i.e this
 	 * LSM hook is not "stackable").
 	 */
-	hlist_for_each_entry(hp, &security_hook_heads.secid_to_secctx, list) {
-		rc = hp->hook.secid_to_secctx(secid, secdata, seclen);
+	lsm_for_each_hook(scall, secid_to_secctx) {
+		rc = scall->hl->hook.secid_to_secctx(secid, secdata, seclen);
 		if (rc != LSM_RET_DEFAULT(secid_to_secctx))
 			return rc;
 	}
@@ -4947,7 +5014,7 @@ int security_xfrm_state_pol_flow_match(struct xfrm_state *x,
 				       struct xfrm_policy *xp,
 				       const struct flowi_common *flic)
 {
-	struct security_hook_list *hp;
+	struct lsm_static_call *scall;
 	int rc = LSM_RET_DEFAULT(xfrm_state_pol_flow_match);
 
 	/*
@@ -4959,9 +5026,8 @@ int security_xfrm_state_pol_flow_match(struct xfrm_state *x,
 	 * For speed optimization, we explicitly break the loop rather than
 	 * using the macro
 	 */
-	hlist_for_each_entry(hp, &security_hook_heads.xfrm_state_pol_flow_match,
-			     list) {
-		rc = hp->hook.xfrm_state_pol_flow_match(x, xp, flic);
+	lsm_for_each_hook(scall, xfrm_state_pol_flow_match) {
+		rc = scall->hl->hook.xfrm_state_pol_flow_match(x, xp, flic);
 		break;
 	}
 	return rc;
-- 
2.42.0.582.g8ccd20d70d-goog


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v5 4/5] bpf: Only enable BPF LSM hooks when an LSM program is attached
  2023-09-28 20:24 [PATCH v5 0/5] Reduce overhead of LSMs with static calls KP Singh
                   ` (2 preceding siblings ...)
  2023-09-28 20:24 ` [PATCH v5 3/5] security: Replace indirect LSM hook calls with static calls KP Singh
@ 2023-09-28 20:24 ` KP Singh
  2023-10-05  8:09   ` Jiri Olsa
  2023-09-28 20:24 ` [PATCH v5 5/5] security: Add CONFIG_SECURITY_HOOK_LIKELY KP Singh
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 24+ messages in thread
From: KP Singh @ 2023-09-28 20:24 UTC (permalink / raw)
  To: linux-security-module, bpf
  Cc: paul, keescook, casey, song, daniel, ast, kpsingh, renauld

BPF LSM hooks have side-effects (even when a default value is returned),
as some hooks end up behaving differently due to the very presence of
the hook.

The static keys guarding the BPF LSM hooks are disabled by default and
enabled only when a BPF program is attached implementing the hook
logic. This avoids the issue of the side-effects and also the minor
overhead associated with the empty callback.

security_file_ioctl:
   0xffffffff818f0e30 <+0>:	endbr64
   0xffffffff818f0e34 <+4>:	nopl   0x0(%rax,%rax,1)
   0xffffffff818f0e39 <+9>:	push   %rbp
   0xffffffff818f0e3a <+10>:	push   %r14
   0xffffffff818f0e3c <+12>:	push   %rbx
   0xffffffff818f0e3d <+13>:	mov    %rdx,%rbx
   0xffffffff818f0e40 <+16>:	mov    %esi,%ebp
   0xffffffff818f0e42 <+18>:	mov    %rdi,%r14
   0xffffffff818f0e45 <+21>:	jmp    0xffffffff818f0e57 <security_file_ioctl+39>
   				^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

   Static key enabled for SELinux

   0xffffffff818f0e47 <+23>:	xchg   %ax,%ax
   				^^^^^^^^^^^^^^

   Static key disabled for BPF. This gets patched when a BPF LSM program
   is attached

   0xffffffff818f0e49 <+25>:	xor    %eax,%eax
   0xffffffff818f0e4b <+27>:	xchg   %ax,%ax
   0xffffffff818f0e4d <+29>:	pop    %rbx
   0xffffffff818f0e4e <+30>:	pop    %r14
   0xffffffff818f0e50 <+32>:	pop    %rbp
   0xffffffff818f0e51 <+33>:	cs jmp 0xffffffff82c00000 <__x86_return_thunk>
   0xffffffff818f0e57 <+39>:	endbr64
   0xffffffff818f0e5b <+43>:	mov    %r14,%rdi
   0xffffffff818f0e5e <+46>:	mov    %ebp,%esi
   0xffffffff818f0e60 <+48>:	mov    %rbx,%rdx
   0xffffffff818f0e63 <+51>:	call   0xffffffff819033c0 <selinux_file_ioctl>
   0xffffffff818f0e68 <+56>:	test   %eax,%eax
   0xffffffff818f0e6a <+58>:	jne    0xffffffff818f0e4d <security_file_ioctl+29>
   0xffffffff818f0e6c <+60>:	jmp    0xffffffff818f0e47 <security_file_ioctl+23>
   0xffffffff818f0e6e <+62>:	endbr64
   0xffffffff818f0e72 <+66>:	mov    %r14,%rdi
   0xffffffff818f0e75 <+69>:	mov    %ebp,%esi
   0xffffffff818f0e77 <+71>:	mov    %rbx,%rdx
   0xffffffff818f0e7a <+74>:	call   0xffffffff8141e3b0 <bpf_lsm_file_ioctl>
   0xffffffff818f0e7f <+79>:	test   %eax,%eax
   0xffffffff818f0e81 <+81>:	jne    0xffffffff818f0e4d <security_file_ioctl+29>
   0xffffffff818f0e83 <+83>:	jmp    0xffffffff818f0e49 <security_file_ioctl+25>
   0xffffffff818f0e85 <+85>:	endbr64
   0xffffffff818f0e89 <+89>:	mov    %r14,%rdi
   0xffffffff818f0e8c <+92>:	mov    %ebp,%esi
   0xffffffff818f0e8e <+94>:	mov    %rbx,%rdx
   0xffffffff818f0e91 <+97>:	pop    %rbx
   0xffffffff818f0e92 <+98>:	pop    %r14
   0xffffffff818f0e94 <+100>:	pop    %rbp
   0xffffffff818f0e95 <+101>:	ret

Reviewed-by: Kees Cook <keescook@chromium.org>
Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
Acked-by: Song Liu <song@kernel.org>
Signed-off-by: KP Singh <kpsingh@kernel.org>
---
 include/linux/bpf.h       |  1 +
 include/linux/bpf_lsm.h   |  5 +++++
 include/linux/lsm_hooks.h | 13 ++++++++++++-
 kernel/bpf/trampoline.c   | 29 +++++++++++++++++++++++++++--
 security/bpf/hooks.c      | 25 ++++++++++++++++++++++++-
 security/security.c       |  3 ++-
 6 files changed, 71 insertions(+), 5 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 08b10a422560..9a81e0396aaa 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1159,6 +1159,7 @@ struct bpf_attach_target_info {
 	struct module *tgt_mod;
 	const char *tgt_name;
 	const struct btf_type *tgt_type;
+	bool is_lsm_target;
 };
 
 #define BPF_DISPATCHER_MAX 48 /* Fits in 2048B */
diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h
index 1de7ece5d36d..5bbc31ac948c 100644
--- a/include/linux/bpf_lsm.h
+++ b/include/linux/bpf_lsm.h
@@ -29,6 +29,7 @@ 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);
+void bpf_lsm_toggle_hook(void *addr, bool value);
 
 static inline struct bpf_storage_blob *bpf_inode(
 	const struct inode *inode)
@@ -78,6 +79,10 @@ static inline void bpf_lsm_find_cgroup_shim(const struct bpf_prog *prog,
 {
 }
 
+static inline void bpf_lsm_toggle_hook(void *addr, bool value)
+{
+}
+
 #endif /* CONFIG_BPF_LSM */
 
 #endif /* _LINUX_BPF_LSM_H */
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index c77a1859214d..57ffe4eb6d30 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -97,11 +97,14 @@ struct lsm_static_calls_table {
  * @scalls: The beginning of the array of static calls assigned to this hook.
  * @hook: The callback for the hook.
  * @lsm: The name of the lsm that owns this hook.
+ * @default_state: The state of the LSM hook when initialized. If set to false,
+ * the static key guarding the hook will be set to disabled.
  */
 struct security_hook_list {
 	struct lsm_static_call	*scalls;
 	union security_list_options	hook;
 	const char			*lsm;
+	bool				default_state;
 } __randomize_layout;
 
 /*
@@ -151,7 +154,15 @@ static inline struct xattr *lsm_get_xattr_slot(struct xattr *xattrs,
 #define LSM_HOOK_INIT(NAME, CALLBACK)			\
 	{						\
 		.scalls = static_calls_table.NAME,	\
-		.hook = { .NAME = CALLBACK }		\
+		.hook = { .NAME = CALLBACK },		\
+		.default_state = true			\
+	}
+
+#define LSM_HOOK_INIT_DISABLED(NAME, CALLBACK)		\
+	{						\
+		.scalls = static_calls_table.NAME,	\
+		.hook = { .NAME = CALLBACK },		\
+		.default_state = false			\
 	}
 
 extern char *lsm_names;
diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
index e97aeda3a86b..df9699bce372 100644
--- a/kernel/bpf/trampoline.c
+++ b/kernel/bpf/trampoline.c
@@ -13,6 +13,7 @@
 #include <linux/bpf_verifier.h>
 #include <linux/bpf_lsm.h>
 #include <linux/delay.h>
+#include <linux/bpf_lsm.h>
 
 /* dummy _ops. The verifier will operate on target program's ops. */
 const struct bpf_verifier_ops bpf_extension_verifier_ops = {
@@ -514,7 +515,7 @@ static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_tr
 {
 	enum bpf_tramp_prog_type kind;
 	struct bpf_tramp_link *link_exiting;
-	int err = 0;
+	int err = 0, num_lsm_progs = 0;
 	int cnt = 0, i;
 
 	kind = bpf_attach_type_to_tramp(link->link.prog);
@@ -545,8 +546,14 @@ static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_tr
 			continue;
 		/* prog already linked */
 		return -EBUSY;
+
+		if (link_exiting->link.prog->type == BPF_PROG_TYPE_LSM)
+			num_lsm_progs++;
 	}
 
+	if (!num_lsm_progs && link->link.prog->type == BPF_PROG_TYPE_LSM)
+		bpf_lsm_toggle_hook(tr->func.addr, true);
+
 	hlist_add_head(&link->tramp_hlist, &tr->progs_hlist[kind]);
 	tr->progs_cnt[kind]++;
 	err = bpf_trampoline_update(tr, true /* lock_direct_mutex */);
@@ -569,8 +576,10 @@ int bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_trampoline
 
 static int __bpf_trampoline_unlink_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr)
 {
+	struct bpf_tramp_link *link_exiting;
 	enum bpf_tramp_prog_type kind;
-	int err;
+	bool lsm_link_found = false;
+	int err, num_lsm_progs = 0;
 
 	kind = bpf_attach_type_to_tramp(link->link.prog);
 	if (kind == BPF_TRAMP_REPLACE) {
@@ -580,8 +589,24 @@ static int __bpf_trampoline_unlink_prog(struct bpf_tramp_link *link, struct bpf_
 		tr->extension_prog = NULL;
 		return err;
 	}
+
+	if (link->link.prog->type == BPF_PROG_TYPE_LSM) {
+		hlist_for_each_entry(link_exiting, &tr->progs_hlist[kind],
+				     tramp_hlist) {
+			if (link_exiting->link.prog->type == BPF_PROG_TYPE_LSM)
+				num_lsm_progs++;
+
+			if (link_exiting->link.prog == link->link.prog)
+				lsm_link_found = true;
+		}
+	}
+
 	hlist_del_init(&link->tramp_hlist);
 	tr->progs_cnt[kind]--;
+
+	if (lsm_link_found && num_lsm_progs == 1)
+		bpf_lsm_toggle_hook(tr->func.addr, false);
+
 	return bpf_trampoline_update(tr, true /* lock_direct_mutex */);
 }
 
diff --git a/security/bpf/hooks.c b/security/bpf/hooks.c
index cfaf1d0e6a5f..1957244196d0 100644
--- a/security/bpf/hooks.c
+++ b/security/bpf/hooks.c
@@ -8,7 +8,7 @@
 
 static struct security_hook_list bpf_lsm_hooks[] __ro_after_init = {
 	#define LSM_HOOK(RET, DEFAULT, NAME, ...) \
-	LSM_HOOK_INIT(NAME, bpf_lsm_##NAME),
+	LSM_HOOK_INIT_DISABLED(NAME, bpf_lsm_##NAME),
 	#include <linux/lsm_hook_defs.h>
 	#undef LSM_HOOK
 	LSM_HOOK_INIT(inode_free_security, bpf_inode_storage_free),
@@ -32,3 +32,26 @@ DEFINE_LSM(bpf) = {
 	.init = bpf_lsm_init,
 	.blobs = &bpf_lsm_blob_sizes
 };
+
+void bpf_lsm_toggle_hook(void *addr, bool value)
+{
+	struct lsm_static_call *scalls;
+	struct security_hook_list *h;
+	int i, j;
+
+	for (i = 0; i < ARRAY_SIZE(bpf_lsm_hooks); i++) {
+		h = &bpf_lsm_hooks[i];
+		scalls = h->scalls;
+		if (h->hook.lsm_callback == addr)
+			continue;
+
+		for (j = 0; j < MAX_LSM_COUNT; j++) {
+			if (scalls[j].hl != h)
+				continue;
+			if (value)
+				static_branch_enable(scalls[j].active);
+			else
+				static_branch_disable(scalls[j].active);
+		}
+	}
+}
diff --git a/security/security.c b/security/security.c
index c2c2cf6b711f..d1ee72e563cc 100644
--- a/security/security.c
+++ b/security/security.c
@@ -382,7 +382,8 @@ static void __init lsm_static_call_init(struct security_hook_list *hl)
 			__static_call_update(scall->key, scall->trampoline,
 					     hl->hook.lsm_callback);
 			scall->hl = hl;
-			static_branch_enable(scall->active);
+			if (hl->default_state)
+				static_branch_enable(scall->active);
 			return;
 		}
 		scall++;
-- 
2.42.0.582.g8ccd20d70d-goog


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v5 5/5] security: Add CONFIG_SECURITY_HOOK_LIKELY
  2023-09-28 20:24 [PATCH v5 0/5] Reduce overhead of LSMs with static calls KP Singh
                   ` (3 preceding siblings ...)
  2023-09-28 20:24 ` [PATCH v5 4/5] bpf: Only enable BPF LSM hooks when an LSM program is attached KP Singh
@ 2023-09-28 20:24 ` KP Singh
  2023-09-29  0:38   ` Kees Cook
  2023-09-29  0:41 ` [PATCH v5 0/5] Reduce overhead of LSMs with static calls Kees Cook
  2023-10-02 11:06 ` Paolo Abeni
  6 siblings, 1 reply; 24+ messages in thread
From: KP Singh @ 2023-09-28 20:24 UTC (permalink / raw)
  To: linux-security-module, bpf
  Cc: paul, keescook, casey, song, daniel, ast, kpsingh, renauld

This config influences the nature of the static key that guards the
static call for LSM hooks.

When enabled, it indicates that an LSM static call slot is more likely
to be initialized. When disabled, it optimizes for the case when static
call slot is more likely to be not initialized.

When a major LSM like (SELinux, AppArmor, Smack etc) is active on a
system the system would benefit from enabling the config. However there
are other cases which would benefit from the config being disabled
(e.g. a system with a BPF LSM with no hooks enabled by default, or an
LSM like loadpin / yama). Ultimately, there is no one-size fits all
solution.

with CONFIG_SECURITY_HOOK_LIKELY enabled, the inactive /
uninitialized case is penalized with a direct jmp (still better than
an indirect jmp):

function security_file_ioctl:
   0xffffffff818f0c80 <+0>:	endbr64
   0xffffffff818f0c84 <+4>:	nopl   0x0(%rax,%rax,1)
   0xffffffff818f0c89 <+9>:	push   %rbp
   0xffffffff818f0c8a <+10>:	push   %r14
   0xffffffff818f0c8c <+12>:	push   %rbx
   0xffffffff818f0c8d <+13>:	mov    %rdx,%rbx
   0xffffffff818f0c90 <+16>:	mov    %esi,%ebp
   0xffffffff818f0c92 <+18>:	mov    %rdi,%r14
   0xffffffff818f0c95 <+21>:	jmp    0xffffffff818f0ca8 <security_file_ioctl+40>

   jump to skip the inactive BPF LSM hook.

   0xffffffff818f0c97 <+23>:	mov    %r14,%rdi
   0xffffffff818f0c9a <+26>:	mov    %ebp,%esi
   0xffffffff818f0c9c <+28>:	mov    %rbx,%rdx
   0xffffffff818f0c9f <+31>:	call   0xffffffff8141e3b0 <bpf_lsm_file_ioctl>
   0xffffffff818f0ca4 <+36>:	test   %eax,%eax
   0xffffffff818f0ca6 <+38>:	jne    0xffffffff818f0cbf <security_file_ioctl+63>
   0xffffffff818f0ca8 <+40>:	endbr64
   0xffffffff818f0cac <+44>:	jmp    0xffffffff818f0ccd <security_file_ioctl+77>

   jump to skip the empty slot.

   0xffffffff818f0cae <+46>:	mov    %r14,%rdi
   0xffffffff818f0cb1 <+49>:	mov    %ebp,%esi
   0xffffffff818f0cb3 <+51>:	mov    %rbx,%rdx
   0xffffffff818f0cb6 <+54>:	nopl   0x0(%rax,%rax,1)
  				^^^^^^^^^^^^^^^^^^^^^^^
				Empty slot

   0xffffffff818f0cbb <+59>:	test   %eax,%eax
   0xffffffff818f0cbd <+61>:	je     0xffffffff818f0ccd <security_file_ioctl+77>
   0xffffffff818f0cbf <+63>:	endbr64
   0xffffffff818f0cc3 <+67>:	pop    %rbx
   0xffffffff818f0cc4 <+68>:	pop    %r14
   0xffffffff818f0cc6 <+70>:	pop    %rbp
   0xffffffff818f0cc7 <+71>:	cs jmp 0xffffffff82c00000 <__x86_return_thunk>
   0xffffffff818f0ccd <+77>:	endbr64
   0xffffffff818f0cd1 <+81>:	xor    %eax,%eax
   0xffffffff818f0cd3 <+83>:	jmp    0xffffffff818f0cbf <security_file_ioctl+63>
   0xffffffff818f0cd5 <+85>:	mov    %r14,%rdi
   0xffffffff818f0cd8 <+88>:	mov    %ebp,%esi
   0xffffffff818f0cda <+90>:	mov    %rbx,%rdx
   0xffffffff818f0cdd <+93>:	pop    %rbx
   0xffffffff818f0cde <+94>:	pop    %r14
   0xffffffff818f0ce0 <+96>:	pop    %rbp
   0xffffffff818f0ce1 <+97>:	ret

When the config is disabled, the case optimizes the scenario above.

security_file_ioctl:
   0xffffffff818f0e30 <+0>:	endbr64
   0xffffffff818f0e34 <+4>:	nopl   0x0(%rax,%rax,1)
   0xffffffff818f0e39 <+9>:	push   %rbp
   0xffffffff818f0e3a <+10>:	push   %r14
   0xffffffff818f0e3c <+12>:	push   %rbx
   0xffffffff818f0e3d <+13>:	mov    %rdx,%rbx
   0xffffffff818f0e40 <+16>:	mov    %esi,%ebp
   0xffffffff818f0e42 <+18>:	mov    %rdi,%r14
   0xffffffff818f0e45 <+21>:	xchg   %ax,%ax
   0xffffffff818f0e47 <+23>:	xchg   %ax,%ax

   The static keys in their disabled state do not create jumps leading
   to faster code.

   0xffffffff818f0e49 <+25>:	xor    %eax,%eax
   0xffffffff818f0e4b <+27>:	xchg   %ax,%ax
   0xffffffff818f0e4d <+29>:	pop    %rbx
   0xffffffff818f0e4e <+30>:	pop    %r14
   0xffffffff818f0e50 <+32>:	pop    %rbp
   0xffffffff818f0e51 <+33>:	cs jmp 0xffffffff82c00000 <__x86_return_thunk>
   0xffffffff818f0e57 <+39>:	endbr64
   0xffffffff818f0e5b <+43>:	mov    %r14,%rdi
   0xffffffff818f0e5e <+46>:	mov    %ebp,%esi
   0xffffffff818f0e60 <+48>:	mov    %rbx,%rdx
   0xffffffff818f0e63 <+51>:	call   0xffffffff8141e3b0 <bpf_lsm_file_ioctl>
   0xffffffff818f0e68 <+56>:	test   %eax,%eax
   0xffffffff818f0e6a <+58>:	jne    0xffffffff818f0e4d <security_file_ioctl+29>
   0xffffffff818f0e6c <+60>:	jmp    0xffffffff818f0e47 <security_file_ioctl+23>
   0xffffffff818f0e6e <+62>:	endbr64
   0xffffffff818f0e72 <+66>:	mov    %r14,%rdi
   0xffffffff818f0e75 <+69>:	mov    %ebp,%esi
   0xffffffff818f0e77 <+71>:	mov    %rbx,%rdx
   0xffffffff818f0e7a <+74>:	nopl   0x0(%rax,%rax,1)
   0xffffffff818f0e7f <+79>:	test   %eax,%eax
   0xffffffff818f0e81 <+81>:	jne    0xffffffff818f0e4d <security_file_ioctl+29>
   0xffffffff818f0e83 <+83>:	jmp    0xffffffff818f0e49 <security_file_ioctl+25>
   0xffffffff818f0e85 <+85>:	endbr64
   0xffffffff818f0e89 <+89>:	mov    %r14,%rdi
   0xffffffff818f0e8c <+92>:	mov    %ebp,%esi
   0xffffffff818f0e8e <+94>:	mov    %rbx,%rdx
   0xffffffff818f0e91 <+97>:	pop    %rbx
   0xffffffff818f0e92 <+98>:	pop    %r14
   0xffffffff818f0e94 <+100>:	pop    %rbp
   0xffffffff818f0e95 <+101>:	ret

Acked-by: Song Liu <song@kernel.org>
Signed-off-by: KP Singh <kpsingh@kernel.org>
---
 security/Kconfig    | 11 +++++++++++
 security/security.c |  6 ++++--
 2 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/security/Kconfig b/security/Kconfig
index 52c9af08ad35..317018dcbc67 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -32,6 +32,17 @@ config SECURITY
 
 	  If you are unsure how to answer this question, answer N.
 
+config SECURITY_HOOK_LIKELY
+	bool "LSM hooks are likely to be initialized"
+	depends on SECURITY && EXPERT
+	default SECURITY_SELINUX || SECURITY_SMACK || SECURITY_TOMOYO || SECURITY_APPARMOR
+	help
+	  This controls the behaviour of the static keys that guard LSM hooks.
+	  If LSM hooks are likely to be initialized by LSMs, then one gets
+	  better performance by enabling this option. However, if the system is
+	  using an LSM where hooks are much likely to be disabled, one gets
+	  better performance by disabling this config.
+
 config SECURITYFS
 	bool "Enable the securityfs filesystem"
 	help
diff --git a/security/security.c b/security/security.c
index d1ee72e563cc..b8eac2e8a59d 100644
--- a/security/security.c
+++ b/security/security.c
@@ -825,7 +825,8 @@ static int lsm_superblock_alloc(struct super_block *sb)
  */
 #define __CALL_STATIC_VOID(NUM, HOOK, ...)				     \
 do {									     \
-	if (static_branch_unlikely(&SECURITY_HOOK_ACTIVE_KEY(HOOK, NUM))) {    \
+	if (static_branch_maybe(CONFIG_SECURITY_HOOK_LIKELY,		     \
+				&SECURITY_HOOK_ACTIVE_KEY(HOOK, NUM))) {     \
 		static_call(LSM_STATIC_CALL(HOOK, NUM))(__VA_ARGS__);	     \
 	}								     \
 } while (0);
@@ -837,7 +838,8 @@ do {									     \
 
 #define __CALL_STATIC_INT(NUM, R, HOOK, LABEL, ...)			     \
 do {									     \
-	if (static_branch_unlikely(&SECURITY_HOOK_ACTIVE_KEY(HOOK, NUM))) {  \
+	if (static_branch_maybe(CONFIG_SECURITY_HOOK_LIKELY,		     \
+				&SECURITY_HOOK_ACTIVE_KEY(HOOK, NUM))) {     \
 		R = static_call(LSM_STATIC_CALL(HOOK, NUM))(__VA_ARGS__);    \
 		if (R != 0)						     \
 			goto LABEL;					     \
-- 
2.42.0.582.g8ccd20d70d-goog


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH v5 2/5] security: Count the LSMs enabled at compile time
  2023-09-28 20:24 ` [PATCH v5 2/5] security: Count the LSMs enabled at compile time KP Singh
@ 2023-09-29  0:37   ` Kees Cook
  0 siblings, 0 replies; 24+ messages in thread
From: Kees Cook @ 2023-09-29  0:37 UTC (permalink / raw)
  To: KP Singh
  Cc: linux-security-module, bpf, paul, casey, song, daniel, ast,
	renauld, Kui-Feng Lee, Andrii Nakryiko

On Thu, Sep 28, 2023 at 10:24:07PM +0200, KP Singh wrote:
> These macros are a clever trick to determine a count of the number of
> LSMs that are enabled in the config to ascertain the maximum number of
> static calls that need to be configured per LSM hook.
> 
> Without this one would need to generate static calls for the total
> number of LSMs in the kernel (even if they are not compiled) times the
> number of LSM hooks which ends up being quite wasteful.
> 
> Suggested-by: Kui-Feng Lee <sinquersw@gmail.com>
> Suggested-by: Andrii Nakryiko <andrii@kernel.org>
> Acked-by: Song Liu <song@kernel.org>
> Signed-off-by: KP Singh <kpsingh@kernel.org>

Thanks for doing the refactor with the existing macro!

Reviewed-by: Kees Cook <keescook@chromium.org>

-- 
Kees Cook

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v5 5/5] security: Add CONFIG_SECURITY_HOOK_LIKELY
  2023-09-28 20:24 ` [PATCH v5 5/5] security: Add CONFIG_SECURITY_HOOK_LIKELY KP Singh
@ 2023-09-29  0:38   ` Kees Cook
  0 siblings, 0 replies; 24+ messages in thread
From: Kees Cook @ 2023-09-29  0:38 UTC (permalink / raw)
  To: KP Singh
  Cc: linux-security-module, bpf, paul, casey, song, daniel, ast, renauld

On Thu, Sep 28, 2023 at 10:24:10PM +0200, KP Singh wrote:
> This config influences the nature of the static key that guards the
> static call for LSM hooks.
> 
> When enabled, it indicates that an LSM static call slot is more likely
> to be initialized. When disabled, it optimizes for the case when static
> call slot is more likely to be not initialized.
> 
> When a major LSM like (SELinux, AppArmor, Smack etc) is active on a
> system the system would benefit from enabling the config. However there
> are other cases which would benefit from the config being disabled
> (e.g. a system with a BPF LSM with no hooks enabled by default, or an
> LSM like loadpin / yama). Ultimately, there is no one-size fits all
> solution.
> 
> with CONFIG_SECURITY_HOOK_LIKELY enabled, the inactive /
> uninitialized case is penalized with a direct jmp (still better than
> an indirect jmp):
> 
> function security_file_ioctl:
>    0xffffffff818f0c80 <+0>:	endbr64
>    0xffffffff818f0c84 <+4>:	nopl   0x0(%rax,%rax,1)
>    0xffffffff818f0c89 <+9>:	push   %rbp
>    0xffffffff818f0c8a <+10>:	push   %r14
>    0xffffffff818f0c8c <+12>:	push   %rbx
>    0xffffffff818f0c8d <+13>:	mov    %rdx,%rbx
>    0xffffffff818f0c90 <+16>:	mov    %esi,%ebp
>    0xffffffff818f0c92 <+18>:	mov    %rdi,%r14
>    0xffffffff818f0c95 <+21>:	jmp    0xffffffff818f0ca8 <security_file_ioctl+40>
> 
>    jump to skip the inactive BPF LSM hook.
> 
>    0xffffffff818f0c97 <+23>:	mov    %r14,%rdi
>    0xffffffff818f0c9a <+26>:	mov    %ebp,%esi
>    0xffffffff818f0c9c <+28>:	mov    %rbx,%rdx
>    0xffffffff818f0c9f <+31>:	call   0xffffffff8141e3b0 <bpf_lsm_file_ioctl>
>    0xffffffff818f0ca4 <+36>:	test   %eax,%eax
>    0xffffffff818f0ca6 <+38>:	jne    0xffffffff818f0cbf <security_file_ioctl+63>
>    0xffffffff818f0ca8 <+40>:	endbr64
>    0xffffffff818f0cac <+44>:	jmp    0xffffffff818f0ccd <security_file_ioctl+77>
> 
>    jump to skip the empty slot.
> 
>    0xffffffff818f0cae <+46>:	mov    %r14,%rdi
>    0xffffffff818f0cb1 <+49>:	mov    %ebp,%esi
>    0xffffffff818f0cb3 <+51>:	mov    %rbx,%rdx
>    0xffffffff818f0cb6 <+54>:	nopl   0x0(%rax,%rax,1)
>   				^^^^^^^^^^^^^^^^^^^^^^^
> 				Empty slot
> 
>    0xffffffff818f0cbb <+59>:	test   %eax,%eax
>    0xffffffff818f0cbd <+61>:	je     0xffffffff818f0ccd <security_file_ioctl+77>
>    0xffffffff818f0cbf <+63>:	endbr64
>    0xffffffff818f0cc3 <+67>:	pop    %rbx
>    0xffffffff818f0cc4 <+68>:	pop    %r14
>    0xffffffff818f0cc6 <+70>:	pop    %rbp
>    0xffffffff818f0cc7 <+71>:	cs jmp 0xffffffff82c00000 <__x86_return_thunk>
>    0xffffffff818f0ccd <+77>:	endbr64
>    0xffffffff818f0cd1 <+81>:	xor    %eax,%eax
>    0xffffffff818f0cd3 <+83>:	jmp    0xffffffff818f0cbf <security_file_ioctl+63>
>    0xffffffff818f0cd5 <+85>:	mov    %r14,%rdi
>    0xffffffff818f0cd8 <+88>:	mov    %ebp,%esi
>    0xffffffff818f0cda <+90>:	mov    %rbx,%rdx
>    0xffffffff818f0cdd <+93>:	pop    %rbx
>    0xffffffff818f0cde <+94>:	pop    %r14
>    0xffffffff818f0ce0 <+96>:	pop    %rbp
>    0xffffffff818f0ce1 <+97>:	ret
> 
> When the config is disabled, the case optimizes the scenario above.
> 
> security_file_ioctl:
>    0xffffffff818f0e30 <+0>:	endbr64
>    0xffffffff818f0e34 <+4>:	nopl   0x0(%rax,%rax,1)
>    0xffffffff818f0e39 <+9>:	push   %rbp
>    0xffffffff818f0e3a <+10>:	push   %r14
>    0xffffffff818f0e3c <+12>:	push   %rbx
>    0xffffffff818f0e3d <+13>:	mov    %rdx,%rbx
>    0xffffffff818f0e40 <+16>:	mov    %esi,%ebp
>    0xffffffff818f0e42 <+18>:	mov    %rdi,%r14
>    0xffffffff818f0e45 <+21>:	xchg   %ax,%ax
>    0xffffffff818f0e47 <+23>:	xchg   %ax,%ax
> 
>    The static keys in their disabled state do not create jumps leading
>    to faster code.
> 
>    0xffffffff818f0e49 <+25>:	xor    %eax,%eax
>    0xffffffff818f0e4b <+27>:	xchg   %ax,%ax
>    0xffffffff818f0e4d <+29>:	pop    %rbx
>    0xffffffff818f0e4e <+30>:	pop    %r14
>    0xffffffff818f0e50 <+32>:	pop    %rbp
>    0xffffffff818f0e51 <+33>:	cs jmp 0xffffffff82c00000 <__x86_return_thunk>
>    0xffffffff818f0e57 <+39>:	endbr64
>    0xffffffff818f0e5b <+43>:	mov    %r14,%rdi
>    0xffffffff818f0e5e <+46>:	mov    %ebp,%esi
>    0xffffffff818f0e60 <+48>:	mov    %rbx,%rdx
>    0xffffffff818f0e63 <+51>:	call   0xffffffff8141e3b0 <bpf_lsm_file_ioctl>
>    0xffffffff818f0e68 <+56>:	test   %eax,%eax
>    0xffffffff818f0e6a <+58>:	jne    0xffffffff818f0e4d <security_file_ioctl+29>
>    0xffffffff818f0e6c <+60>:	jmp    0xffffffff818f0e47 <security_file_ioctl+23>
>    0xffffffff818f0e6e <+62>:	endbr64
>    0xffffffff818f0e72 <+66>:	mov    %r14,%rdi
>    0xffffffff818f0e75 <+69>:	mov    %ebp,%esi
>    0xffffffff818f0e77 <+71>:	mov    %rbx,%rdx
>    0xffffffff818f0e7a <+74>:	nopl   0x0(%rax,%rax,1)
>    0xffffffff818f0e7f <+79>:	test   %eax,%eax
>    0xffffffff818f0e81 <+81>:	jne    0xffffffff818f0e4d <security_file_ioctl+29>
>    0xffffffff818f0e83 <+83>:	jmp    0xffffffff818f0e49 <security_file_ioctl+25>
>    0xffffffff818f0e85 <+85>:	endbr64
>    0xffffffff818f0e89 <+89>:	mov    %r14,%rdi
>    0xffffffff818f0e8c <+92>:	mov    %ebp,%esi
>    0xffffffff818f0e8e <+94>:	mov    %rbx,%rdx
>    0xffffffff818f0e91 <+97>:	pop    %rbx
>    0xffffffff818f0e92 <+98>:	pop    %r14
>    0xffffffff818f0e94 <+100>:	pop    %rbp
>    0xffffffff818f0e95 <+101>:	ret
> 
> Acked-by: Song Liu <song@kernel.org>
> Signed-off-by: KP Singh <kpsingh@kernel.org>

This looks excellent, and gives us the right balance automatically. :)

Reviewed-by: Kees Cook <keescook@chromium.org>

-- 
Kees Cook

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v5 0/5] Reduce overhead of LSMs with static calls
  2023-09-28 20:24 [PATCH v5 0/5] Reduce overhead of LSMs with static calls KP Singh
                   ` (4 preceding siblings ...)
  2023-09-28 20:24 ` [PATCH v5 5/5] security: Add CONFIG_SECURITY_HOOK_LIKELY KP Singh
@ 2023-09-29  0:41 ` Kees Cook
  2023-10-02 11:06 ` Paolo Abeni
  6 siblings, 0 replies; 24+ messages in thread
From: Kees Cook @ 2023-09-29  0:41 UTC (permalink / raw)
  To: KP Singh, paul
  Cc: linux-security-module, bpf, casey, song, daniel, ast, renauld

On Thu, Sep 28, 2023 at 10:24:05PM +0200, KP Singh wrote:
> # Performance improvement
> 
> With this patch-set some syscalls with lots of LSM hooks in their path
> benefitted at an average of ~3% and I/O and Pipe based system calls benefitting
> the most.

Paul, FWIW, I think this series is ready to land in -next. I'd like it
to get some bake time there just to see if anything unexpected shows up.
It's quite happy in all my local testing, though.

-Kees

-- 
Kees Cook

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v5 3/5] security: Replace indirect LSM hook calls with static calls
  2023-09-28 20:24 ` [PATCH v5 3/5] security: Replace indirect LSM hook calls with static calls KP Singh
@ 2023-09-30 16:13   ` kernel test robot
  2023-09-30 20:40     ` Kees Cook
  0 siblings, 1 reply; 24+ messages in thread
From: kernel test robot @ 2023-09-30 16:13 UTC (permalink / raw)
  To: KP Singh, linux-security-module, bpf
  Cc: oe-kbuild-all, paul, keescook, casey, song, daniel, ast, kpsingh,
	renauld

Hi KP,

kernel test robot noticed the following build errors:

[auto build test ERROR on bpf-next/master]
[also build test ERROR on bpf/master pcmoore-selinux/next linus/master v6.6-rc3 next-20230929]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/KP-Singh/kernel-Add-helper-macros-for-loop-unrolling/20230929-042610
base:   https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git master
patch link:    https://lore.kernel.org/r/20230928202410.3765062-4-kpsingh%40kernel.org
patch subject: [PATCH v5 3/5] security: Replace indirect LSM hook calls with static calls
config: i386-randconfig-001-20230930 (https://download.01.org/0day-ci/archive/20230930/202309302332.1mxVwb0U-lkp@intel.com/config)
compiler: gcc-9 (Debian 9.3.0-22) 9.3.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20230930/202309302332.1mxVwb0U-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202309302332.1mxVwb0U-lkp@intel.com/

All errors (new ones prefixed by >>):

>> security/security.c:139:1: error: Only string constants are supported as initializers for randomized structures with flexible arrays
     139 | };
         | ^


vim +139 security/security.c

   118	
   119	/*
   120	 * Initialise a table of static calls for each LSM hook.
   121	 * DEFINE_STATIC_CALL_NULL invocation above generates a key (STATIC_CALL_KEY)
   122	 * and a trampoline (STATIC_CALL_TRAMP) which are used to call
   123	 * __static_call_update when updating the static call.
   124	 */
   125	struct lsm_static_calls_table static_calls_table __ro_after_init = {
   126	#define INIT_LSM_STATIC_CALL(NUM, NAME)					\
   127		(struct lsm_static_call) {					\
   128			.key = &STATIC_CALL_KEY(LSM_STATIC_CALL(NAME, NUM)),	\
   129			.trampoline = LSM_HOOK_TRAMP(NAME, NUM),		\
   130			.active = &SECURITY_HOOK_ACTIVE_KEY(NAME, NUM),		\
   131		},
   132	#define LSM_HOOK(RET, DEFAULT, NAME, ...)				\
   133		.NAME = {							\
   134			LSM_DEFINE_UNROLL(INIT_LSM_STATIC_CALL, NAME)		\
   135		},
   136	#include <linux/lsm_hook_defs.h>
   137	#undef LSM_HOOK
   138	#undef INIT_LSM_STATIC_CALL
 > 139	};
   140	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v5 3/5] security: Replace indirect LSM hook calls with static calls
  2023-09-30 16:13   ` kernel test robot
@ 2023-09-30 20:40     ` Kees Cook
  2023-10-04  0:09       ` KP Singh
  0 siblings, 1 reply; 24+ messages in thread
From: Kees Cook @ 2023-09-30 20:40 UTC (permalink / raw)
  To: kernel test robot
  Cc: KP Singh, linux-security-module, bpf, oe-kbuild-all, paul, casey,
	song, daniel, ast, renauld

On Sun, Oct 01, 2023 at 12:13:06AM +0800, kernel test robot wrote:
> Hi KP,
> 
> kernel test robot noticed the following build errors:
> 
> [auto build test ERROR on bpf-next/master]
> [also build test ERROR on bpf/master pcmoore-selinux/next linus/master v6.6-rc3 next-20230929]
> [If your patch is applied to the wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://git-scm.com/docs/git-format-patch#_base_tree_information]
> 
> url:    https://github.com/intel-lab-lkp/linux/commits/KP-Singh/kernel-Add-helper-macros-for-loop-unrolling/20230929-042610
> base:   https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git master
> patch link:    https://lore.kernel.org/r/20230928202410.3765062-4-kpsingh%40kernel.org
> patch subject: [PATCH v5 3/5] security: Replace indirect LSM hook calls with static calls
> config: i386-randconfig-001-20230930 (https://download.01.org/0day-ci/archive/20230930/202309302332.1mxVwb0U-lkp@intel.com/config)
> compiler: gcc-9 (Debian 9.3.0-22) 9.3.0
> reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20230930/202309302332.1mxVwb0U-lkp@intel.com/reproduce)
> 
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Closes: https://lore.kernel.org/oe-kbuild-all/202309302332.1mxVwb0U-lkp@intel.com/
> 
> All errors (new ones prefixed by >>):
> 
> >> security/security.c:139:1: error: Only string constants are supported as initializers for randomized structures with flexible arrays
>      139 | };
>          | ^

Uuh, where is there a flexible array here?

> vim +139 security/security.c
> 
>    118	
>    119	/*
>    120	 * Initialise a table of static calls for each LSM hook.
>    121	 * DEFINE_STATIC_CALL_NULL invocation above generates a key (STATIC_CALL_KEY)
>    122	 * and a trampoline (STATIC_CALL_TRAMP) which are used to call
>    123	 * __static_call_update when updating the static call.
>    124	 */
>    125	struct lsm_static_calls_table static_calls_table __ro_after_init = {
>    126	#define INIT_LSM_STATIC_CALL(NUM, NAME)					\
>    127		(struct lsm_static_call) {					\
>    128			.key = &STATIC_CALL_KEY(LSM_STATIC_CALL(NAME, NUM)),	\
>    129			.trampoline = LSM_HOOK_TRAMP(NAME, NUM),		\
>    130			.active = &SECURITY_HOOK_ACTIVE_KEY(NAME, NUM),		\
>    131		},
>    132	#define LSM_HOOK(RET, DEFAULT, NAME, ...)				\
>    133		.NAME = {							\
>    134			LSM_DEFINE_UNROLL(INIT_LSM_STATIC_CALL, NAME)		\
>    135		},
>    136	#include <linux/lsm_hook_defs.h>
>    137	#undef LSM_HOOK
>    138	#undef INIT_LSM_STATIC_CALL
>  > 139	};
>    140	

*confused*

-Kees

-- 
Kees Cook

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v5 0/5] Reduce overhead of LSMs with static calls
  2023-09-28 20:24 [PATCH v5 0/5] Reduce overhead of LSMs with static calls KP Singh
                   ` (5 preceding siblings ...)
  2023-09-29  0:41 ` [PATCH v5 0/5] Reduce overhead of LSMs with static calls Kees Cook
@ 2023-10-02 11:06 ` Paolo Abeni
  2023-10-02 11:09   ` KP Singh
  6 siblings, 1 reply; 24+ messages in thread
From: Paolo Abeni @ 2023-10-02 11:06 UTC (permalink / raw)
  To: KP Singh, linux-security-module, bpf
  Cc: paul, keescook, casey, song, daniel, ast, renauld

On Thu, 2023-09-28 at 22:24 +0200, KP Singh wrote:
> # Background
> 
> LSM hooks (callbacks) are currently invoked as indirect function calls. These
> callbacks are registered into a linked list at boot time as the order of the
> LSMs can be configured on the kernel command line with the "lsm=" command line
> parameter.
> 
> Indirect function calls have a high overhead due to retpoline mitigation for
> various speculative execution attacks.
> 
> Retpolines remain relevant even with newer generation CPUs as recently
> discovered speculative attacks, like Spectre BHB need Retpolines to mitigate
> against branch history injection and still need to be used in combination with
> newer mitigation features like eIBRS.
> 
> This overhead is especially significant for the "bpf" LSM which allows the user
> to implement LSM functionality with eBPF program. In order to facilitate this
> the "bpf" LSM provides a default callback for all LSM hooks. When enabled,
> the "bpf" LSM incurs an unnecessary / avoidable indirect call. This is
> especially bad in OS hot paths (e.g. in the networking stack).
> This overhead prevents the adoption of bpf LSM on performance critical
> systems, and also, in general, slows down all LSMs.
> 
> Since we know the address of the enabled LSM callbacks at compile time and only
> the order is determined at boot time, the LSM framework can allocate static
> calls for each of the possible LSM callbacks and these calls can be updated once
> the order is determined at boot.
> 
> This series is a respin of the RFC proposed by Paul Renauld (renauld@google.com)
> and Brendan Jackman (jackmanb@google.com) [1]
> 
> # Performance improvement
> 
> With this patch-set some syscalls with lots of LSM hooks in their path
> benefitted at an average of ~3% and I/O and Pipe based system calls benefitting
> the most.
> 
> Here are the 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

FTR, I also measure a ~3% tput improvement in UDP stream test over
loopback.

@KP Singh, I would have appreciated being cc-ed here, since I provided
feedback on a previous revision (as soon as I learned of this effort).

Cheers,

Paolo


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v5 0/5] Reduce overhead of LSMs with static calls
  2023-10-02 11:06 ` Paolo Abeni
@ 2023-10-02 11:09   ` KP Singh
  2023-10-02 13:27     ` Paolo Abeni
  0 siblings, 1 reply; 24+ messages in thread
From: KP Singh @ 2023-10-02 11:09 UTC (permalink / raw)
  To: Paolo Abeni
  Cc: linux-security-module, bpf, paul, keescook, casey, song, daniel,
	ast, renauld

On Mon, Oct 2, 2023 at 1:06 PM Paolo Abeni <pabeni@redhat.com> wrote:
>
> On Thu, 2023-09-28 at 22:24 +0200, KP Singh wrote:
> > # Background
> >
> > LSM hooks (callbacks) are currently invoked as indirect function calls. These
> > callbacks are registered into a linked list at boot time as the order of the
> > LSMs can be configured on the kernel command line with the "lsm=" command line
> > parameter.
> >
> > Indirect function calls have a high overhead due to retpoline mitigation for
> > various speculative execution attacks.
> >
> > Retpolines remain relevant even with newer generation CPUs as recently
> > discovered speculative attacks, like Spectre BHB need Retpolines to mitigate
> > against branch history injection and still need to be used in combination with
> > newer mitigation features like eIBRS.
> >
> > This overhead is especially significant for the "bpf" LSM which allows the user
> > to implement LSM functionality with eBPF program. In order to facilitate this
> > the "bpf" LSM provides a default callback for all LSM hooks. When enabled,
> > the "bpf" LSM incurs an unnecessary / avoidable indirect call. This is
> > especially bad in OS hot paths (e.g. in the networking stack).
> > This overhead prevents the adoption of bpf LSM on performance critical
> > systems, and also, in general, slows down all LSMs.
> >
> > Since we know the address of the enabled LSM callbacks at compile time and only
> > the order is determined at boot time, the LSM framework can allocate static
> > calls for each of the possible LSM callbacks and these calls can be updated once
> > the order is determined at boot.
> >
> > This series is a respin of the RFC proposed by Paul Renauld (renauld@google.com)
> > and Brendan Jackman (jackmanb@google.com) [1]
> >
> > # Performance improvement
> >
> > With this patch-set some syscalls with lots of LSM hooks in their path
> > benefitted at an average of ~3% and I/O and Pipe based system calls benefitting
> > the most.
> >
> > Here are the 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
>
> FTR, I also measure a ~3% tput improvement in UDP stream test over
> loopback.
>

Thanks for running the numbers and testing these patches, greatly appreciated!

> @KP Singh, I would have appreciated being cc-ed here, since I provided

Definitely, a miss on my part. Will keep you Cc'ed in any future revisions.

I think we can also add a Tested-by: tag on the main patch and add
your performance numbers to the commit as well.

- KP

> feedback on a previous revision (as soon as I learned of this effort).
>
> Cheers,
>
> Paolo
>

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v5 0/5] Reduce overhead of LSMs with static calls
  2023-10-02 11:09   ` KP Singh
@ 2023-10-02 13:27     ` Paolo Abeni
  0 siblings, 0 replies; 24+ messages in thread
From: Paolo Abeni @ 2023-10-02 13:27 UTC (permalink / raw)
  To: KP Singh
  Cc: linux-security-module, bpf, paul, keescook, casey, song, daniel,
	ast, renauld

On Mon, 2023-10-02 at 13:09 +0200, KP Singh wrote:
> On Mon, Oct 2, 2023 at 1:06 PM Paolo Abeni <pabeni@redhat.com> wrote:
> > On Thu, 2023-09-28 at 22:24 +0200, KP Singh wrote:
> > > # Background
> > > 
> > > LSM hooks (callbacks) are currently invoked as indirect function calls. These
> > > callbacks are registered into a linked list at boot time as the order of the
> > > LSMs can be configured on the kernel command line with the "lsm=" command line
> > > parameter.
> > > 
> > > Indirect function calls have a high overhead due to retpoline mitigation for
> > > various speculative execution attacks.
> > > 
> > > Retpolines remain relevant even with newer generation CPUs as recently
> > > discovered speculative attacks, like Spectre BHB need Retpolines to mitigate
> > > against branch history injection and still need to be used in combination with
> > > newer mitigation features like eIBRS.
> > > 
> > > This overhead is especially significant for the "bpf" LSM which allows the user
> > > to implement LSM functionality with eBPF program. In order to facilitate this
> > > the "bpf" LSM provides a default callback for all LSM hooks. When enabled,
> > > the "bpf" LSM incurs an unnecessary / avoidable indirect call. This is
> > > especially bad in OS hot paths (e.g. in the networking stack).
> > > This overhead prevents the adoption of bpf LSM on performance critical
> > > systems, and also, in general, slows down all LSMs.
> > > 
> > > Since we know the address of the enabled LSM callbacks at compile time and only
> > > the order is determined at boot time, the LSM framework can allocate static
> > > calls for each of the possible LSM callbacks and these calls can be updated once
> > > the order is determined at boot.
> > > 
> > > This series is a respin of the RFC proposed by Paul Renauld (renauld@google.com)
> > > and Brendan Jackman (jackmanb@google.com) [1]
> > > 
> > > # Performance improvement
> > > 
> > > With this patch-set some syscalls with lots of LSM hooks in their path
> > > benefitted at an average of ~3% and I/O and Pipe based system calls benefitting
> > > the most.
> > > 
> > > Here are the 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
> > 
> > FTR, I also measure a ~3% tput improvement in UDP stream test over
> > loopback.
> > 
> 
> Thanks for running the numbers and testing these patches, greatly appreciated!
> 
> > @KP Singh, I would have appreciated being cc-ed here, since I provided
> 
> Definitely, a miss on my part. Will keep you Cc'ed in any future revisions.

Thanks!

> I think we can also add a Tested-by: tag on the main patch and add
> your performance numbers to the commit as well.

Feel free to include that, even if my testing is limited to the
performance test described above.

Cheers,

Paolo


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v5 3/5] security: Replace indirect LSM hook calls with static calls
  2023-09-30 20:40     ` Kees Cook
@ 2023-10-04  0:09       ` KP Singh
  0 siblings, 0 replies; 24+ messages in thread
From: KP Singh @ 2023-10-04  0:09 UTC (permalink / raw)
  To: Kees Cook
  Cc: kernel test robot, linux-security-module, bpf, oe-kbuild-all,
	paul, casey, song, daniel, ast, renauld

On Sat, Sep 30, 2023 at 10:40 PM Kees Cook <keescook@chromium.org> wrote:
>
> On Sun, Oct 01, 2023 at 12:13:06AM +0800, kernel test robot wrote:
> > Hi KP,
> >
> > kernel test robot noticed the following build errors:
> >
> > [auto build test ERROR on bpf-next/master]
> > [also build test ERROR on bpf/master pcmoore-selinux/next linus/master v6.6-rc3 next-20230929]
> > [If your patch is applied to the wrong git tree, kindly drop us a note.
> > And when submitting patch, we suggest to use '--base' as documented in
> > https://git-scm.com/docs/git-format-patch#_base_tree_information]
> >
> > url:    https://github.com/intel-lab-lkp/linux/commits/KP-Singh/kernel-Add-helper-macros-for-loop-unrolling/20230929-042610
> > base:   https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git master
> > patch link:    https://lore.kernel.org/r/20230928202410.3765062-4-kpsingh%40kernel.org
> > patch subject: [PATCH v5 3/5] security: Replace indirect LSM hook calls with static calls
> > config: i386-randconfig-001-20230930 (https://download.01.org/0day-ci/archive/20230930/202309302332.1mxVwb0U-lkp@intel.com/config)
> > compiler: gcc-9 (Debian 9.3.0-22) 9.3.0
> > reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20230930/202309302332.1mxVwb0U-lkp@intel.com/reproduce)
> >
> > If you fix the issue in a separate patch/commit (i.e. not just a new version of
> > the same patch/commit), kindly add following tags
> > | Reported-by: kernel test robot <lkp@intel.com>
> > | Closes: https://lore.kernel.org/oe-kbuild-all/202309302332.1mxVwb0U-lkp@intel.com/
> >
> > All errors (new ones prefixed by >>):
> >
> > >> security/security.c:139:1: error: Only string constants are supported as initializers for randomized structures with flexible arrays
> >      139 | };
> >          | ^
>
> Uuh, where is there a flexible array here?

I cannot seem to reproduce this with my gcc

wget https://download.01.org/0day-ci/archive/20230930/202309302332.1mxVwb0U-lkp@intel.com/config
mkdir build_dir && cp config build_dir/.config
make -j  64 W=1 O=build_dir ARCH=i386 olddefconfig
make -j 64  W=1 O=build_dir ARCH=i386 SHELL=/bin/bash
history

on bpf-next/master builds the kernel for me:

Kernel: arch/x86/boot/bzImage is ready  (#1)

The reproducer does not seem to come with a tool chain, and I even
hacked the make.cross from another similar reply for gcc-9 and it
still seems to work. Not sure what happened in the CI there.




>
> > vim +139 security/security.c
> >
> >    118
> >    119        /*
> >    120         * Initialise a table of static calls for each LSM hook.
> >    121         * DEFINE_STATIC_CALL_NULL invocation above generates a key (STATIC_CALL_KEY)
> >    122         * and a trampoline (STATIC_CALL_TRAMP) which are used to call
> >    123         * __static_call_update when updating the static call.
> >    124         */
> >    125        struct lsm_static_calls_table static_calls_table __ro_after_init = {
> >    126        #define INIT_LSM_STATIC_CALL(NUM, NAME)                                 \
> >    127                (struct lsm_static_call) {                                      \
> >    128                        .key = &STATIC_CALL_KEY(LSM_STATIC_CALL(NAME, NUM)),    \
> >    129                        .trampoline = LSM_HOOK_TRAMP(NAME, NUM),                \
> >    130                        .active = &SECURITY_HOOK_ACTIVE_KEY(NAME, NUM),         \
> >    131                },
> >    132        #define LSM_HOOK(RET, DEFAULT, NAME, ...)                               \
> >    133                .NAME = {                                                       \
> >    134                        LSM_DEFINE_UNROLL(INIT_LSM_STATIC_CALL, NAME)           \
> >    135                },
> >    136        #include <linux/lsm_hook_defs.h>
> >    137        #undef LSM_HOOK
> >    138        #undef INIT_LSM_STATIC_CALL
> >  > 139        };
> >    140
>
> *confused*
>
> -Kees
>
> --
> Kees Cook

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v5 4/5] bpf: Only enable BPF LSM hooks when an LSM program is attached
  2023-09-28 20:24 ` [PATCH v5 4/5] bpf: Only enable BPF LSM hooks when an LSM program is attached KP Singh
@ 2023-10-05  8:09   ` Jiri Olsa
  2023-10-05 13:26     ` KP Singh
  0 siblings, 1 reply; 24+ messages in thread
From: Jiri Olsa @ 2023-10-05  8:09 UTC (permalink / raw)
  To: KP Singh
  Cc: linux-security-module, bpf, paul, keescook, casey, song, daniel,
	ast, renauld

On Thu, Sep 28, 2023 at 10:24:09PM +0200, KP Singh wrote:

SNIP

> diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
> index e97aeda3a86b..df9699bce372 100644
> --- a/kernel/bpf/trampoline.c
> +++ b/kernel/bpf/trampoline.c
> @@ -13,6 +13,7 @@
>  #include <linux/bpf_verifier.h>
>  #include <linux/bpf_lsm.h>
>  #include <linux/delay.h>
> +#include <linux/bpf_lsm.h>
>  
>  /* dummy _ops. The verifier will operate on target program's ops. */
>  const struct bpf_verifier_ops bpf_extension_verifier_ops = {
> @@ -514,7 +515,7 @@ static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_tr
>  {
>  	enum bpf_tramp_prog_type kind;
>  	struct bpf_tramp_link *link_exiting;
> -	int err = 0;
> +	int err = 0, num_lsm_progs = 0;
>  	int cnt = 0, i;
>  
>  	kind = bpf_attach_type_to_tramp(link->link.prog);
> @@ -545,8 +546,14 @@ static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_tr
>  			continue;
>  		/* prog already linked */
>  		return -EBUSY;
> +
> +		if (link_exiting->link.prog->type == BPF_PROG_TYPE_LSM)
> +			num_lsm_progs++;

this looks wrong, it's never reached.. seems like we should add separate
hlist_for_each_entry loop over trampoline's links for this check/init of
num_lsm_progs ?

jirka

>  	}
>  
> +	if (!num_lsm_progs && link->link.prog->type == BPF_PROG_TYPE_LSM)
> +		bpf_lsm_toggle_hook(tr->func.addr, true);
> +
>  	hlist_add_head(&link->tramp_hlist, &tr->progs_hlist[kind]);
>  	tr->progs_cnt[kind]++;
>  	err = bpf_trampoline_update(tr, true /* lock_direct_mutex */);
> @@ -569,8 +576,10 @@ int bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_trampoline
>  
>  static int __bpf_trampoline_unlink_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr)
>  {
> +	struct bpf_tramp_link *link_exiting;
>  	enum bpf_tramp_prog_type kind;
> -	int err;
> +	bool lsm_link_found = false;
> +	int err, num_lsm_progs = 0;
>  
>  	kind = bpf_attach_type_to_tramp(link->link.prog);
>  	if (kind == BPF_TRAMP_REPLACE) {
> @@ -580,8 +589,24 @@ static int __bpf_trampoline_unlink_prog(struct bpf_tramp_link *link, struct bpf_
>  		tr->extension_prog = NULL;
>  		return err;
>  	}
> +
> +	if (link->link.prog->type == BPF_PROG_TYPE_LSM) {
> +		hlist_for_each_entry(link_exiting, &tr->progs_hlist[kind],
> +				     tramp_hlist) {
> +			if (link_exiting->link.prog->type == BPF_PROG_TYPE_LSM)
> +				num_lsm_progs++;
> +
> +			if (link_exiting->link.prog == link->link.prog)
> +				lsm_link_found = true;
> +		}
> +	}
> +
>  	hlist_del_init(&link->tramp_hlist);
>  	tr->progs_cnt[kind]--;
> +
> +	if (lsm_link_found && num_lsm_progs == 1)
> +		bpf_lsm_toggle_hook(tr->func.addr, false);
> +
>  	return bpf_trampoline_update(tr, true /* lock_direct_mutex */);
>  }
>  
> diff --git a/security/bpf/hooks.c b/security/bpf/hooks.c
> index cfaf1d0e6a5f..1957244196d0 100644
> --- a/security/bpf/hooks.c
> +++ b/security/bpf/hooks.c
> @@ -8,7 +8,7 @@
>  
>  static struct security_hook_list bpf_lsm_hooks[] __ro_after_init = {
>  	#define LSM_HOOK(RET, DEFAULT, NAME, ...) \
> -	LSM_HOOK_INIT(NAME, bpf_lsm_##NAME),
> +	LSM_HOOK_INIT_DISABLED(NAME, bpf_lsm_##NAME),
>  	#include <linux/lsm_hook_defs.h>
>  	#undef LSM_HOOK
>  	LSM_HOOK_INIT(inode_free_security, bpf_inode_storage_free),
> @@ -32,3 +32,26 @@ DEFINE_LSM(bpf) = {
>  	.init = bpf_lsm_init,
>  	.blobs = &bpf_lsm_blob_sizes
>  };
> +
> +void bpf_lsm_toggle_hook(void *addr, bool value)
> +{
> +	struct lsm_static_call *scalls;
> +	struct security_hook_list *h;
> +	int i, j;
> +
> +	for (i = 0; i < ARRAY_SIZE(bpf_lsm_hooks); i++) {
> +		h = &bpf_lsm_hooks[i];
> +		scalls = h->scalls;
> +		if (h->hook.lsm_callback == addr)
> +			continue;
> +
> +		for (j = 0; j < MAX_LSM_COUNT; j++) {
> +			if (scalls[j].hl != h)
> +				continue;
> +			if (value)
> +				static_branch_enable(scalls[j].active);
> +			else
> +				static_branch_disable(scalls[j].active);
> +		}
> +	}
> +}
> diff --git a/security/security.c b/security/security.c
> index c2c2cf6b711f..d1ee72e563cc 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -382,7 +382,8 @@ static void __init lsm_static_call_init(struct security_hook_list *hl)
>  			__static_call_update(scall->key, scall->trampoline,
>  					     hl->hook.lsm_callback);
>  			scall->hl = hl;
> -			static_branch_enable(scall->active);
> +			if (hl->default_state)
> +				static_branch_enable(scall->active);
>  			return;
>  		}
>  		scall++;
> -- 
> 2.42.0.582.g8ccd20d70d-goog
> 
> 

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v5 4/5] bpf: Only enable BPF LSM hooks when an LSM program is attached
  2023-10-05  8:09   ` Jiri Olsa
@ 2023-10-05 13:26     ` KP Singh
  2023-10-05 13:27       ` KP Singh
  0 siblings, 1 reply; 24+ messages in thread
From: KP Singh @ 2023-10-05 13:26 UTC (permalink / raw)
  To: Jiri Olsa
  Cc: linux-security-module, bpf, paul, keescook, casey, song, daniel,
	ast, renauld

On Thu, Oct 5, 2023 at 10:09 AM Jiri Olsa <olsajiri@gmail.com> wrote:
>
> On Thu, Sep 28, 2023 at 10:24:09PM +0200, KP Singh wrote:
>
> SNIP
>
> > diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
> > index e97aeda3a86b..df9699bce372 100644
> > --- a/kernel/bpf/trampoline.c
> > +++ b/kernel/bpf/trampoline.c
> > @@ -13,6 +13,7 @@
> >  #include <linux/bpf_verifier.h>
> >  #include <linux/bpf_lsm.h>
> >  #include <linux/delay.h>
> > +#include <linux/bpf_lsm.h>
> >
> >  /* dummy _ops. The verifier will operate on target program's ops. */
> >  const struct bpf_verifier_ops bpf_extension_verifier_ops = {
> > @@ -514,7 +515,7 @@ static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_tr
> >  {
> >       enum bpf_tramp_prog_type kind;
> >       struct bpf_tramp_link *link_exiting;
> > -     int err = 0;
> > +     int err = 0, num_lsm_progs = 0;
> >       int cnt = 0, i;
> >
> >       kind = bpf_attach_type_to_tramp(link->link.prog);
> > @@ -545,8 +546,14 @@ static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_tr
> >                       continue;
> >               /* prog already linked */
> >               return -EBUSY;
> > +
> > +             if (link_exiting->link.prog->type == BPF_PROG_TYPE_LSM)
> > +                     num_lsm_progs++;
>
> this looks wrong, it's never reached.. seems like we should add separate
> hlist_for_each_entry loop over trampoline's links for this check/init of
> num_lsm_progs ?
>
> jirka

Good catch, I missed this during my rebase, after
https://lore.kernel.org/bpf/20220510205923.3206889-2-kuifeng@fb.com/
this condition is basically never reached. I will do a general loop
over to count LSM programs and toggle the hook to true (and same for
unlink).

- KP

[...]

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v5 4/5] bpf: Only enable BPF LSM hooks when an LSM program is attached
  2023-10-05 13:26     ` KP Singh
@ 2023-10-05 13:27       ` KP Singh
  2023-10-05 13:52         ` Jiri Olsa
  0 siblings, 1 reply; 24+ messages in thread
From: KP Singh @ 2023-10-05 13:27 UTC (permalink / raw)
  To: Jiri Olsa
  Cc: linux-security-module, bpf, paul, keescook, casey, song, daniel,
	ast, renauld

On Thu, Oct 5, 2023 at 3:26 PM KP Singh <kpsingh@kernel.org> wrote:
>
> On Thu, Oct 5, 2023 at 10:09 AM Jiri Olsa <olsajiri@gmail.com> wrote:
> >
> > On Thu, Sep 28, 2023 at 10:24:09PM +0200, KP Singh wrote:
> >
> > SNIP
> >
> > > diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
> > > index e97aeda3a86b..df9699bce372 100644
> > > --- a/kernel/bpf/trampoline.c
> > > +++ b/kernel/bpf/trampoline.c
> > > @@ -13,6 +13,7 @@
> > >  #include <linux/bpf_verifier.h>
> > >  #include <linux/bpf_lsm.h>
> > >  #include <linux/delay.h>
> > > +#include <linux/bpf_lsm.h>
> > >
> > >  /* dummy _ops. The verifier will operate on target program's ops. */
> > >  const struct bpf_verifier_ops bpf_extension_verifier_ops = {
> > > @@ -514,7 +515,7 @@ static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_tr
> > >  {
> > >       enum bpf_tramp_prog_type kind;
> > >       struct bpf_tramp_link *link_exiting;

I think this is a typo here. It should be existing, no?

> > > -     int err = 0;
> > > +     int err = 0, num_lsm_progs = 0;
> > >       int cnt = 0, i;
> > >
> > >       kind = bpf_attach_type_to_tramp(link->link.prog);
> > > @@ -545,8 +546,14 @@ static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_tr
> > >                       continue;
> > >               /* prog already linked */
> > >               return -EBUSY;
> > > +
> > > +             if (link_exiting->link.prog->type == BPF_PROG_TYPE_LSM)
> > > +                     num_lsm_progs++;
> >
> > this looks wrong, it's never reached.. seems like we should add separate
> > hlist_for_each_entry loop over trampoline's links for this check/init of
> > num_lsm_progs ?
> >
> > jirka
>
> Good catch, I missed this during my rebase, after
> https://lore.kernel.org/bpf/20220510205923.3206889-2-kuifeng@fb.com/
> this condition is basically never reached. I will do a general loop
> over to count LSM programs and toggle the hook to true (and same for
> unlink).
>
> - KP
>
> [...]

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v5 4/5] bpf: Only enable BPF LSM hooks when an LSM program is attached
  2023-10-05 13:27       ` KP Singh
@ 2023-10-05 13:52         ` Jiri Olsa
  2023-10-05 16:07           ` KP Singh
  0 siblings, 1 reply; 24+ messages in thread
From: Jiri Olsa @ 2023-10-05 13:52 UTC (permalink / raw)
  To: KP Singh
  Cc: Jiri Olsa, linux-security-module, bpf, paul, keescook, casey,
	song, daniel, ast, renauld

On Thu, Oct 05, 2023 at 03:27:35PM +0200, KP Singh wrote:
> On Thu, Oct 5, 2023 at 3:26 PM KP Singh <kpsingh@kernel.org> wrote:
> >
> > On Thu, Oct 5, 2023 at 10:09 AM Jiri Olsa <olsajiri@gmail.com> wrote:
> > >
> > > On Thu, Sep 28, 2023 at 10:24:09PM +0200, KP Singh wrote:
> > >
> > > SNIP
> > >
> > > > diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
> > > > index e97aeda3a86b..df9699bce372 100644
> > > > --- a/kernel/bpf/trampoline.c
> > > > +++ b/kernel/bpf/trampoline.c
> > > > @@ -13,6 +13,7 @@
> > > >  #include <linux/bpf_verifier.h>
> > > >  #include <linux/bpf_lsm.h>
> > > >  #include <linux/delay.h>
> > > > +#include <linux/bpf_lsm.h>
> > > >
> > > >  /* dummy _ops. The verifier will operate on target program's ops. */
> > > >  const struct bpf_verifier_ops bpf_extension_verifier_ops = {
> > > > @@ -514,7 +515,7 @@ static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_tr
> > > >  {
> > > >       enum bpf_tramp_prog_type kind;
> > > >       struct bpf_tramp_link *link_exiting;
> 
> I think this is a typo here. It should be existing, no?

yes, I was wondering about that as well ;-)

jirka

> 
> > > > -     int err = 0;
> > > > +     int err = 0, num_lsm_progs = 0;
> > > >       int cnt = 0, i;
> > > >
> > > >       kind = bpf_attach_type_to_tramp(link->link.prog);
> > > > @@ -545,8 +546,14 @@ static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_tr
> > > >                       continue;
> > > >               /* prog already linked */
> > > >               return -EBUSY;
> > > > +
> > > > +             if (link_exiting->link.prog->type == BPF_PROG_TYPE_LSM)
> > > > +                     num_lsm_progs++;
> > >
> > > this looks wrong, it's never reached.. seems like we should add separate
> > > hlist_for_each_entry loop over trampoline's links for this check/init of
> > > num_lsm_progs ?
> > >
> > > jirka
> >
> > Good catch, I missed this during my rebase, after
> > https://lore.kernel.org/bpf/20220510205923.3206889-2-kuifeng@fb.com/
> > this condition is basically never reached. I will do a general loop
> > over to count LSM programs and toggle the hook to true (and same for
> > unlink).
> >
> > - KP
> >
> > [...]

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v5 4/5] bpf: Only enable BPF LSM hooks when an LSM program is attached
  2023-10-05 13:52         ` Jiri Olsa
@ 2023-10-05 16:07           ` KP Singh
  2023-10-06  7:27             ` Jiri Olsa
  0 siblings, 1 reply; 24+ messages in thread
From: KP Singh @ 2023-10-05 16:07 UTC (permalink / raw)
  To: Jiri Olsa
  Cc: linux-security-module, bpf, paul, keescook, casey, song, daniel,
	ast, renauld

On Thu, Oct 5, 2023 at 3:52 PM Jiri Olsa <olsajiri@gmail.com> wrote:
>
> On Thu, Oct 05, 2023 at 03:27:35PM +0200, KP Singh wrote:
> > On Thu, Oct 5, 2023 at 3:26 PM KP Singh <kpsingh@kernel.org> wrote:
> > >
> > > On Thu, Oct 5, 2023 at 10:09 AM Jiri Olsa <olsajiri@gmail.com> wrote:
> > > >
> > > > On Thu, Sep 28, 2023 at 10:24:09PM +0200, KP Singh wrote:
> > > >
> > > > SNIP
> > > >
> > > > > diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
> > > > > index e97aeda3a86b..df9699bce372 100644
> > > > > --- a/kernel/bpf/trampoline.c
> > > > > +++ b/kernel/bpf/trampoline.c
> > > > > @@ -13,6 +13,7 @@
> > > > >  #include <linux/bpf_verifier.h>
> > > > >  #include <linux/bpf_lsm.h>
> > > > >  #include <linux/delay.h>
> > > > > +#include <linux/bpf_lsm.h>
> > > > >
> > > > >  /* dummy _ops. The verifier will operate on target program's ops. */
> > > > >  const struct bpf_verifier_ops bpf_extension_verifier_ops = {
> > > > > @@ -514,7 +515,7 @@ static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_tr
> > > > >  {
> > > > >       enum bpf_tramp_prog_type kind;
> > > > >       struct bpf_tramp_link *link_exiting;
> >
> > I think this is a typo here. It should be existing, no?
>
> yes, I was wondering about that as well ;-)
>
> jirka
>
> >
> > > > > -     int err = 0;
> > > > > +     int err = 0, num_lsm_progs = 0;
> > > > >       int cnt = 0, i;
> > > > >
> > > > >       kind = bpf_attach_type_to_tramp(link->link.prog);
> > > > > @@ -545,8 +546,14 @@ static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_tr
> > > > >                       continue;
> > > > >               /* prog already linked */
> > > > >               return -EBUSY;
> > > > > +
> > > > > +             if (link_exiting->link.prog->type == BPF_PROG_TYPE_LSM)
> > > > > +                     num_lsm_progs++;
> > > >
> > > > this looks wrong, it's never reached.. seems like we should add separate
> > > > hlist_for_each_entry loop over trampoline's links for this check/init of
> > > > num_lsm_progs ?
> > > >
> > > > jirka
> > >
> > > Good catch, I missed this during my rebase, after
> > > https://lore.kernel.org/bpf/20220510205923.3206889-2-kuifeng@fb.com/
> > > this condition is basically never reached. I will do a general loop
> > > over to count LSM programs and toggle the hook to true (and same for
> > > unlink).

So, there is something that is unclear about this code, i.e. what
happens when there is an error from bpf_trampoline_update fails and
the link and unlink seem to have different expectations:

* link seems to go back to the linked list and removes the trampoline
and restores the refcount:

[...]
 err = bpf_trampoline_update(tr, true /* lock_direct_mutex */);
 if (err) {
    hlist_del_init(&link->tramp_hlist);
    tr->progs_cnt[kind]--;
  }
 return err;
}


* unlink does restore the side effect (i.e. it does not put the
removed trampoline back and increments the refcount).

hlist_del_init(&link->tramp_hlist);
tr->progs_cnt[kind]--;
return bpf_trampoline_update(tr, true /* lock_direct_mutex */);

However, I think I will make it simpler and enforce the invariant that
if an LSM program is attached, the hook is enabled and vice versa. How
about:

diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
index df9699bce372..4f31384b5637 100644
--- a/kernel/bpf/trampoline.c
+++ b/kernel/bpf/trampoline.c
@@ -511,11 +511,30 @@ static enum bpf_tramp_prog_type
bpf_attach_type_to_tramp(struct bpf_prog *prog)
        }
 }

+static void bpf_trampoline_toggle_lsm(struct bpf_trampoline *tr,
+                                     enum bpf_tramp_prog_type kind)
+{
+       struct bpf_tramp_link *link;
+       volatile bool found = false;
+
+       /* Loop through the links and if any LSM program is attached, ensure
+        * that the hook is enabled.
+        */
+       hlist_for_each_entry(link, &tr->progs_hlist[kind], tramp_hlist) {
+               if (link->link.prog->type == BPF_PROG_TYPE_LSM) {
+                       found  = true;
+                       break;
+               }
+       }
+
+       bpf_lsm_toggle_hook(tr->func.addr, found);
+}
+
 static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link,
struct bpf_trampoline *tr)
 {
        enum bpf_tramp_prog_type kind;
        struct bpf_tramp_link *link_exiting;
-       int err = 0, num_lsm_progs = 0;
+       int err = 0;
        int cnt = 0, i;

        kind = bpf_attach_type_to_tramp(link->link.prog);
@@ -547,15 +566,14 @@ static int __bpf_trampoline_link_prog(struct
bpf_tramp_link *link, struct bpf_tr
                /* prog already linked */
                return -EBUSY;

-               if (link_exiting->link.prog->type == BPF_PROG_TYPE_LSM)
-                       num_lsm_progs++;
        }

-       if (!num_lsm_progs && link->link.prog->type == BPF_PROG_TYPE_LSM)
-               bpf_lsm_toggle_hook(tr->func.addr, true);
-
        hlist_add_head(&link->tramp_hlist, &tr->progs_hlist[kind]);
        tr->progs_cnt[kind]++;
+
+       if (link->link.prog->type == BPF_PROG_TYPE_LSM)
+               bpf_trampoline_toggle_lsm(tr, kind);
+
        err = bpf_trampoline_update(tr, true /* lock_direct_mutex */);
        if (err) {
                hlist_del_init(&link->tramp_hlist);
@@ -578,7 +596,6 @@ static int __bpf_trampoline_unlink_prog(struct
bpf_tramp_link *link, struct bpf_
 {
        struct bpf_tramp_link *link_exiting;
        enum bpf_tramp_prog_type kind;
-       bool lsm_link_found = false;
        int err, num_lsm_progs = 0;

        kind = bpf_attach_type_to_tramp(link->link.prog);
@@ -595,18 +612,14 @@ static int __bpf_trampoline_unlink_prog(struct
bpf_tramp_link *link, struct bpf_
                                     tramp_hlist) {
                        if (link_exiting->link.prog->type == BPF_PROG_TYPE_LSM)
                                num_lsm_progs++;
-
-                       if (link_exiting->link.prog == link->link.prog)
-                               lsm_link_found = true;
                }
        }

        hlist_del_init(&link->tramp_hlist);
        tr->progs_cnt[kind]--;

-       if (lsm_link_found && num_lsm_progs == 1)
-               bpf_lsm_toggle_hook(tr->func.addr, false);
-
+       if (link->link.prog->type == BPF_PROG_TYPE_LSM)
+               bpf_trampoline_toggle_lsm(tr, kind);
        return bpf_trampoline_update(tr, true /* lock_direct_mutex */);
 }


- KP

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH v5 4/5] bpf: Only enable BPF LSM hooks when an LSM program is attached
  2023-10-05 16:07           ` KP Singh
@ 2023-10-06  7:27             ` Jiri Olsa
  2023-10-06  9:05               ` Jiri Olsa
  0 siblings, 1 reply; 24+ messages in thread
From: Jiri Olsa @ 2023-10-06  7:27 UTC (permalink / raw)
  To: KP Singh
  Cc: Jiri Olsa, linux-security-module, bpf, paul, keescook, casey,
	song, daniel, ast, renauld

On Thu, Oct 05, 2023 at 06:07:28PM +0200, KP Singh wrote:

SNIP

> diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
> index df9699bce372..4f31384b5637 100644
> --- a/kernel/bpf/trampoline.c
> +++ b/kernel/bpf/trampoline.c
> @@ -511,11 +511,30 @@ static enum bpf_tramp_prog_type
> bpf_attach_type_to_tramp(struct bpf_prog *prog)
>         }
>  }
> 
> +static void bpf_trampoline_toggle_lsm(struct bpf_trampoline *tr,
> +                                     enum bpf_tramp_prog_type kind)
> +{
> +       struct bpf_tramp_link *link;
> +       volatile bool found = false;
> +
> +       /* Loop through the links and if any LSM program is attached, ensure
> +        * that the hook is enabled.
> +        */
> +       hlist_for_each_entry(link, &tr->progs_hlist[kind], tramp_hlist) {
> +               if (link->link.prog->type == BPF_PROG_TYPE_LSM) {
> +                       found  = true;
> +                       break;
> +               }
> +       }
> +
> +       bpf_lsm_toggle_hook(tr->func.addr, found);
> +}
> +
>  static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link,
> struct bpf_trampoline *tr)
>  {
>         enum bpf_tramp_prog_type kind;
>         struct bpf_tramp_link *link_exiting;
> -       int err = 0, num_lsm_progs = 0;
> +       int err = 0;
>         int cnt = 0, i;
> 
>         kind = bpf_attach_type_to_tramp(link->link.prog);
> @@ -547,15 +566,14 @@ static int __bpf_trampoline_link_prog(struct
> bpf_tramp_link *link, struct bpf_tr
>                 /* prog already linked */
>                 return -EBUSY;
> 
> -               if (link_exiting->link.prog->type == BPF_PROG_TYPE_LSM)
> -                       num_lsm_progs++;
>         }
> 
> -       if (!num_lsm_progs && link->link.prog->type == BPF_PROG_TYPE_LSM)
> -               bpf_lsm_toggle_hook(tr->func.addr, true);
> -
>         hlist_add_head(&link->tramp_hlist, &tr->progs_hlist[kind]);
>         tr->progs_cnt[kind]++;
> +
> +       if (link->link.prog->type == BPF_PROG_TYPE_LSM)
> +               bpf_trampoline_toggle_lsm(tr, kind);

how about keeping BPF_PROG_TYPE_LSM progs type count of attached programs
in bpf_trampoline and toggle lsm on first coming in and last going out?

also the trampoline attach is actually made in bpf_trampoline_update,
so I wonder it'd make more sense to put it in there, but it's already
complicated, so it actually might be easier in here

jirka

> +
>         err = bpf_trampoline_update(tr, true /* lock_direct_mutex */);
>         if (err) {
>                 hlist_del_init(&link->tramp_hlist);
> @@ -578,7 +596,6 @@ static int __bpf_trampoline_unlink_prog(struct
> bpf_tramp_link *link, struct bpf_
>  {
>         struct bpf_tramp_link *link_exiting;
>         enum bpf_tramp_prog_type kind;
> -       bool lsm_link_found = false;
>         int err, num_lsm_progs = 0;
> 
>         kind = bpf_attach_type_to_tramp(link->link.prog);
> @@ -595,18 +612,14 @@ static int __bpf_trampoline_unlink_prog(struct
> bpf_tramp_link *link, struct bpf_
>                                      tramp_hlist) {
>                         if (link_exiting->link.prog->type == BPF_PROG_TYPE_LSM)
>                                 num_lsm_progs++;
> -
> -                       if (link_exiting->link.prog == link->link.prog)
> -                               lsm_link_found = true;
>                 }
>         }
> 
>         hlist_del_init(&link->tramp_hlist);
>         tr->progs_cnt[kind]--;
> 
> -       if (lsm_link_found && num_lsm_progs == 1)
> -               bpf_lsm_toggle_hook(tr->func.addr, false);
> -
> +       if (link->link.prog->type == BPF_PROG_TYPE_LSM)
> +               bpf_trampoline_toggle_lsm(tr, kind);
>         return bpf_trampoline_update(tr, true /* lock_direct_mutex */);
>  }
> 
> 
> - KP

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v5 4/5] bpf: Only enable BPF LSM hooks when an LSM program is attached
  2023-10-06  7:27             ` Jiri Olsa
@ 2023-10-06  9:05               ` Jiri Olsa
  2023-10-06 10:57                 ` KP Singh
  0 siblings, 1 reply; 24+ messages in thread
From: Jiri Olsa @ 2023-10-06  9:05 UTC (permalink / raw)
  To: KP Singh
  Cc: Jiri Olsa, linux-security-module, bpf, paul, keescook, casey,
	song, daniel, ast, renauld

On Fri, Oct 06, 2023 at 09:27:57AM +0200, Jiri Olsa wrote:

SNIP

> >  static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link,
> > struct bpf_trampoline *tr)
> >  {
> >         enum bpf_tramp_prog_type kind;
> >         struct bpf_tramp_link *link_exiting;
> > -       int err = 0, num_lsm_progs = 0;
> > +       int err = 0;
> >         int cnt = 0, i;
> > 
> >         kind = bpf_attach_type_to_tramp(link->link.prog);
> > @@ -547,15 +566,14 @@ static int __bpf_trampoline_link_prog(struct
> > bpf_tramp_link *link, struct bpf_tr
> >                 /* prog already linked */
> >                 return -EBUSY;
> > 
> > -               if (link_exiting->link.prog->type == BPF_PROG_TYPE_LSM)
> > -                       num_lsm_progs++;
> >         }
> > 
> > -       if (!num_lsm_progs && link->link.prog->type == BPF_PROG_TYPE_LSM)
> > -               bpf_lsm_toggle_hook(tr->func.addr, true);
> > -
> >         hlist_add_head(&link->tramp_hlist, &tr->progs_hlist[kind]);
> >         tr->progs_cnt[kind]++;
> > +
> > +       if (link->link.prog->type == BPF_PROG_TYPE_LSM)
> > +               bpf_trampoline_toggle_lsm(tr, kind);
> 
> how about keeping BPF_PROG_TYPE_LSM progs type count of attached programs
> in bpf_trampoline and toggle lsm on first coming in and last going out?

hm we actually allow other tracing program types to attach to bpf_lsm_*
functions, so I wonder we should toggle the lsm hook for each program
type (for bpf_lsm_* trampolines) because they'd expect the hook is called

but I'm not sure it's a valid use case to have like normal fentry program
attached to bpf_lsm_XXX function

jirka

> 
> also the trampoline attach is actually made in bpf_trampoline_update,
> so I wonder it'd make more sense to put it in there, but it's already
> complicated, so it actually might be easier in here
> 
> jirka
> 
> > +
> >         err = bpf_trampoline_update(tr, true /* lock_direct_mutex */);
> >         if (err) {
> >                 hlist_del_init(&link->tramp_hlist);
> > @@ -578,7 +596,6 @@ static int __bpf_trampoline_unlink_prog(struct
> > bpf_tramp_link *link, struct bpf_
> >  {
> >         struct bpf_tramp_link *link_exiting;
> >         enum bpf_tramp_prog_type kind;
> > -       bool lsm_link_found = false;
> >         int err, num_lsm_progs = 0;
> > 
> >         kind = bpf_attach_type_to_tramp(link->link.prog);
> > @@ -595,18 +612,14 @@ static int __bpf_trampoline_unlink_prog(struct
> > bpf_tramp_link *link, struct bpf_
> >                                      tramp_hlist) {
> >                         if (link_exiting->link.prog->type == BPF_PROG_TYPE_LSM)
> >                                 num_lsm_progs++;
> > -
> > -                       if (link_exiting->link.prog == link->link.prog)
> > -                               lsm_link_found = true;
> >                 }
> >         }
> > 
> >         hlist_del_init(&link->tramp_hlist);
> >         tr->progs_cnt[kind]--;
> > 
> > -       if (lsm_link_found && num_lsm_progs == 1)
> > -               bpf_lsm_toggle_hook(tr->func.addr, false);
> > -
> > +       if (link->link.prog->type == BPF_PROG_TYPE_LSM)
> > +               bpf_trampoline_toggle_lsm(tr, kind);
> >         return bpf_trampoline_update(tr, true /* lock_direct_mutex */);
> >  }
> > 
> > 
> > - KP

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v5 4/5] bpf: Only enable BPF LSM hooks when an LSM program is attached
  2023-10-06  9:05               ` Jiri Olsa
@ 2023-10-06 10:57                 ` KP Singh
  2023-10-06 18:32                   ` KP Singh
  0 siblings, 1 reply; 24+ messages in thread
From: KP Singh @ 2023-10-06 10:57 UTC (permalink / raw)
  To: Jiri Olsa
  Cc: linux-security-module, bpf, paul, keescook, casey, song, daniel,
	ast, renauld

On Fri, Oct 6, 2023 at 11:05 AM Jiri Olsa <olsajiri@gmail.com> wrote:
>
> On Fri, Oct 06, 2023 at 09:27:57AM +0200, Jiri Olsa wrote:
>
> SNIP
>
> > >  static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link,
> > > struct bpf_trampoline *tr)
> > >  {
> > >         enum bpf_tramp_prog_type kind;
> > >         struct bpf_tramp_link *link_exiting;
> > > -       int err = 0, num_lsm_progs = 0;
> > > +       int err = 0;
> > >         int cnt = 0, i;
> > >
> > >         kind = bpf_attach_type_to_tramp(link->link.prog);
> > > @@ -547,15 +566,14 @@ static int __bpf_trampoline_link_prog(struct
> > > bpf_tramp_link *link, struct bpf_tr
> > >                 /* prog already linked */
> > >                 return -EBUSY;
> > >
> > > -               if (link_exiting->link.prog->type == BPF_PROG_TYPE_LSM)
> > > -                       num_lsm_progs++;
> > >         }
> > >
> > > -       if (!num_lsm_progs && link->link.prog->type == BPF_PROG_TYPE_LSM)
> > > -               bpf_lsm_toggle_hook(tr->func.addr, true);
> > > -
> > >         hlist_add_head(&link->tramp_hlist, &tr->progs_hlist[kind]);
> > >         tr->progs_cnt[kind]++;
> > > +
> > > +       if (link->link.prog->type == BPF_PROG_TYPE_LSM)
> > > +               bpf_trampoline_toggle_lsm(tr, kind);
> >
> > how about keeping BPF_PROG_TYPE_LSM progs type count of attached programs
> > in bpf_trampoline and toggle lsm on first coming in and last going out?
>
> hm we actually allow other tracing program types to attach to bpf_lsm_*
> functions, so I wonder we should toggle the lsm hook for each program
> type (for bpf_lsm_* trampolines) because they'd expect the hook is called

Tracing is about tracing, attaching a tracing program to bpf_lsm_ that
changes the actual trace here is not something I would recommend.
Infact, I have used tracing programs to figure out whether bpf_lsm_*
is being called to debug stuff. Tracing users can always attach to
security_* if they like.

- KP

>
> but I'm not sure it's a valid use case to have like normal fentry program
> attached to bpf_lsm_XXX function
>
> jirka
>
> >
> > also the trampoline attach is actually made in bpf_trampoline_update,
> > so I wonder it'd make more sense to put it in there, but it's already
> > complicated, so it actually might be easier in here
> >
> > jirka
> >
> > > +
> > >         err = bpf_trampoline_update(tr, true /* lock_direct_mutex */);
> > >         if (err) {
> > >                 hlist_del_init(&link->tramp_hlist);
> > > @@ -578,7 +596,6 @@ static int __bpf_trampoline_unlink_prog(struct
> > > bpf_tramp_link *link, struct bpf_
> > >  {
> > >         struct bpf_tramp_link *link_exiting;
> > >         enum bpf_tramp_prog_type kind;
> > > -       bool lsm_link_found = false;
> > >         int err, num_lsm_progs = 0;
> > >
> > >         kind = bpf_attach_type_to_tramp(link->link.prog);
> > > @@ -595,18 +612,14 @@ static int __bpf_trampoline_unlink_prog(struct
> > > bpf_tramp_link *link, struct bpf_
> > >                                      tramp_hlist) {
> > >                         if (link_exiting->link.prog->type == BPF_PROG_TYPE_LSM)
> > >                                 num_lsm_progs++;
> > > -
> > > -                       if (link_exiting->link.prog == link->link.prog)
> > > -                               lsm_link_found = true;
> > >                 }
> > >         }
> > >
> > >         hlist_del_init(&link->tramp_hlist);
> > >         tr->progs_cnt[kind]--;
> > >
> > > -       if (lsm_link_found && num_lsm_progs == 1)
> > > -               bpf_lsm_toggle_hook(tr->func.addr, false);
> > > -
> > > +       if (link->link.prog->type == BPF_PROG_TYPE_LSM)
> > > +               bpf_trampoline_toggle_lsm(tr, kind);
> > >         return bpf_trampoline_update(tr, true /* lock_direct_mutex */);
> > >  }
> > >
> > >
> > > - KP
>

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v5 4/5] bpf: Only enable BPF LSM hooks when an LSM program is attached
  2023-10-06 10:57                 ` KP Singh
@ 2023-10-06 18:32                   ` KP Singh
  0 siblings, 0 replies; 24+ messages in thread
From: KP Singh @ 2023-10-06 18:32 UTC (permalink / raw)
  To: Jiri Olsa
  Cc: linux-security-module, bpf, paul, keescook, casey, song, daniel,
	ast, renauld

On Fri, Oct 6, 2023 at 12:57 PM KP Singh <kpsingh@kernel.org> wrote:
>
> On Fri, Oct 6, 2023 at 11:05 AM Jiri Olsa <olsajiri@gmail.com> wrote:
> >
> > On Fri, Oct 06, 2023 at 09:27:57AM +0200, Jiri Olsa wrote:
> >
> > SNIP
> >
> > > >  static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link,
> > > > struct bpf_trampoline *tr)
> > > >  {
> > > >         enum bpf_tramp_prog_type kind;
> > > >         struct bpf_tramp_link *link_exiting;
> > > > -       int err = 0, num_lsm_progs = 0;
> > > > +       int err = 0;
> > > >         int cnt = 0, i;
> > > >
> > > >         kind = bpf_attach_type_to_tramp(link->link.prog);
> > > > @@ -547,15 +566,14 @@ static int __bpf_trampoline_link_prog(struct
> > > > bpf_tramp_link *link, struct bpf_tr
> > > >                 /* prog already linked */
> > > >                 return -EBUSY;
> > > >
> > > > -               if (link_exiting->link.prog->type == BPF_PROG_TYPE_LSM)
> > > > -                       num_lsm_progs++;
> > > >         }
> > > >
> > > > -       if (!num_lsm_progs && link->link.prog->type == BPF_PROG_TYPE_LSM)
> > > > -               bpf_lsm_toggle_hook(tr->func.addr, true);
> > > > -
> > > >         hlist_add_head(&link->tramp_hlist, &tr->progs_hlist[kind]);
> > > >         tr->progs_cnt[kind]++;
> > > > +
> > > > +       if (link->link.prog->type == BPF_PROG_TYPE_LSM)
> > > > +               bpf_trampoline_toggle_lsm(tr, kind);
> > >
> > > how about keeping BPF_PROG_TYPE_LSM progs type count of attached programs
> > > in bpf_trampoline and toggle lsm on first coming in and last going out?
> >
> > hm we actually allow other tracing program types to attach to bpf_lsm_*
> > functions, so I wonder we should toggle the lsm hook for each program
> > type (for bpf_lsm_* trampolines) because they'd expect the hook is called
>
> Tracing is about tracing, attaching a tracing program to bpf_lsm_ that
> changes the actual trace here is not something I would recommend.
> Infact, I have used tracing programs to figure out whether bpf_lsm_*
> is being called to debug stuff. Tracing users can always attach to
> security_* if they like.
>

I will rev up with this fix and send it out as I will be unavailable
for the next 2 weeks.

- KP

> - KP
>
> >
> > but I'm not sure it's a valid use case to have like normal fentry program
> > attached to bpf_lsm_XXX function
> >
> > jirka
> >
> > >
> > > also the trampoline attach is actually made in bpf_trampoline_update,
> > > so I wonder it'd make more sense to put it in there, but it's already
> > > complicated, so it actually might be easier in here
> > >
> > > jirka
> > >
> > > > +
> > > >         err = bpf_trampoline_update(tr, true /* lock_direct_mutex */);
> > > >         if (err) {
> > > >                 hlist_del_init(&link->tramp_hlist);
> > > > @@ -578,7 +596,6 @@ static int __bpf_trampoline_unlink_prog(struct
> > > > bpf_tramp_link *link, struct bpf_
> > > >  {
> > > >         struct bpf_tramp_link *link_exiting;
> > > >         enum bpf_tramp_prog_type kind;
> > > > -       bool lsm_link_found = false;
> > > >         int err, num_lsm_progs = 0;
> > > >
> > > >         kind = bpf_attach_type_to_tramp(link->link.prog);
> > > > @@ -595,18 +612,14 @@ static int __bpf_trampoline_unlink_prog(struct
> > > > bpf_tramp_link *link, struct bpf_
> > > >                                      tramp_hlist) {
> > > >                         if (link_exiting->link.prog->type == BPF_PROG_TYPE_LSM)
> > > >                                 num_lsm_progs++;
> > > > -
> > > > -                       if (link_exiting->link.prog == link->link.prog)
> > > > -                               lsm_link_found = true;
> > > >                 }
> > > >         }
> > > >
> > > >         hlist_del_init(&link->tramp_hlist);
> > > >         tr->progs_cnt[kind]--;
> > > >
> > > > -       if (lsm_link_found && num_lsm_progs == 1)
> > > > -               bpf_lsm_toggle_hook(tr->func.addr, false);
> > > > -
> > > > +       if (link->link.prog->type == BPF_PROG_TYPE_LSM)
> > > > +               bpf_trampoline_toggle_lsm(tr, kind);
> > > >         return bpf_trampoline_update(tr, true /* lock_direct_mutex */);
> > > >  }
> > > >
> > > >
> > > > - KP
> >

^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2023-10-06 18:32 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-09-28 20:24 [PATCH v5 0/5] Reduce overhead of LSMs with static calls KP Singh
2023-09-28 20:24 ` [PATCH v5 1/5] kernel: Add helper macros for loop unrolling KP Singh
2023-09-28 20:24 ` [PATCH v5 2/5] security: Count the LSMs enabled at compile time KP Singh
2023-09-29  0:37   ` Kees Cook
2023-09-28 20:24 ` [PATCH v5 3/5] security: Replace indirect LSM hook calls with static calls KP Singh
2023-09-30 16:13   ` kernel test robot
2023-09-30 20:40     ` Kees Cook
2023-10-04  0:09       ` KP Singh
2023-09-28 20:24 ` [PATCH v5 4/5] bpf: Only enable BPF LSM hooks when an LSM program is attached KP Singh
2023-10-05  8:09   ` Jiri Olsa
2023-10-05 13:26     ` KP Singh
2023-10-05 13:27       ` KP Singh
2023-10-05 13:52         ` Jiri Olsa
2023-10-05 16:07           ` KP Singh
2023-10-06  7:27             ` Jiri Olsa
2023-10-06  9:05               ` Jiri Olsa
2023-10-06 10:57                 ` KP Singh
2023-10-06 18:32                   ` KP Singh
2023-09-28 20:24 ` [PATCH v5 5/5] security: Add CONFIG_SECURITY_HOOK_LIKELY KP Singh
2023-09-29  0:38   ` Kees Cook
2023-09-29  0:41 ` [PATCH v5 0/5] Reduce overhead of LSMs with static calls Kees Cook
2023-10-02 11:06 ` Paolo Abeni
2023-10-02 11:09   ` KP Singh
2023-10-02 13:27     ` Paolo Abeni

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.