Linux Security Modules development
 help / color / mirror / Atom feed
* [PATCH] Add LoadPin support for eBPF program loading
@ 2026-06-11 18:59 Alex Roberts via B4 Relay
  2026-06-12  0:08 ` David Windsor
  2026-06-12  6:17 ` kernel test robot
  0 siblings, 2 replies; 3+ messages in thread
From: Alex Roberts via B4 Relay @ 2026-06-11 18:59 UTC (permalink / raw)
  To: Kees Cook, Paul Moore, James Morris, Serge E. Hallyn
  Cc: linux-kernel, linux-security-module, bpf, Alexei Starovoitov,
	KP Singh, Alex Roberts

From: Alex Roberts <alex.roberts109@outlook.com>

Add LoadPin LSM hook to bpf_prog_load to prevent loading of BPF
programs from untrusted filesystems

---
There have been several efforts to provide a trust mechanism for eBPF programs –
particularly in the form of a signed program. After many discussions, BPF
Signing became supported [1].

This patch series intends to provide an alternative trust mechanism for eBPF
programs using LoadPin and the LoadPin dm-verity support. The approach is to pin
eBPF userspace loaders to a single filesystem or to one or more dm-verity
integrity protected filesystems. This is similar to the existing  Loadpin+verity
implementation and approach of e.g., loading unsigned kernel modules or kexec
images from a pinned trusted filesystem [2].

When a userspace application attempts to load a BPF program, LoadPin
first checks whether the exe_file is located on the pinned root, if so
bpf_prog_load is allowed. Otherwise, Loadpin denies bpf_prog_load. Additionally,
if verity support is enabled, LoadPin determines whether the exe_file is located
on a verity backed device and whether the root digest of that device is in the
list of trusted digests. bpf_prog_load is allowed if the verity device has a
trusted root digest.

Background:
In a secure boot environment, secure boot can be extended to the root filesystem
by means of dm-verity. Placing userspace eBPF programs on trusted filesystems
and restricting their use to a trusted filesystem implicitly extends the trust
to the underlying BPF program itself, without having to sign the bytecode.
Rather than sign each program and load keys into the kernel keyring, it can be
sufficient to sign the filesystem.

Additional Considerations:
Because the userspace loader itself defines the filesystem to pin from, it
should be acknowledged that this does not necessarily solve the dynamically
generated eBPF usecase. For example, if the pinned filesystem includes bpftrace
or other userspace applications that dynamically generate and load eBPF
bytecode, this trust mechanism does not apply. But for systems that either
   1) remove/do-not-provide dynamically generating programs or
   2) loadpin filesystem(s) that exclude such programs,
then it can be reasoned that only trusted bytecode is loaded.

[1] https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/commit/?h=for-next&id=58a5820582e4c809dd26b3f2d396cf072411d6e8
[2] https://lore.kernel.org/lkml/20220517163437.v4.2.I01c67af41d2f6525c6d023101671d7339a9bc8b5@changeid/

Signed-off-by: Alex Roberts <alex.roberts109@outlook.com>
---
 include/linux/kernel_read_file.h |  1 +
 security/loadpin/Kconfig         | 12 ++++++++++++
 security/loadpin/loadpin.c       | 29 +++++++++++++++++++++++++++++
 3 files changed, 42 insertions(+)

diff --git a/include/linux/kernel_read_file.h b/include/linux/kernel_read_file.h
index d613a7b4dd35..75cbd514562f 100644
--- a/include/linux/kernel_read_file.h
+++ b/include/linux/kernel_read_file.h
@@ -15,6 +15,7 @@
 	id(POLICY, security-policy)		\
 	id(X509_CERTIFICATE, x509-certificate)	\
 	id(MODULE_COMPRESSED, kernel-module-compressed) \
+	id(EBPF, ebpf) \
 	id(MAX_ID, )
 
 #define __fid_enumify(ENUM, dummy) READING_ ## ENUM,
diff --git a/security/loadpin/Kconfig b/security/loadpin/Kconfig
index aef63d3e30df..be0754735d5d 100644
--- a/security/loadpin/Kconfig
+++ b/security/loadpin/Kconfig
@@ -42,3 +42,15 @@ config SECURITY_LOADPIN_VERITY
 
 	  This is followed by the verity digests, with one digest per
 	  line.
+
+config SECURITY_LOADPIN_EBPF
+	bool "Pin ebpf programs to one filesystem"
+	depends on SECURITY_LOADPIN
+	help
+	  eBPF program loading is pinned to the first filesystem from which the
+	  application loading the progam resides. When enabled, applications on
+	  other filesystems that attempt to load an eBPF program will be rejected.
+	  This is best used when applications that load eBPF programs reside on a
+	  read-only filesystem from which tools for dynamically generating eBPF
+	  programs such as bpftrace are not installed. This will not restrict the
+	  kernel from loading a bpf program.
\ No newline at end of file
diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c
index f71861f98e1a..47fd7e247edb 100644
--- a/security/loadpin/loadpin.c
+++ b/security/loadpin/loadpin.c
@@ -23,6 +23,10 @@
 #include <uapi/linux/loadpin.h>
 #include <uapi/linux/lsm.h>
 
+#ifdef CONFIG_SECURITY_LOADPIN_EBPF
+#include <linux/bpf.h>
+#endif /* CONFIG_SECURITY_LOADPIN_BPF */
+
 #define VERITY_DIGEST_FILE_HEADER "# LOADPIN_TRUSTED_VERITY_ROOT_DIGESTS"
 
 static void report_load(const char *origin, struct file *file, char *operation)
@@ -204,6 +208,28 @@ static int loadpin_load_data(enum kernel_load_data_id id, bool contents)
 	return loadpin_check(NULL, (enum kernel_read_file_id) id);
 }
 
+#ifdef CONFIG_SECURITY_LOADPIN_EBPF
+static int loadpin_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
+				  struct bpf_token *token, bool is_kernel)
+{
+	int res = 0;
+	struct file *exe_file = NULL;
+	struct mm_struct *mm = current->mm;
+
+	if (is_kernel || !mm)
+		return 0;
+
+	exe_file = get_mm_exe_file(mm);
+	if (!exe_file)
+		return 0;
+
+	res = loadpin_check(exe_file, READING_EBPF);
+	fput(exe_file);
+
+	return res;
+}
+#endif /* CONFIG_SECURITY_LOADPIN_EBPF */
+
 static const struct lsm_id loadpin_lsmid = {
 	.name = "loadpin",
 	.id = LSM_ID_LOADPIN,
@@ -213,6 +239,9 @@ static struct security_hook_list loadpin_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(sb_free_security, loadpin_sb_free_security),
 	LSM_HOOK_INIT(kernel_read_file, loadpin_read_file),
 	LSM_HOOK_INIT(kernel_load_data, loadpin_load_data),
+#ifdef CONFIG_SECURITY_LOADPIN_EBPF
+	LSM_HOOK_INIT(bpf_prog_load, loadpin_bpf_prog_load),
+#endif /* CONFIG_SECURITY_LOADPIN_EBPF */
 };
 
 static void __init parse_exclude(void)

---
base-commit: 122b52f0bab007ebeb414c8280c1def17b9ed1f4
change-id: 20260611-b4-rfc-loadpin-ebpf-086c41deb503

Best regards,
--  
Alex Roberts <alex.roberts109@outlook.com>



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

* Re: [PATCH] Add LoadPin support for eBPF program loading
  2026-06-11 18:59 [PATCH] Add LoadPin support for eBPF program loading Alex Roberts via B4 Relay
@ 2026-06-12  0:08 ` David Windsor
  2026-06-12  6:17 ` kernel test robot
  1 sibling, 0 replies; 3+ messages in thread
From: David Windsor @ 2026-06-12  0:08 UTC (permalink / raw)
  To: alex.roberts109, Kees Cook, Paul Moore, James Morris,
	Serge E . Hallyn
  Cc: linux-kernel, linux-security-module, bpf, Alexei Starovoitov,
	KP Singh, David Windsor

On Thu, Jun 11, 2026 at 01:59:10PM -0500, Alex Roberts wrote:
> +static int loadpin_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
> +				  struct bpf_token *token, bool is_kernel)
> +{
> +	int res = 0;
> +	struct file *exe_file = NULL;
> +	struct mm_struct *mm = current->mm;
> +
> +	if (is_kernel || !mm)
> +		return 0;
> +
> +	exe_file = get_mm_exe_file(mm);
> +	if (!exe_file)
> +		return 0;
> +
> +	res = loadpin_check(exe_file, READING_EBPF);

Why are we checking current here? IIUC this will be whoever calls
bpf(2), which would be the loader, which would then be able to load bpf
programs from an untrusted source.

In the kmod case loadpin_check() sees the .ko itself.

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

* Re: [PATCH] Add LoadPin support for eBPF program loading
  2026-06-11 18:59 [PATCH] Add LoadPin support for eBPF program loading Alex Roberts via B4 Relay
  2026-06-12  0:08 ` David Windsor
@ 2026-06-12  6:17 ` kernel test robot
  1 sibling, 0 replies; 3+ messages in thread
From: kernel test robot @ 2026-06-12  6:17 UTC (permalink / raw)
  To: Alex Roberts via B4 Relay, Kees Cook, Paul Moore, James Morris,
	Serge E. Hallyn
  Cc: oe-kbuild-all, linux-kernel, linux-security-module, bpf,
	Alexei Starovoitov, KP Singh, Alex Roberts

Hi Alex,

kernel test robot noticed the following build errors:

[auto build test ERROR on 122b52f0bab007ebeb414c8280c1def17b9ed1f4]

url:    https://github.com/intel-lab-lkp/linux/commits/Alex-Roberts-via-B4-Relay/Add-LoadPin-support-for-eBPF-program-loading/20260612-031559
base:   122b52f0bab007ebeb414c8280c1def17b9ed1f4
patch link:    https://lore.kernel.org/r/20260611-b4-rfc-loadpin-ebpf-v1-1-11a6c8e6170d%40outlook.com
patch subject: [PATCH] Add LoadPin support for eBPF program loading
config: nios2-randconfig-002-20260612 (https://download.01.org/0day-ci/archive/20260612/202606121426.NrJtdO3K-lkp@intel.com/config)
compiler: nios2-linux-gcc (GCC) 11.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260612/202606121426.NrJtdO3K-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/202606121426.NrJtdO3K-lkp@intel.com/

All errors (new ones prefixed by >>):

   In file included from <command-line>:
   security/selinux/hooks.c: In function 'selinux_kernel_read_file':
>> include/linux/compiler_types.h:699:45: error: call to '__compiletime_assert_916' declared with attribute error: New kernel_read_file_id introduced; update SELinux!
     699 |         _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
         |                                             ^
   include/linux/compiler_types.h:680:25: note: in definition of macro '__compiletime_assert'
     680 |                         prefix ## suffix();                             \
         |                         ^~~~~~
   include/linux/compiler_types.h:699:9: note: in expansion of macro '_compiletime_assert'
     699 |         _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
         |         ^~~~~~~~~~~~~~~~~~~
   include/linux/build_bug.h:40:37: note: in expansion of macro 'compiletime_assert'
      40 | #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
         |                                     ^~~~~~~~~~~~~~~~~~
   security/selinux/hooks.c:4412:9: note: in expansion of macro 'BUILD_BUG_ON_MSG'
    4412 |         BUILD_BUG_ON_MSG(READING_MAX_ID > 8,
         |         ^~~~~~~~~~~~~~~~
   security/selinux/hooks.c: In function 'selinux_kernel_load_data':
>> include/linux/compiler_types.h:699:45: error: call to '__compiletime_assert_917' declared with attribute error: New kernel_load_data_id introduced; update SELinux!
     699 |         _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
         |                                             ^
   include/linux/compiler_types.h:680:25: note: in definition of macro '__compiletime_assert'
     680 |                         prefix ## suffix();                             \
         |                         ^~~~~~
   include/linux/compiler_types.h:699:9: note: in expansion of macro '_compiletime_assert'
     699 |         _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
         |         ^~~~~~~~~~~~~~~~~~~
   include/linux/build_bug.h:40:37: note: in expansion of macro 'compiletime_assert'
      40 | #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
         |                                     ^~~~~~~~~~~~~~~~~~
   security/selinux/hooks.c:4449:9: note: in expansion of macro 'BUILD_BUG_ON_MSG'
    4449 |         BUILD_BUG_ON_MSG(LOADING_MAX_ID > 8,
         |         ^~~~~~~~~~~~~~~~


vim +/__compiletime_assert_916 +699 include/linux/compiler_types.h

eb5c2d4b45e3d2 Will Deacon 2020-07-21  685  
eb5c2d4b45e3d2 Will Deacon 2020-07-21  686  #define _compiletime_assert(condition, msg, prefix, suffix) \
eb5c2d4b45e3d2 Will Deacon 2020-07-21  687  	__compiletime_assert(condition, msg, prefix, suffix)
eb5c2d4b45e3d2 Will Deacon 2020-07-21  688  
eb5c2d4b45e3d2 Will Deacon 2020-07-21  689  /**
eb5c2d4b45e3d2 Will Deacon 2020-07-21  690   * compiletime_assert - break build and emit msg if condition is false
eb5c2d4b45e3d2 Will Deacon 2020-07-21  691   * @condition: a compile-time constant condition to check
eb5c2d4b45e3d2 Will Deacon 2020-07-21  692   * @msg:       a message to emit if condition is false
eb5c2d4b45e3d2 Will Deacon 2020-07-21  693   *
eb5c2d4b45e3d2 Will Deacon 2020-07-21  694   * In tradition of POSIX assert, this macro will break the build if the
eb5c2d4b45e3d2 Will Deacon 2020-07-21  695   * supplied condition is *false*, emitting the supplied error message if the
eb5c2d4b45e3d2 Will Deacon 2020-07-21  696   * compiler has support to do so.
eb5c2d4b45e3d2 Will Deacon 2020-07-21  697   */
eb5c2d4b45e3d2 Will Deacon 2020-07-21  698  #define compiletime_assert(condition, msg) \
eb5c2d4b45e3d2 Will Deacon 2020-07-21 @699  	_compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
eb5c2d4b45e3d2 Will Deacon 2020-07-21  700  

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

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

end of thread, other threads:[~2026-06-12  6:17 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-11 18:59 [PATCH] Add LoadPin support for eBPF program loading Alex Roberts via B4 Relay
2026-06-12  0:08 ` David Windsor
2026-06-12  6:17 ` kernel test robot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox