Linux Documentation
 help / color / mirror / Atom feed
* [RFC PATCH net-next 0/3] netns: optionally inherit IPv4 TCP sysctls from old net
From: nmreadelf @ 2026-04-30  1:30 UTC (permalink / raw)
  To: davem, edumazet, kuba, pabeni, corbet, dsahern
  Cc: horms, chia-yu.chang, idosch, ij, brauner, jack, kuniyu, jlayton,
	netdev, linux-doc, linux-kernel, kong414, lance.yang, leon.hwang

a new network namespace starts with built-in TCP defaults.
In container-heavy setups, operators often tune TCP sysctls in init_net and then
need to re-apply the same values for each new netns.

This series adds an opt-in mechanism to initialize per-netns IPv4 TCP sysctl
settings from init_net at netns creation time.

Behavior:

Default is unchanged.
When net.ipv4.netns_inherit_tcp_sysctls=1, new netns inherit
TCP sysctl from old_net.

nmreadelf (3):
  ipv4: netns: group copyable TCP sysctls in netns_ipv4
  net: ipv4: add netns_inherit_tcp_sysctls sysctl
  tcp: netns: optionally inherit IPv4 TCP sysctls from parent netns

 .../net_cachelines/netns_ipv4_sysctl.rst      | 25 +++----
 include/net/netns/ipv4.h                      | 33 +++++----
 net/core/net_namespace.c                      | 72 +++++++++++++++++++
 net/ipv4/sysctl_net_ipv4.c                    |  9 +++
 4 files changed, 114 insertions(+), 25 deletions(-)

-- 
2.47.3


^ permalink raw reply

* [PATCH] crypto: af_alg - Document the deprecation of AF_ALG
From: Eric Biggers @ 2026-04-30  1:15 UTC (permalink / raw)
  To: linux-crypto, Herbert Xu
  Cc: linux-doc, linux-api, linux-kernel, netdev, Linus Torvalds,
	Eric Biggers

AF_ALG is almost completely unnecessary, and it exposes a massive attack
surface that hasn't been standing up to modern vulnerability discovery
tools.  The latest one even has its own website, providing a small
Python script that reliably roots most Linux distros: https://copy.fail/

This isn't sustainable, especially as LLMs have accelerated the rate the
vulnerabilities are coming in.  The effort that is being put into this
thing is vastly disproportional to the few programs that actually use
it, and those programs would be better served by userspace code anyway.

These issues have been noted in many mailing list discussions already.
But until now they haven't been reflected in the documentation or
kconfig menu itself, and the vulnerabilities are still coming in.

Let's go ahead and document the deprecation.

This isn't intended to change anything overnight.  After all, most Linux
distros won't be able to disable the kconfig options quite yet, mainly
because of iwd.  But this should create a bit more impetus for these
userspace programs to be fixed, and the documentation update should also
help prevent more users from appearing.

Signed-off-by: Eric Biggers <ebiggers@kernel.org>
---

This patch is targeting crypto/master

 Documentation/crypto/userspace-if.rst | 82 ++++++++++++++++++++-------
 crypto/Kconfig                        | 69 ++++++++++++++++------
 2 files changed, 113 insertions(+), 38 deletions(-)

diff --git a/Documentation/crypto/userspace-if.rst b/Documentation/crypto/userspace-if.rst
index 021759198fe7..c39f5c79a5b7 100644
--- a/Documentation/crypto/userspace-if.rst
+++ b/Documentation/crypto/userspace-if.rst
@@ -2,30 +2,72 @@ User Space Interface
 ====================
 
 Introduction
 ------------
 
-The concepts of the kernel crypto API visible to kernel space is fully
-applicable to the user space interface as well. Therefore, the kernel
-crypto API high level discussion for the in-kernel use cases applies
-here as well.
-
-The major difference, however, is that user space can only act as a
-consumer and never as a provider of a transformation or cipher
-algorithm.
-
-The following covers the user space interface exported by the kernel
-crypto API. A working example of this description is libkcapi that can
-be obtained from [1]. That library can be used by user space
-applications that require cryptographic services from the kernel.
-
-Some details of the in-kernel kernel crypto API aspects do not apply to
-user space, however. This includes the difference between synchronous
-and asynchronous invocations. The user space API call is fully
-synchronous.
-
-[1] https://www.chronox.de/libkcapi/index.html
+AF_ALG provides unprivileged userspace programs access to arbitrary hash,
+symmetric cipher, AEAD, and RNG algorithms that are implemented in kernel-mode
+code.
+
+AF_ALG is insecure and is deprecated. Originally added to the kernel in 2010,
+most kernel developers now consider it to be a mistake.
+
+AF_ALG continues to be supported only for backwards compatibility. On systems
+where no programs using AF_ALG remain, the support for it should be disabled by
+disabling ``CONFIG_CRYPTO_USER_API_*``.
+
+Deprecation
+-----------
+
+AF_ALG was originally intended to provide userspace programs access to crypto
+accelerators that they wouldn't otherwise have access to.
+
+However, that capability turned out to not be useful on very many systems. More
+significantly, the actual implementation exposes a vastly greater amount of
+functionality than that. It actually provides access to all software algorithms.
+
+This includes arbitrary compositions of different algorithms created via a
+complex template system, as well as algorithms that only make sense as internal
+implementation details of other algorithms. It also includes full zero-copy
+support, which is difficult for the kernel to implement securely.
+
+Ultimately, these algorithms are just math computations. They use the same
+instructions that userspace programs already have access to, just accessed in a
+much more convoluted and less efficient way.
+
+Indeed, userspace code is nearly always what is being used anyway. These same
+algorithms are widely implemented in userspace crypto libraries.
+
+Meanwhile, AF_ALG hasn't been withstanding modern vulnerability discovery tools
+such as syzbot and large language models. It receives a steady stream of CVEs.
+Some of the examples include:
+
+- CVE-2026-31677
+- CVE-2026-31431 (https://copy.fail)
+- CVE-2025-38079
+- CVE-2025-37808
+- CVE-2024-26824
+- CVE-2022-48781
+- CVE-2019-8912
+- CVE-2018-14619
+- CVE-2017-18075
+- CVE-2017-17806
+- CVE-2017-17805
+- CVE-2016-10147
+- CVE-2015-8970
+- CVE-2015-3331
+- CVE-2014-9644
+- CVE-2013-7421
+- CVE-2011-4081
+
+It is recommended that, whenever possible, userspace programs be migrated to
+userspace crypto code (which again, is what is normally used anyway) and
+``CONFIG_CRYPTO_USER_API_*`` be disabled.  On systems that use SELinux, SELinux
+can also be used to restrict the use of AF_ALG to trusted programs.
+
+The remainder of this documentation provides the historical documentation for
+the deprecated AF_ALG interface.
 
 User Space API General Remarks
 ------------------------------
 
 The kernel crypto API is accessible from user space. Currently, the
diff --git a/crypto/Kconfig b/crypto/Kconfig
index 103d1f58cb7c..6cd1c478d4be 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -1278,48 +1278,72 @@ config CRYPTO_DF80090A
 	tristate
 	select CRYPTO_AES
 	select CRYPTO_CTR
 
 endmenu
-menu "Userspace interface"
+menu "Userspace interface (deprecated)"
 
 config CRYPTO_USER_API
 	tristate
 
 config CRYPTO_USER_API_HASH
-	tristate "Hash algorithms"
+	tristate "Hash algorithms (deprecated)"
 	depends on NET
 	select CRYPTO_HASH
 	select CRYPTO_USER_API
 	help
-	  Enable the userspace interface for hash algorithms.
+	  Enable the AF_ALG userspace interface for hash algorithms.  This
+	  provides unprivileged userspace programs access to arbitrary hash
+	  algorithms implemented in the kernel's privileged execution context.
 
-	  See Documentation/crypto/userspace-if.rst and
-	  https://www.chronox.de/libkcapi/html/index.html
+	  This interface is deprecated and is supported only for backwards
+	  compatibility.  It regularly has vulnerabilities, and the capabilities
+	  it provides are redundant with userspace crypto libraries.
+
+	  Enable this only if needed for support for a program that hasn't yet
+	  been converted to userspace crypto, for example iwd.
+
+	  See also Documentation/crypto/userspace-if.rst
 
 config CRYPTO_USER_API_SKCIPHER
-	tristate "Symmetric key cipher algorithms"
+	tristate "Symmetric key cipher algorithms (deprecated)"
 	depends on NET
 	select CRYPTO_SKCIPHER
 	select CRYPTO_USER_API
 	help
-	  Enable the userspace interface for symmetric key cipher algorithms.
+	  Enable the AF_ALG userspace interface for symmetric key algorithms.
+	  This provides unprivileged userspace programs access to arbitrary
+	  symmetric key algorithms implemented in the kernel's privileged
+	  execution context.
+
+	  This interface is deprecated and is supported only for backwards
+	  compatibility.  It regularly has vulnerabilities, and the capabilities
+	  it provides are redundant with userspace crypto libraries.
+
+	  Enable this only if needed for support for a program that hasn't yet
+	  been converted to userspace crypto, for example iwd, or cryptsetup
+	  with certain algorithms.
 
-	  See Documentation/crypto/userspace-if.rst and
-	  https://www.chronox.de/libkcapi/html/index.html
+	  See also Documentation/crypto/userspace-if.rst
 
 config CRYPTO_USER_API_RNG
-	tristate "RNG (random number generator) algorithms"
+	tristate "Random number generation algorithms (deprecated)"
 	depends on NET
 	select CRYPTO_RNG
 	select CRYPTO_USER_API
 	help
-	  Enable the userspace interface for RNG (random number generator)
-	  algorithms.
+	  Enable the AF_ALG userspace interface for random number generation
+	  (RNG) algorithms.  This provides unprivileged userspace programs
+	  access to arbitrary RNG algorithms implemented in the kernel's
+	  privileged execution context.
 
-	  See Documentation/crypto/userspace-if.rst and
-	  https://www.chronox.de/libkcapi/html/index.html
+	  This interface is deprecated and is supported only for backwards
+	  compatibility.  It regularly has vulnerabilities, and the capabilities
+	  it provides are redundant with userspace crypto libraries as well as
+	  the normal kernel RNG (e.g., /dev/urandom and getrandom(2)).
+
+	  See also Documentation/crypto/userspace-if.rst
 
 config CRYPTO_USER_API_RNG_CAVP
 	bool "Enable CAVP testing of DRBG"
 	depends on CRYPTO_USER_API_RNG && CRYPTO_DRBG
 	help
@@ -1330,20 +1354,29 @@ config CRYPTO_USER_API_RNG_CAVP
 
 	  This should only be enabled for CAVP testing. You should say
 	  no unless you know what this is.
 
 config CRYPTO_USER_API_AEAD
-	tristate "AEAD cipher algorithms"
+	tristate "AEAD cipher algorithms (deprecated)"
 	depends on NET
 	select CRYPTO_AEAD
 	select CRYPTO_SKCIPHER
 	select CRYPTO_USER_API
 	help
-	  Enable the userspace interface for AEAD cipher algorithms.
+	  Enable the AF_ALG userspace interface for authenticated encryption
+	  with associated data (AEAD) algorithms.  This provides unprivileged
+	  userspace programs access to arbitrary AEAD algorithms implemented in
+	  the kernel's privileged execution context.
+
+	  This interface is deprecated and is supported only for backwards
+	  compatibility.  It regularly has vulnerabilities, and the capabilities
+	  it provides are redundant with userspace crypto libraries.
+
+	  Enable this only if needed for support for a program that hasn't yet
+	  been converted to userspace crypto, for example iwd.
 
-	  See Documentation/crypto/userspace-if.rst and
-	  https://www.chronox.de/libkcapi/html/index.html
+	  See also Documentation/crypto/userspace-if.rst
 
 config CRYPTO_USER_API_ENABLE_OBSOLETE
 	bool "Obsolete cryptographic algorithms"
 	depends on CRYPTO_USER_API
 	default y

base-commit: 57b8e2d666a31fa201432d58f5fe3469a0dd83ba
-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH v4 3/3] Documentation: deprecated.rst: kmalloc-family: mark argument as optional
From: SeongJae Park @ 2026-04-30  1:03 UTC (permalink / raw)
  To: Manuel Ebner
  Cc: SeongJae Park, Jonathan Corbet, Shuah Khan, linux-doc, Kees Cook,
	linux-kernel, workflows, linux-mm, Geert Uytterhoeven
In-Reply-To: <20260429072704.311603-2-manuelebner@mailbox.org>

On Wed, 29 Apr 2026 09:27:04 +0200 Manuel Ebner <manuelebner@mailbox.org> wrote:

> put the optional argument (gfp) in square brackets
> add default value = GFP_KERNEL
> 
> eg. ptr = kmalloc_obj(*ptr, gfp);
>  -> ptr = kmalloc_obj(*ptr [, gfp] );
> 
> Signed-off-by: Manuel Ebner <manuelebner@mailbox.org>

I have a trivial question below, but because it is trivial,

Acked-by: SeongJae Park <sj@kernel.org>

> ---
>  Documentation/process/deprecated.rst | 15 ++++++++-------
>  1 file changed, 8 insertions(+), 7 deletions(-)
> 
> diff --git a/Documentation/process/deprecated.rst b/Documentation/process/deprecated.rst
> index fed56864d036..ac75b7ecac47 100644
> --- a/Documentation/process/deprecated.rst
> +++ b/Documentation/process/deprecated.rst
> @@ -392,13 +392,14 @@ allocations. For example, these open coded assignments::
>  
>  become, respectively::
>  
> -	ptr = kmalloc_obj(*ptr, gfp);
> -	ptr = kzalloc_obj(*ptr, gfp);
> -	ptr = kmalloc_objs(*ptr, count, gfp);
> -	ptr = kzalloc_objs(*ptr, count, gfp);
> -	ptr = kmalloc_flex(*ptr, flex_member, count, gfp);
> -	__auto_type ptr = kmalloc_obj(struct foo, gfp);
> -
> +	ptr = kmalloc_obj(*ptr [, gfp] );
> +	ptr = kzalloc_obj(*ptr [, gfp] );
> +	ptr = kmalloc_objs(*ptr, count [, gfp] );
> +	ptr = kzalloc_objs(*ptr, count [, gfp] );
> +	ptr = kmalloc_flex(*ptr, flex_member, count [, gfp] );
> +	__auto_type ptr = kmalloc_obj(struct foo [, gfp] );
> +
> +The argument gfp is optional, the default value is GFP_KERNEL.
>  If `ptr->flex_member` is annotated with __counted_by(), the allocation
>  will automatically fail if `count` is larger than the maximum
>  representable value that can be stored in the counter member associated

Like 'ptr->flex_member' and 'count', why don't you enclose 'gfp' and
'GFP_KERNEL' with backticks ('`')?


Thanks,
SJ

[...]

^ permalink raw reply

* Re: [PATCH v4 1/3] Documentation: adopt new coding style of type-aware kmalloc-family
From: SeongJae Park @ 2026-04-30  1:00 UTC (permalink / raw)
  To: SeongJae Park
  Cc: Manuel Ebner, Jonathan Corbet, Shuah Khan, linux-doc, Kees Cook,
	linux-kernel, workflows, linux-sound, linux-media, linux-mm
In-Reply-To: <20260430005333.113698-1-sj@kernel.org>

On Wed, 29 Apr 2026 17:53:36 -0700 SeongJae Park <sj@kernel.org> wrote:

> On Wed, 29 Apr 2026 09:14:44 +0200 Manuel Ebner <manuelebner@mailbox.org> wrote:
> 
> > Update the documentation to reflect new type-aware kmalloc-family as
> > suggested in commit 2932ba8d9c99 ("slab: Introduce kmalloc_obj()
> > and family")
> > 
> > ptr = kmalloc(sizeof(*ptr), gfp);
> >  -> ptr = kmalloc_obj(*ptr);
> > ptr = kmalloc(sizeof(struct some_obj_name), gfp);
> >  -> ptr = kmalloc_obj(*ptr);
> > ptr = kzalloc(sizeof(*ptr), gfp);
> >  -> ptr = kzalloc_obj(*ptr);
> > ptr = kmalloc_array(count, sizeof(*ptr), gfp);
> >  -> ptr = kmalloc_objs(*ptr, count);
> > ptr = kcalloc(count, sizeof(*ptr), gfp);
> >  -> ptr = kzalloc_objs(*ptr, count);

Forgot asking this, sorry.  Shouldn't 'gfp' parameters be kept?

> > 
> > Signed-off-by: Manuel Ebner <manuelebner@mailbox.org>
> 
> Acked-by: SeongJae Park <sj@kernel.org>

My Acked-by: is still valid regardless of your answer to my trivial question.


Thanks,
SJ

[...]

^ permalink raw reply

* Re: [PATCH v4 2/3] Documentation: RCU: adopt new coding style of type-aware kmalloc-family
From: SeongJae Park @ 2026-04-30  0:56 UTC (permalink / raw)
  To: Manuel Ebner
  Cc: SeongJae Park, Jonathan Corbet, Shuah Khan, linux-doc, rcu,
	Kees Cook, linux-mm, Paul E . McKenney
In-Reply-To: <20260429072320.310817-2-manuelebner@mailbox.org>

On Wed, 29 Apr 2026 09:23:21 +0200 Manuel Ebner <manuelebner@mailbox.org> wrote:

> Update Documentation/RCU/* to reflect new type-aware kmalloc-family
> as suggested in commit 2932ba8d9c99 ("slab: Introduce kmalloc_obj()
> and family")
> 
> ptr = kmalloc(sizeof(*ptr), gfp);
>  -> ptr = kmalloc_obj(*ptr);

Shouldn't 'gfp' parameter be kept?

> 
> Signed-off-by: Manuel Ebner <manuelebner@mailbox.org>
> Acked-by: Paul E. McKenney <paulmck@kernel.org>

Other than the above,

Acked-by: SeongJae Park <sj@kernel.org>


Thanks,
SJ

[...]

^ permalink raw reply

* Re: [PATCH v4 1/3] Documentation: adopt new coding style of type-aware kmalloc-family
From: SeongJae Park @ 2026-04-30  0:53 UTC (permalink / raw)
  To: Manuel Ebner
  Cc: SeongJae Park, Jonathan Corbet, Shuah Khan, linux-doc, Kees Cook,
	linux-kernel, workflows, linux-sound, linux-media, linux-mm
In-Reply-To: <20260429071445.309733-2-manuelebner@mailbox.org>

On Wed, 29 Apr 2026 09:14:44 +0200 Manuel Ebner <manuelebner@mailbox.org> wrote:

> Update the documentation to reflect new type-aware kmalloc-family as
> suggested in commit 2932ba8d9c99 ("slab: Introduce kmalloc_obj()
> and family")
> 
> ptr = kmalloc(sizeof(*ptr), gfp);
>  -> ptr = kmalloc_obj(*ptr);
> ptr = kmalloc(sizeof(struct some_obj_name), gfp);
>  -> ptr = kmalloc_obj(*ptr);
> ptr = kzalloc(sizeof(*ptr), gfp);
>  -> ptr = kzalloc_obj(*ptr);
> ptr = kmalloc_array(count, sizeof(*ptr), gfp);
>  -> ptr = kmalloc_objs(*ptr, count);
> ptr = kcalloc(count, sizeof(*ptr), gfp);
>  -> ptr = kzalloc_objs(*ptr, count);
> 
> Signed-off-by: Manuel Ebner <manuelebner@mailbox.org>

Acked-by: SeongJae Park <sj@kernel.org>


Thanks,
SJ

[...]

^ permalink raw reply

* Re: [PATCH v2] docs: kernel-parameters: document scope of irqaffinity= parameter
From: kernel test robot @ 2026-04-30  0:28 UTC (permalink / raw)
  To: Aaron Tomlin, corbet, skhan
  Cc: oe-kbuild-all, tglx, akpm, bp, rdunlap, dave.hansen, feng.tang,
	pawan.kumar.gupta, dapeng1.mi, kees, elver, paulmck, lirongqing,
	bhelgaas, bigeasy, linux-doc, linux-kernel
In-Reply-To: <20260421150911.42404-1-atomlin@atomlin.com>

Hi Aaron,

kernel test robot noticed the following build warnings:

[auto build test WARNING on lwn/docs-next]
[also build test WARNING on linus/master v7.1-rc1 next-20260429]
[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/Aaron-Tomlin/docs-kernel-parameters-document-scope-of-irqaffinity-parameter/20260423-221944
base:   git://git.lwn.net/linux.git docs-next
patch link:    https://lore.kernel.org/r/20260421150911.42404-1-atomlin%40atomlin.com
patch subject: [PATCH v2] docs: kernel-parameters: document scope of irqaffinity= parameter
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
docutils: docutils (Docutils 0.21.2, Python 3.13.5, on linux)
reproduce: (https://download.01.org/0day-ci/archive/20260430/202604300215.1Alrqyxi-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/202604300215.1Alrqyxi-lkp@intel.com/

All warnings (new ones prefixed by >>):

   Warning: tools/docs/documentation-file-ref-check references a file that doesn't exist: Documentation/devicetree/dt-object-internal.txt
   Warning: tools/docs/documentation-file-ref-check references a file that doesn't exist: m,^Documentation/scheduler/sched-pelt
   Warning: tools/docs/documentation-file-ref-check references a file that doesn't exist: m,(Documentation/translations/[
   Using alabaster theme
   Documentation/core-api/irq/managed_irq.rst:97: ERROR: Unexpected indentation. [docutils]
>> Documentation/core-api/irq/managed_irq.rst:104: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils]
   Documentation/core-api/irq/managed_irq.rst:109: ERROR: Unexpected indentation. [docutils]
   Documentation/core-api/irq/managed_irq.rst:111: ERROR: Unexpected indentation. [docutils]
   Documentation/core-api/irq/managed_irq.rst:118: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils]
   Documentation/core-api/irq/managed_irq.rst:122: ERROR: Unexpected indentation. [docutils]
   Documentation/core-api/irq/managed_irq.rst:123: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils]


vim +104 Documentation/core-api/irq/managed_irq.rst

    65	
    66	- A QEMU instance is booted with "-device virtio-scsi-pci".
    67	  The MSI‑X device exposes 11 interrupts: 3 "management" interrupts and 8
    68	  "queue" interrupts. The driver requests the 8 queue interrupts, each of which
    69	  is affine to exactly one CPU. If that CPU goes offline, the interrupt is shut
    70	  down.
    71	
    72	  Assuming interrupt 48 is one of the queue interrupts, the following appears::
    73	
    74	    /proc/irq/48/effective_affinity_list:7
    75	    /proc/irq/48/smp_affinity_list:7
    76	
    77	  This indicates that the interrupt is served only by CPU7. Shutting down CPU7
    78	  does not migrate the interrupt to another CPU::
    79	
    80	    /proc/irq/48/effective_affinity_list:0
    81	    /proc/irq/48/smp_affinity_list:7
    82	
    83	  If the Linux kernel was built with Kconfig CONFIG_GENERIC_IRQ_DEBUGFS
    84	  enabled, this can be verified via the debugfs interface (e.g.,
    85	  /sys/kernel/debug/irq/irqs/48). The dstate field will include
    86	  IRQD_IRQ_DISABLED, IRQD_IRQ_MASKED and IRQD_MANAGED_SHUTDOWN.
    87	  A managed IRQ will also include IRQD_AFFINITY_MANAGED. For example:
    88	
    89	    # cat /sys/kernel/debug/irq/irqs/87
    90	    handler:  handle_edge_irq
    91	    device:   0000:41:00.0
    92	    status:   0x00000000
    93	    istate:   0x00004000
    94	    ddepth:   0
    95	    wdepth:   0
    96	    dstate:   0x19601200
    97			IRQD_ACTIVATED
    98			IRQD_IRQ_STARTED
    99			IRQD_SINGLE_TARGET
   100			IRQD_AFFINITY_SET
   101			IRQD_AFFINITY_MANAGED
   102			IRQD_AFFINITY_ON_ACTIVATE
   103			IRQD_HANDLE_ENFORCE_IRQCTX
 > 104	    node:     0
   105	    affinity: 9
   106	    effectiv: 9
   107	    pending:
   108	    domain:  IR-PCI-MSIX-0000:41:00.0-12
   109	     hwirq:   0x8
   110	     chip:    IR-PCI-MSIX-0000:41:00.0
   111	      flags:   0x430
   112			 IRQCHIP_SKIP_SET_WAKE
   113			 IRQCHIP_ONESHOT_SAFE
   114	
   115	      address_hi: 0x00000000
   116	      address_lo: 0xfee00000
   117	      msg_data:   0x00000008
   118	     parent:
   119		domain:  AMD-IR-3-14
   120		 hwirq:   0x41000000
   121		 chip:    AMD-IR
   122		  flags:   0x0
   123		 parent:
   124		    domain:  VECTOR
   125		     hwirq:   0x57
   126		     chip:    APIC
   127		      flags:   0x0
   128		     Vector:    33
   129		     Target:     9
   130		     move_in_progress: 0
   131		     is_managed:       1
   132		     can_reserve:      0
   133		     has_reserved:     0
   134		     cleanup_pending:  0
   135	

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

^ permalink raw reply

* Re: [PATCH RFC v5 00/53] guest_memfd: In-place conversion support
From: Michael Roth @ 2026-04-29 23:51 UTC (permalink / raw)
  To: ackerleytng
  Cc: aik, andrew.jones, binbin.wu, brauner, chao.p.peng, david,
	ira.weiny, jmattson, jthoughton, oupton, pankaj.gupta, qperret,
	rick.p.edgecombe, rientjes, shivankg, steven.price, tabba, willy,
	wyihan, yan.y.zhao, forkloop, pratyush, suzuki.poulose,
	aneesh.kumar, Paolo Bonzini, Sean Christopherson, Thomas Gleixner,
	Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
	Jonathan Corbet, Shuah Khan, Shuah Khan, Vishal Annapurve,
	Andrew Morton, Chris Li, Kairui Song, Kemeng Shi, Nhat Pham,
	Baoquan He, Barry Song, Axel Rasmussen, Yuanchu Xie, Wei Xu,
	Youngjun Park, Qi Zheng, Shakeel Butt, Kiryl Shutsemau,
	Jason Gunthorpe, Vlastimil Babka, kvm, linux-kernel,
	linux-trace-kernel, linux-doc, linux-kselftest, linux-mm,
	linux-coco
In-Reply-To: <20260428-gmem-inplace-conversion-v5-0-d8608ccfca22@google.com>

On Tue, Apr 28, 2026 at 04:24:55PM -0700, Ackerley Tng via B4 Relay wrote:
> [Some people who received this message don't often get email from devnull+ackerleytng.google.com@kernel.org. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
> 
> This is RFC v5 of guest_memfd in-place conversion support.
> 
> Up till now, guest_memfd supports the entire inode worth of memory being
> used as all-shared, or all-private. CoCo VMs may request guest memory to be
> converted between private and shared states, and the only way to support
> that currently would be to have the userspace VMM provide two sources of
> backing memory from completely different areas of physical memory.
> 
> pKVM has a use case for in-place sharing: the guest and host may be
> cooperating on given data, and pKVM doesn't protect data through
> encryption, so copying that given data between different areas of physical
> memory as part of conversions would be unnecessary work.
> 
> This series also serves as a foundation for guest_memfd huge page
> support. Now, guest_memfd only supports PAGE_SIZE pages, so if two sources
> of backing memory are used, the userspace VMM could maintain a steady total
> memory utilized by punching out the pages that are not used. When huge
> pages are available in guest_memfd, even if the backing memory source
> supports hole punching within a huge page, punching out pages to maintain
> the total memory utilized by a VM would be introducing lots of
> fragmentation.
> 
> In-place conversion avoids fragmentation by allowing the same physical
> memory to be used for both shared and private memory, with guest_memfd
> tracks the shared/private status of all the pages at a per-page
> granularity.
> 
> The central principle, which guest_memfd continues to uphold, is that any
> guest-private page will not be mappable to host userspace. All pages will
> be mmap()-able in host userspace, but accesses to guest-private pages (as
> tracked by guest_memfd) will result in a SIGBUS.
> 
> This series introduces a guest_memfd ioctl (not kvm, vm or vcpu, but
> guest_memfd ioctl) that allows userspace to set memory
> attributes (shared/private) directly through the guest_memfd. This is the
> appropriate interface because shared/private-ness is a property of memory
> and hence the request should be sent directly to the memory provider -
> guest_memfd.
> 
> Tested with both CONFIG_KVM_VM_MEMORY_ATTRIBUTES enabled and disabled:
> 
> + tools/testing/selftests/kvm/guest_memfd_test.c
> + tools/testing/selftests/kvm/pre_fault_memory_test.c
> + tools/testing/selftests/kvm/x86/guest_memfd_conversions_test.c
> + tools/testing/selftests/kvm/x86/private_mem_conversions_test.c
> + tools/testing/selftests/kvm/x86/private_mem_conversions_test.sh
> + tools/testing/selftests/kvm/x86/private_mem_kvm_exits_test.c
> 
> Updates for this revision:
> 
> + For TDX and SNP, PRESERVE supported only before VM is finalized only for
>   to_private conversions.
>     + This allows PRESERVE to be used as part of the VM memory
>       loading/encryption flow
>     + Only support PRESERVE for to_private conversions (to_shared on
>       populated memory on TDX would cause zeroing)
>     + Relaxed constraints for SNP and TDX to allow NULL to be passed as
>       source address.
> + Dropped KVM_CAP_MEMORY_ATTRIBUTES2. KVM_CAP_MEMORY_ATTRIBUTES reports
>   attributes supported by the KVM_SET_MEMORY_ATTRIBUTES VM ioctl, and
>   KVM_CAP_GUEST_MEMFD_MEMORY_ATTRIBUTES reports attributes supported bt the
>   KVM_SET_MEMORY_ATTRIBUTES2 guest_memfd ioctl.
>     + KVM_SET_MEMORY_ATTRIBUTES2 is not supported by the VM ioctl
> + Resolve locking issue when kvm_gmem_get_attribute() is called from
>   kvm_mmu_zap_collapsible_spte() by bugging the VM. guest_memfd memslots
>   don't support dirty tracking, so the locking issue is not on an
>   accessible code path.
> + Moved guest_memfd_conversions_test.c to only be compiled and tested for
>   x86, since it depends so heavily on KVM_X86_SW_PROTECTED_VM's as a
>   testing vehicle
> 
> TODOs
> 
> + Perhaps further clarify PRESERVE flag: [8]

I made a super-long-winded reply to that thread, but to summarize:

PRESERVE flag has different enumeration/behavior/enforcement for pre-launch
vs. post-launch, and similar considerations might come into play for
other flags, so to make it easier to enumerate what flags are available
for pre-launch/post-launch, maybe we could have 2 capabilities instead
of 1:

  KVM_CAP_MEMORY_ATTRIBUTES2_PRE_LAUNCH_FLAGS
  KVM_CAP_MEMORY_ATTRIBUTES2_FLAGS

where SNP/TDX would only advertise PRESERVE for PRE_LAUNCH, and pKVM I
guess would enumerate it for both (or maybe just POST_LAUNCH?)

That lets us keep the flags definitions more straightforward but still
allows userspace to easily enumerate what exactly should be available at
pre vs. post launch time, and give us some flexibility to detail
variations in behavior between the 2 phases without documenting
edge-cases in terms of VM types.

> + Resolve issue where guest_memfd_conversions_test, which uses the
>   kselftest framework, doesn't perform teardown on assertion
>   failure. Please see proposal at [9]
> + Test with TDX selftests. We're in the process of rebasing TDX selftests
>   on this series and will post updates when that's tested.
> 
> I would like feedback on:
> 
> + Content modes: 0 (MODE_UNSPECIFIED), ZERO, and PRESERVE. Is that all
>   good, or does anyone think there is a use case for something else?
> + Should the content modes apply even if no attribute changes are required?
>     + See notes added in "KVM: guest_memfd: Apply content modes while
>       setting memory attributes"

Looking at the example you have there:

  + Note: These content modes apply to the entire requested range, not
  + just the parts of the range that underwent conversion. For example, if
  + this was the initial state:
  + 
  +   * [0x0000, 0x1000): shared
  +   * [0x1000, 0x2000): private
  +   * [0x2000, 0x3000): shared
  + and range [0x0000, 0x3000) was set to shared, the content mode would
  + apply to all memory in [0x0000, 0x3000), not just the range that
  + underwent conversion [0x1000, 0x2000).

Userspace would be aware of whether the range contains pages that were
already set to private, so if it really wants to set the just the
[0x1000, 0x2000) range to shared with appropriate content mode, it is
fully able to do so by just issuing the ioctl for that specific range.
If it attempts to issue it for the entire range, it only seems like it
would defy normal expectations and cause confusion to skip ranges, and
I'm not sure it gains us anything useful in exchange for that potential
confusion.

>     + Possibly related: should setting attributes be allowed if some
>       sub-range requested already has the requested attribute?

As it is now, userspace has that capability (to use finer-grained ranges
if it doesn't want to re-issue unecessary/unwanted conversions), similar
to above. And KVM internally will just issue kvm_arch_gmem_prepare()
calls so that architecture-specific handling can deal with this case
(e.g. SNP's sev_gmem_prepare() already checks if the corresponding
attribute is set in the RMP table and just skips it otherwise). So I
don't think we really gain anything but added complexity if we try to
make gmem more selective about it.

-Mike

> + Structure of how various content modes are checked for support or
>   applied? I used overridable weak functions for architectures that haven't
>   defined support, and defined overrides for x86 to show how I think it would
>   work. For CoCo platforms, I only implemented TDX for illustration purposes
>   and might need help with the other platforms. Should I have used
>   kvm_x86_ops? I tried and found myself defining lots of boilerplate.
> + The use of private_mem_conversions_test.sh to run different options in
>   private_mem_conversions_test. If this makes sense, I'll adjust the
>   Makefile to have private_mem_conversions_test tested only via the script.
> 
> This series is based on kvm/next, and here's the tree for your convenience:
> 
> https://github.com/googleprodkernel/linux-cc/commits/guest_memfd-inplace-conversion-v5
> 
> Older series:
> 
> + RFCv4 is at [7]
> + RFCv3 is at [6]
> + RFCv2 is at [5]
> + RFCv1 is at [4]
> + Previous versions of this feature, part of other series, are available at
>   [1][2][3].
> 
> [1] https://lore.kernel.org/all/bd163de3118b626d1005aa88e71ef2fb72f0be0f.1726009989.git.ackerleytng@google.com/
> [2] https://lore.kernel.org/all/20250117163001.2326672-6-tabba@google.com/
> [3] https://lore.kernel.org/all/b784326e9ccae6a08388f1bf39db70a2204bdc51.1747264138.git.ackerleytng@google.com/
> [4] https://lore.kernel.org/all/cover.1760731772.git.ackerleytng@google.com/T/
> [5] https://lore.kernel.org/all/cover.1770071243.git.ackerleytng@google.com/T/
> [6] https://lore.kernel.org/r/20260313-gmem-inplace-conversion-v3-0-5fc12a70ec89@google.com/T/
> [7] https://lore.kernel.org/all/20260326-gmem-inplace-conversion-v4-0-e202fe950ffd@google.com/T/
> [8] https://lore.kernel.org/all/CAEvNRgGbMhkX310CkFY_M5x-zod=BDTiuznrZ0XvFPUK7weL1A@mail.gmail.com/
> [9] https://lore.kernel.org/all/20260414-selftest-global-metadata-v1-0-fd223922bc57@google.com/T/
> 
> Signed-off-by: Ackerley Tng <ackerleytng@google.com>
> ---
> Ackerley Tng (34):
>       KVM: x86/mmu: Bug the VM if gmem attributes are queried to determine max mapping level
>       KVM: guest_memfd: Update kvm_gmem_populate() to use gmem attributes
>       KVM: guest_memfd: Only prepare folios for private pages
>       KVM: Move kvm_supported_mem_attributes() to kvm_host.h
>       KVM: guest_memfd: Add basic support for KVM_SET_MEMORY_ATTRIBUTES2
>       KVM: guest_memfd: Ensure pages are not in use before conversion
>       KVM: guest_memfd: Call arch invalidate hooks on conversion
>       KVM: guest_memfd: Return early if range already has requested attributes
>       KVM: guest_memfd: Advertise KVM_SET_MEMORY_ATTRIBUTES2 ioctl
>       KVM: guest_memfd: Handle lru_add fbatch refcounts during conversion safety check
>       KVM: guest_memfd: Use actual size for invalidation in kvm_gmem_release()
>       KVM: guest_memfd: Determine invalidation filter from memory attributes
>       KVM: guest_memfd: Introduce default handlers for content modes
>       KVM: guest_memfd: Apply content modes while setting memory attributes
>       KVM: x86: Support SW_PROTECTED_VM in applying content modes
>       KVM: TDX: Make source page optional for KVM_TDX_INIT_MEM_REGION
>       KVM: x86: Support SNP and TDX applying content modes
>       KVM: x86: Bug CoCo VM on page fault before finalizing
>       KVM: Add CAP to enumerate supported SET_MEMORY_ATTRIBUTES2 flags
>       KVM: selftests: Test basic single-page conversion flow
>       KVM: selftests: Test conversion flow when INIT_SHARED
>       KVM: selftests: Test conversion precision in guest_memfd
>       KVM: selftests: Test conversion before allocation
>       KVM: selftests: Convert with allocated folios in different layouts
>       KVM: selftests: Test that truncation does not change shared/private status
>       KVM: selftests: Test conversion with elevated page refcount
>       KVM: selftests: Test that conversion to private does not support ZERO
>       KVM: selftests: Support checking that data not equal expected
>       KVM: selftests: Test that not specifying a conversion flag scrambles memory contents
>       KVM: selftests: Reset shared memory after hole-punching
>       KVM: selftests: Provide function to look up guest_memfd details from gpa
>       KVM: selftests: Make TEST_EXPECT_SIGBUS thread-safe
>       KVM: selftests: Update private_mem_conversions_test to mmap() guest_memfd
>       KVM: selftests: Add script to exercise private_mem_conversions_test
> 
> Michael Roth (1):
>       KVM: SEV: Make 'uaddr' parameter optional for KVM_SEV_SNP_LAUNCH_UPDATE
> 
> Sean Christopherson (18):
>       KVM: guest_memfd: Introduce per-gmem attributes, use to guard user mappings
>       KVM: Rename KVM_GENERIC_MEMORY_ATTRIBUTES to KVM_VM_MEMORY_ATTRIBUTES
>       KVM: Enumerate support for PRIVATE memory iff kvm_arch_has_private_mem is defined
>       KVM: Stub in ability to disable per-VM memory attribute tracking
>       KVM: guest_memfd: Wire up kvm_get_memory_attributes() to per-gmem attributes
>       KVM: Move KVM_VM_MEMORY_ATTRIBUTES config definition to x86
>       KVM: Let userspace disable per-VM mem attributes, enable per-gmem attributes
>       KVM: guest_memfd: Enable INIT_SHARED on guest_memfd for x86 Coco VMs
>       KVM: selftests: Create gmem fd before "regular" fd when adding memslot
>       KVM: selftests: Rename guest_memfd{,_offset} to gmem_{fd,offset}
>       KVM: selftests: Add support for mmap() on guest_memfd in core library
>       KVM: selftests: Add selftests global for guest memory attributes capability
>       KVM: selftests: Add helpers for calling ioctls on guest_memfd
>       KVM: selftests: Test that shared/private status is consistent across processes
>       KVM: selftests: Provide common function to set memory attributes
>       KVM: selftests: Check fd/flags provided to mmap() when setting up memslot
>       KVM: selftests: Update pre-fault test to work with per-guest_memfd attributes
>       KVM: selftests: Update private memory exits test to work with per-gmem attributes
> 
>  Documentation/virt/kvm/api.rst                     | 139 ++++-
>  .../virt/kvm/x86/amd-memory-encryption.rst         |  19 +-
>  Documentation/virt/kvm/x86/intel-tdx.rst           |   4 +
>  arch/x86/include/asm/kvm_host.h                    |   2 +-
>  arch/x86/kvm/Kconfig                               |  15 +-
>  arch/x86/kvm/mmu/mmu.c                             |  20 +-
>  arch/x86/kvm/svm/sev.c                             |  18 +-
>  arch/x86/kvm/vmx/tdx.c                             |   8 +-
>  arch/x86/kvm/x86.c                                 | 145 ++++-
>  include/linux/kvm_host.h                           |  74 ++-
>  include/trace/events/kvm.h                         |   4 +-
>  include/uapi/linux/kvm.h                           |  21 +
>  mm/swap.c                                          |   2 +
>  tools/testing/selftests/kvm/Makefile.kvm           |   5 +
>  tools/testing/selftests/kvm/include/kvm_util.h     | 141 ++++-
>  tools/testing/selftests/kvm/include/test_util.h    |  34 +-
>  .../selftests/kvm/kvm_has_gmem_attributes.c        |  17 +
>  tools/testing/selftests/kvm/lib/kvm_util.c         | 130 +++--
>  tools/testing/selftests/kvm/lib/test_util.c        |   7 -
>  tools/testing/selftests/kvm/lib/x86/sev.c          |   2 +-
>  .../testing/selftests/kvm/pre_fault_memory_test.c  |   4 +-
>  .../kvm/x86/guest_memfd_conversions_test.c         | 552 +++++++++++++++++++
>  .../kvm/x86/private_mem_conversions_test.c         |  55 +-
>  .../kvm/x86/private_mem_conversions_test.sh        | 128 +++++
>  .../selftests/kvm/x86/private_mem_kvm_exits_test.c |  38 +-
>  virt/kvm/Kconfig                                   |   3 +-
>  virt/kvm/guest_memfd.c                             | 591 ++++++++++++++++++++-
>  virt/kvm/kvm_main.c                                |  87 ++-
>  28 files changed, 2075 insertions(+), 190 deletions(-)
> ---
> base-commit: 39f1c201b93f4ff71631bac72cff6eb155f976a4
> change-id: 20260225-gmem-inplace-conversion-bd0dbd39753a
> 
> Best regards,
> --
> Ackerley Tng <ackerleytng@google.com>
> 
> 

^ permalink raw reply

* Re: [PATCH RFC v4 10/44] KVM: guest_memfd: Add support for KVM_SET_MEMORY_ATTRIBUTES2
From: Michael Roth @ 2026-04-29 23:21 UTC (permalink / raw)
  To: Ackerley Tng
  Cc: aik, andrew.jones, binbin.wu, brauner, chao.p.peng, david,
	ira.weiny, jmattson, jthoughton, oupton, pankaj.gupta, qperret,
	rick.p.edgecombe, rientjes, shivankg, steven.price, tabba, willy,
	wyihan, yan.y.zhao, forkloop, pratyush, suzuki.poulose,
	aneesh.kumar, Paolo Bonzini, Sean Christopherson, Thomas Gleixner,
	Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
	Jonathan Corbet, Shuah Khan, Shuah Khan, Vishal Annapurve,
	Andrew Morton, Chris Li, Kairui Song, Kemeng Shi, Nhat Pham,
	Baoquan He, Barry Song, Axel Rasmussen, Yuanchu Xie, Wei Xu,
	Jason Gunthorpe, Vlastimil Babka, kvm, linux-kernel,
	linux-trace-kernel, linux-doc, linux-kselftest, linux-mm
In-Reply-To: <CAEvNRgGbMhkX310CkFY_M5x-zod=BDTiuznrZ0XvFPUK7weL1A@mail.gmail.com>

On Fri, Apr 24, 2026 at 12:08:45PM -0700, Ackerley Tng wrote:
> Michael Roth <michael.roth@amd.com> writes:
> 
> Thank you for your patches!
> 
> >
> > [...snip...]
> >
> >>
> >> I also did some minor updates (prefixed with a "[squash]" tag) to advertise
> >> the KVM_SET_MEMORY_ATTRIBUTES2_PRESERVED flag so it can be used by
> >
> > Though I'm not sure how we deal with it if SNP/TDX at some point become
> > capable of using the PRESERVED flag *after* populate... but maybe that's
> > too unlikely to worry about? If we wanted to address it though, we could
> > have both PRESERVED and PRESERVED_BEFORE_LAUNCH so they can be
> > enumerated separately from the start.
> >
> 
> Not sure how likely it is, but if SNP and TDX can honor PRESERVE
> semantics after populate, I think we could implement support under a new
> flag like CIPHER.

That works, but it still makes things *slightly* awkward due to special-casing
the PRESERVE semantics for 1 guest type vs. another.

For instance, for the QEMU patches what I'd like to do is be able to
issue conversions with the PRESERVE flag set, and it makes sense that
when the function that handles that gets called (in this case,
guest_memfd_set_memory_attributes_fd()), it asserts that QEMU
checked for the corresponding capability/flag (via
KVM_CAP_MEMORY_ATTRIBUTES2_FLAGS) earlier on during QEMU init time
and stored that into kvm_supported_memory_attributes2_flag to check
against later in cases where we try to use PRESERVE:

  https://github.com/AMDESE/qemu/blob/1308d4bcd8dd8acd151243e96ff0be4098389b93/accel/kvm/kvm-all.c#L1655

but that check is sort of incomplete: for use-cases like pKVM it makes
sense to allow conversion+PRESERVE at all times, but for SNP/TDX we'd
ideally have an additional check like:

  static int guest_memfd_set_memory_attributes_fd(..., attrs, flags)
  {
      struct kvm_memory_attributes2 attrs;
      ...

      assert(kvm_supported_memory_attributes & attrs);
      assert(kvm_supported_memory_attributes2_flags & flags);

      /* SNP and TDX only support PRESERVE prior to launch) */
      if (vm_type_is_snp_or_tdx() && (flags & PRESERVE))
          assert(vcpus_not_yet_started_and_LAUNCH_FINISH_etc_not_yet_called);
  
      ...
  }

maybe that's fine, but there's just some asymmetry in the fact that
the capabilities checks are essentially self-documenting/self-enforcing
in this regard, except for PRESERVE+SNP/TDX where userspace needs to
sprinkle some additional vm_type-specific logic.

So that's why I was sort of thinking PRESERVE might deserve a
PRESERVE_BEFORE_LAUNCH or something so the kernel can enumerate the
support rather than userspace needing to read into the documentation /
implementation to implement finer-grained checks/etc.

> 
> CIPHER can then be used to mean "do the encryption or decryption", and
> for platforms not supporting encryption, they'd stick with PRESERVE?

It's all theoretical, but I'd imagine that *if* SNP/TDX ever added
support for allowing userspace to write encrypted memory into a guest
range, it would be something like:

  - write plain-text data to corresponding GPA
  - issue shared-to-private conversion with CIPHER (or PRESERVE) bit
    set, which will then be augmented to make some sort of firmware
    call to encrypt the memory with guest key and make it visible to
    guest

So, at a high-level, if host writes 0xbeef to shared memory, then upon
completion of the converison ioctl, 0xbeef then be visible to the guest
via encrypted/private memory, which seems to match the semantics of
PRESERVE perfectly:

+``KVM_SET_MEMORY_ATTRIBUTES2_PRESERVE``
+
+  On conversion, KVM guarantees memory contents will be preserved with
+  respect to the last written unencrypted value.  As a concrete
+  example, if the host writes ``0xbeef`` to shared memory and converts 
+  the memory to private, the guest will also read ``0xbeef``, even if
+  the in-memory data is encrypted as part of the conversion.  And vice
+  versa, if the guest writes ``0xbeef`` to private memory and then
+  converts the memory to shared, the host (and guest) will read
+  ``0xbeef`` (if the memory is accessible).

So it seems like a waste to have to add a CIPHER that would essentially
have the same semantics (even though the architectural implementation
details are different), and then complicate the documentation of
PRESERVE to have vm-specific behaviors that userspace needs to account
for, rather than leaving PRESERVE as-is and adding a
PRESERVE_BEFORE_LAUNCH that neatly encapsulates the specialness of the
SNP/TDX flow without complicating the more generally-defined flags like
PRESERVE that could in theory become usable by all variants in the future.

But then that sort of has me wondering if PRESERVE should be
PRESERVE_AFTER_LAUNCH (to cover cases where maybe SNP/TDX gain this
ability in the future, but can only use it post-launch, where other
architectures might allow it both before/after.)

...but that seems to be getting into a more general issue of how we
determine what is available before-launch/after-launch, so maybe instead
of introducing new flags, we just have 2 capabilities that returns what
flags are available at each particular phase, e.g.

  KVM_CAP_MEMORY_ATTRIBUTES2_PRE_LAUNCH_FLAGS
  KVM_CAP_MEMORY_ATTRIBUTES2_POST_LAUNCH_FLAGS

And then userspace can easily probe/lock down what it expects to be
available prior to launching the guest vs after without any vm-specific
logic, and we can continue to just stick with PRESERVE/ZERO. (and if
we do end up need to further distinguish pre vs. post-launch behavior,
this gives us some flexibility to handle that as well).

> 
> Should we redefine the semantics of PRESERVE to be "ensure that memory
> contents don't change while guest_memfd tracking is being updated" and
> avoid making a commitment on how the guest should read the memory?
> 
> The above update would be aligned with ZERO not being allowed for
> conversions to private (because KVM/guest_memfd does not make guarantees
> about the contract between the host and guest.

It's a little awkward here too, because PRESERVE is sort of making a
contract between host/guest: we are providing trusted data to the guest.
However, there's an attestation proces that allows the guest to enforce
that for pre-launch and ensure we are following through on that
contract. PRESERVE for post-launch...I'm not sure how that could be
enforced, but I think this is further reason to allow pre vs. post
launch flags to be enumerated separately.

Thanks,

Mike

> 
> This way, all of those (ZERO, PRESERVE) will focus on KVM's interface
> with the host.
> 
> This lines up for SW_PROTECTED_VMs too, since reading memory that didn't
> change in the guest is the contract between SW_PROTECTED_VMs and the
> host.
> 
> >> userspace for SNP/TDX in the kvm_gmem_populate() path as agreed upon
> >> during PUCK.
> >>
> >>
> >> [...snip...]
> >>

^ permalink raw reply

* Re: [PATCH v4 05/11] PCI: liveupdate: Inherit bus numbers during Live Update
From: David Matlack @ 2026-04-29 22:56 UTC (permalink / raw)
  To: Jacob Pan
  Cc: iommu, kexec, linux-doc, linux-kernel, linux-mm, linux-pci,
	Adithya Jayachandran, Alexander Graf, Alex Williamson,
	Bjorn Helgaas, Chris Li, David Rientjes, Jason Gunthorpe,
	Joerg Roedel, Jonathan Corbet, Josh Hilke, Leon Romanovsky,
	Lukas Wunner, Mike Rapoport, Parav Pandit, Pasha Tatashin,
	Pranjal Shrivastava, Pratyush Yadav, Robin Murphy, Saeed Mahameed,
	Samiullah Khawaja, Shuah Khan, Will Deacon, William Tu, Yi Liu
In-Reply-To: <20260429152814.000005f7@linux.microsoft.com>

On Wed, Apr 29, 2026 at 3:28 PM Jacob Pan <jacob.pan@linux.microsoft.com> wrote:

> Below looks correct to me, but I have another question. How do you
> stablize PCI BARs? PCI BDF stability does not guarantee BARs don't get
> moved, right?

That's right, this series does not guarantee that BARs do not get
moved or sized. For P2P use-cases, we do need BARs to remain
stable and avoid sizing during enumeration. We also need to
ensure bridge windows are preserved.

My plan is to send another series to ready the PCI core for P2P
use-cases, sometime after this one.

^ permalink raw reply

* Re: [PATCH] Documentation: riscv: cmodx: fix typos
From: Randy Dunlap @ 2026-04-29 22:38 UTC (permalink / raw)
  To: Avi Radinsky, palmer, pjw, aou, alex, corbet
  Cc: linux-riscv, linux-doc, linux-kernel
In-Reply-To: <391d16fb-5f11-45fa-8f3b-1debe095695e@tennr.com>



On 4/29/26 3:35 PM, Avi Radinsky wrote:
> Fix typos in the dynamic ftrace section: atmoic -> atomic (twice),
> pacthable -> patchable, derect -> directed.
> 
> Signed-off-by: Avi Radinsky <avi.radinsky@tennr.com>

Acked-by: Randy Dunlap <rdunlap@infradead.org>
Thanks.

> ---
>  Documentation/arch/riscv/cmodx.rst | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
> 
> diff --git a/Documentation/arch/riscv/cmodx.rst b/Documentation/arch/riscv/cmodx.rst
> index 40ba53bed..cbfa812a1 100644
> --- a/Documentation/arch/riscv/cmodx.rst
> +++ b/Documentation/arch/riscv/cmodx.rst
> @@ -21,13 +21,13 @@ call at each patchable function entry, and patches it dynamically at runtime to
>  enable or disable the redirection. In the case of RISC-V, 2 instructions,
>  AUIPC + JALR, are required to compose a function call. However, it is impossible
>  to patch 2 instructions and expect that a concurrent read-side executes them
> -without a race condition. This series makes atmoic code patching possible in
> +without a race condition. This series makes atomic code patching possible in
>  RISC-V ftrace. Kernel preemption makes things even worse as it allows the old
>  state to persist across the patching process with stop_machine().
>  
>  In order to get rid of stop_machine() and run dynamic ftrace with full kernel
>  preemption, we partially initialize each patchable function entry at boot-time,
> -setting the first instruction to AUIPC, and the second to NOP. Now, atmoic
> +setting the first instruction to AUIPC, and the second to NOP. Now, atomic
>  patching is possible because the kernel only has to update one instruction.
>  According to Ziccif, as long as an instruction is naturally aligned, the ISA
>  guarantee an  atomic update.
> @@ -36,8 +36,8 @@ By fixing down the first instruction, AUIPC, the range of the ftrace trampoline
>  is limited to +-2K from the predetermined target, ftrace_caller, due to the lack
>  of immediate encoding space in RISC-V. To address the issue, we introduce
>  CALL_OPS, where an 8B naturally align metadata is added in front of each
> -pacthable function. The metadata is resolved at the first trampoline, then the
> -execution can be derect to another custom trampoline.
> +patchable function. The metadata is resolved at the first trampoline, then the
> +execution can be directed to another custom trampoline.
>  
>  CMODX in the User Space
>  -----------------------

-- 
~Randy

^ permalink raw reply

* [PATCH] Documentation: riscv: cmodx: fix typos
From: Avi Radinsky @ 2026-04-29 22:35 UTC (permalink / raw)
  To: palmer, pjw, aou, alex, corbet
  Cc: linux-riscv, linux-doc, linux-kernel, avi.radinsky

Fix typos in the dynamic ftrace section: atmoic -> atomic (twice),
pacthable -> patchable, derect -> directed.

Signed-off-by: Avi Radinsky <avi.radinsky@tennr.com>
---
 Documentation/arch/riscv/cmodx.rst | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Documentation/arch/riscv/cmodx.rst b/Documentation/arch/riscv/cmodx.rst
index 40ba53bed..cbfa812a1 100644
--- a/Documentation/arch/riscv/cmodx.rst
+++ b/Documentation/arch/riscv/cmodx.rst
@@ -21,13 +21,13 @@ call at each patchable function entry, and patches it dynamically at runtime to
 enable or disable the redirection. In the case of RISC-V, 2 instructions,
 AUIPC + JALR, are required to compose a function call. However, it is impossible
 to patch 2 instructions and expect that a concurrent read-side executes them
-without a race condition. This series makes atmoic code patching possible in
+without a race condition. This series makes atomic code patching possible in
 RISC-V ftrace. Kernel preemption makes things even worse as it allows the old
 state to persist across the patching process with stop_machine().
 
 In order to get rid of stop_machine() and run dynamic ftrace with full kernel
 preemption, we partially initialize each patchable function entry at boot-time,
-setting the first instruction to AUIPC, and the second to NOP. Now, atmoic
+setting the first instruction to AUIPC, and the second to NOP. Now, atomic
 patching is possible because the kernel only has to update one instruction.
 According to Ziccif, as long as an instruction is naturally aligned, the ISA
 guarantee an  atomic update.
@@ -36,8 +36,8 @@ By fixing down the first instruction, AUIPC, the range of the ftrace trampoline
 is limited to +-2K from the predetermined target, ftrace_caller, due to the lack
 of immediate encoding space in RISC-V. To address the issue, we introduce
 CALL_OPS, where an 8B naturally align metadata is added in front of each
-pacthable function. The metadata is resolved at the first trampoline, then the
-execution can be derect to another custom trampoline.
+patchable function. The metadata is resolved at the first trampoline, then the
+execution can be directed to another custom trampoline.
 
 CMODX in the User Space
 -----------------------
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH v2 0/8] x86/resctrl: Support for AMD Global (Slow) Memory Bandwidth Allocation
From: Reinette Chatre @ 2026-04-29 22:34 UTC (permalink / raw)
  To: Babu Moger, corbet, tony.luck, tglx, mingo, bp, dave.hansen
  Cc: skhan, x86, Dave.Martin, james.morse, hpa, akpm, rdunlap,
	dapeng1.mi, kees, elver, lirongqing, ebiggers, paulmck, seanjc,
	pawan.kumar.gupta, nikunj, yazen.ghannam, peterz, chang.seok.bae,
	kim.phillips, thomas.lendacky, naveen, elena.reshetova, xin,
	linux-doc, linux-kernel, eranian, peternewman
In-Reply-To: <cover.1776980182.git.babu.moger@amd.com>

Hi Babu,

On 4/23/26 6:41 PM, Babu Moger wrote:
> 
> This series adds resctrl support for two new AMD memory-bandwidth
> allocation features:
> 
>   - GMBA  - Global Memory Bandwidth Allocation (hardware name: GLBE).
>             Bounds DRAM bandwidth for groups of threads that span
>             multiple L3 QoS domains, rather than being per-L3 like MBA.
> 
>   - GSMBA - Global Slow Memory Bandwidth Allocation (hardware name:
>             GLSBE). The CXL.memory / slow-memory counterpart of GMBA,
>             analogous to how SMBA relates to MBA.
> 
> Both features share a new "NPS-node" control domain: a set of QoS (L3)
> domains grouped together and aligned to the system's NPS (Nodes Per
> Socket) BIOS configuration. Although the control domain is NPS-scoped,
> the underlying bandwidth-limit MSRs (MSR_IA32_GMBA_BW_BASE 0xc0000600,
> MSR_IA32_GSMBA_BW_BASE 0xc0000680) are instantiated per L3. Programming
> a single control domain therefore requires writing the MSR on one CPU
> per L3 that the domain spans - a new pattern for resctrl. Patches 2/8
> and 3/8 introduce that infrastructure so the new resources can reuse
> it.
> 
> The features are documented in:
> 
>   AMD64 Zen6 Platform Quality of Service (PQOS) Extensions,
>   Publication # 69193 Revision 1.00, Issue Date March 2026
> 
> available at https://bugzilla.kernel.org/show_bug.cgi?id=206537
> 
> Series overview
> ---------------
> 
> Patches 1-5 to enable GMBA:
> 
>   1/8  x86,fs/resctrl: Add support for Global Bandwidth Enforcement (GLBE)
> 
>   2/8  x86/resctrl: Add RESCTRL_NPS_NODE scope for AMD NPS-aligned domains
>        Add a new ctrl_scope value for resctrl resources whose control
>        domain spans multiple L3s within an NPS node.
> 
>   3/8  x86/resctrl: Update control MSRs per L3 for NPS-scoped resources
>        Add resctrl_arch_update_nps(): builds a cpumask with one CPU per
>        distinct L3 in the domain, then issues rdt_ctrl_update() via
>        smp_call_function_many() on that mask. Falls back to the full
>        domain mask if the scratch masks cannot be built. Route
>        resctrl_arch_update_domains() and resctrl_arch_reset_all_ctrls()
>        through this helper when ctrl_scope == RESCTRL_NPS_NODE.
> 
>   4/8  x86,fs/resctrl: Add the resource for Global Memory Bandwidth Allocation
>        Register RDT_RESOURCE_GMBA in rdt_resources_all[] with
>        ctrl_scope=RESCTRL_NPS_NODE and schema_fmt=RANGE, add commands to
>        discover feature details.
> 
>   5/8  fs/resctrl: Add the documentation for Global Memory Bandwidth Allocation
>        Add examples in Documentation/filesystems/resctrl.rst.
> 
> Patches 6-8 to enable GSMBA in the same shape:
> 
>   6/8  x86,fs/resctrl: Add support for Global Slow Memory Bandwidth Allocation
> 
>   7/8  x86,fs/resctrl: Add the resource for Global Slow Memory Bandwidth Allocation
>        Register RDT_RESOURCE_GSMBA with ctrl_scope=RESCTRL_NPS_NODE.
> 
>   8/8  fs/resctrl: Add the documentation for Global Slow Memory Bandwidth Allocation
>        Add examples in Documentation/filesystems/resctrl.rst.
> 
> Changes since v1
> ----------------
>   - Earlier sent RFC(v1) with Global Bandwidth Enforcement (GLBE) and
>     Privilege Level Zero Association (PLZA). This series only handles
>     Global Memory Bandwidth Allocation. Both the features are sent separately.
> 
>   - Documentation
>       * Fixed grammar in the GMBA / GSMBA sections of resctrl.rst.
>       * Added examples to update GMBA and GSMBA in resctrl.rst documentation.
> 
>   - Major changes are releated to RESCTRL_NPS_NODE scope handling.
> 
>   - Commit messages
>       * Reworked the changelogs in all the patches.
> 
> Previous Revisions:
> v1 : https://lore.kernel.org/lkml/cover.1769029977.git.babu.moger@amd.com/

What are your expectations from this submission? From what I can tell this ignores
v1 feedback in several ways:
- It introduces two new resources, GMBA and GSMBA, when the previous discussion agreed that
  these are not actually new resources but instead new controls for the existing MBA/SMBA resources.
- It does not mention or attempt to address dependency on new resource schema descriptions [1]
  to support user space in understanding how to interact with the new GMBA/GSMBA controls but
  instead defers that to a snippet in the documentation that user space needs to
  parse to know this control operates at multiples of 1GB/s. 

Apart from ignoring v1 feedback this new version appears to complicate user interface even more
since now it is possible for there to be a single control that may operate at different scopes but from
what I can tell there is nothing that helps user understand whether, for example, domain "0" means
the whole system or a NUMA node? 

We have discussed several times now how resctrl interface needs to be enhanced to support
this and other upcoming features from Intel, RISC-V, Arm MPAM, and NVidia. It is thus
unexpected that this submission ignores all the previous discussions. 

Since there are so many dependencies on the new schema format support I am prioritizing this
and created a PoC that I am currently refining and hope to share soon. We can collaborate on this
to ensure that it provides a good foundation for the GMBA and GSMBA support. 

Consider what I describe in [2] - even in that response I speculate that a "scope" may be needed and
this seems to be case. I believe would help this "NPS = 4" scenario. Adding "scope" to what I shared in
[2] may look like:

info/
└── MB/
    └── resource_schemata/
        ├── GMB/
        │   ├── max:4096
        │   ├── min:1
        │   ├── resolution:1
        │   ├── scale:1
        │   ├── tolerance:0
        │   ├── type:scalar linear
        │   ├── scope:NODE
        │   └── unit:GBps
        └── MB/
            ├── max:8192
            ├── min:1
            ├── resolution:8
            ├── scale:1
            ├── tolerance:0
            ├── type:scalar linear
            ├── scope:L3
            └── unit:GBps

With a "scope" property of the control user space can know what the domain ID in the
schemata file refers to. In above example the "GMB" control has "NODE" scope so user space knows
that a domain ID refers to NUMA node. If the system is "NPS = 4" then the scope could be,
for example, "SYSTEM" (for the lack of a better term) so that user space knows that "0" means
entire system. What do you think?

Also note how the other control properties helps user understand what the schemata file control
values mean. This is what I expected the GMBA/GSMBA enabling to look like ... and you seemed to
agree [3] in v1 discussion. What changed?

Reinette

[1] https://lore.kernel.org/lkml/aPtfMFfLV1l%2FRB0L@e133380.arm.com/
[2] https://lore.kernel.org/lkml/06a237bd-c370-4d3f-99de-124e8c50e711@intel.com/  
[3] https://lore.kernel.org/lkml/91d50431-41f3-49d7-a9e6-a3bee2de5162@amd.com/

^ permalink raw reply

* Re: [PATCH v4 05/11] PCI: liveupdate: Inherit bus numbers during Live Update
From: Jacob Pan @ 2026-04-29 22:28 UTC (permalink / raw)
  To: David Matlack
  Cc: iommu, kexec, linux-doc, linux-kernel, linux-mm, linux-pci,
	Adithya Jayachandran, Alexander Graf, Alex Williamson,
	Bjorn Helgaas, Chris Li, David Rientjes, Jason Gunthorpe,
	Joerg Roedel, Jonathan Corbet, Josh Hilke, Leon Romanovsky,
	Lukas Wunner, Mike Rapoport, Parav Pandit, Pasha Tatashin,
	Pranjal Shrivastava, Pratyush Yadav, Robin Murphy, Saeed Mahameed,
	Samiullah Khawaja, Shuah Khan, Will Deacon, William Tu, Yi Liu,
	jacob.pan
In-Reply-To: <ae_SHCjNGrEPurzH@google.com>

Hi David,

On Mon, 27 Apr 2026 21:16:12 +0000
David Matlack <dmatlack@google.com> wrote:

> On 2026-04-27 08:40 PM, David Matlack wrote:
> > On 2026-04-27 11:47 AM, Jacob Pan wrote:  
> > > On Thu, 23 Apr 2026 21:23:09 +0000
> > > David Matlack <dmatlack@google.com> wrote:  
> >   
> > > > To keep things simple, inherit the secondary and subordinate bus
> > > > numbers on all bridges if any PCI devices were preserved (i.e.
> > > > even bridges without any downstream endpoints that were
> > > > preserved). This avoids accidentally assigning a bridge a new
> > > > window that overlaps with a preserved device that is downstream
> > > > of a different bridge.
> > > > 
> > > > If a bridge is enumerated with a broken topology or has no bus
> > > > numbers set during a Live Update, refuse to assign it new bus
> > > > numbers and refuse to enumerate devices below it. This is a
> > > > safety measure to prevent topology conflicts.
> > > > 
> > > > Require that CONFIG_CARDBUS is not enabled to enable
> > > > CONFIG_PCI_LIVEUPDATE since inheriting bus numbers on
> > > > PCI-to-CardBus bridges requires additional work but is not a
> > > > priority at the moment.
> > > > 
> > > > Signed-off-by: David Matlack <dmatlack@google.com>  
> >   
> > > > +	/*
> > > > +	 * During a Live Update, preserved devices are allowed
> > > > to continue
> > > > +	 * performing memory transactions. The kernel must not
> > > > change the fabric
> > > > +	 * topology, including bus numbers, since that would
> > > > require disabling
> > > > +	 * and flushing any memory transactions first.
> > > > +	 *
> > > > +	 * To keep things simple, inherit the secondary and
> > > > subordinate bus
> > > > +	 * numbers on _all_ bridges if _any_ PCI devices were
> > > > preserved (i.e.
> > > > +	 * even bridges without any downstream endpoints that
> > > > were preserved).
> > > > +	 * This avoids accidentally assigning a bridge a new
> > > > window that
> > > > +	 * overlaps with a preserved device that is downstream
> > > > of a different
> > > > +	 * bridge.
> > > > +	 */
> > > > +	dev->liveupdate_inherit_buses = true;
> > > > +  
> > > This flag never gets cleared after the incoming kernel boot up,
> > > what if the user does a manual rescan via sysfs? i.e.
> > > # echo 1 > /sys/bus/pci/rescan
> > > pcibios_assign_all_busses() will never gets called for this
> > > device, and may hit this
> > > 	if (dev->liveupdate_inherit_buses) {
> > > 		pci_err(dev, "Cannot reconfigure bridge during
> > > 		Live Update!\n");
> > > 
> > > So, maybe clear it in pci_liveupdate_finish()?  
> > 
> > I think we can allo wa rescan to assign new bus numbers once all
> > devices go through pci_liveupdate_finish() by clearing
> > dev->liveupdate_inherit_buses on all devices in pci_flb_finish(). We
> > would need to hold pci_rescan_remove_lock to avoid this racing with
> > such a rescan.
> > 
> > Now that you bring up /sys/bus/pci/rescan... I think we also need to
> > set dev->liveupdate_inherit_buses in the outgoing kernel, to avoid
> > bus numbers changing on outgoing preserved devices.
> > pci_flb_preserve() should take pci_rescan_remove_lock and set
> > dev->liveupdate_inherit_buses on all devices, and
> > pci_flb_unpreserve() should do the opposite.
> > 
> > If we did all then then /sys/bus/pci/rescan can work like normal as
> > long as no devices are preserved (incoming or outgoing). If any
> > devices are preserved then dev->liveupdate_inherit_buses gets set
> > to prevent bus numbers from changing during a possible rescan.  
> 
> Something like this? This is a diff applied on top of this commit.
Below looks correct to me, but I have another question. How do you
stablize PCI BARs? PCI BDF stability does not guarantee BARs don't get
moved, right?

> 
> diff --git a/drivers/pci/liveupdate.c b/drivers/pci/liveupdate.c
> index fead478e8a04..b1b0a5b1a5df 100644
> --- a/drivers/pci/liveupdate.c
> +++ b/drivers/pci/liveupdate.c
> @@ -120,6 +120,20 @@
>  
>  #include "pci.h"
>  
> +/*
> + * During a Live Update, preserved devices are allowed to continue
> performing
> + * memory transactions. The kernel must not change the fabric
> topology,
> + * including bus numbers, since that would require disabling and
> flushing any
> + * memory transactions first.
> + *
> + * To keep things simple, inherit the secondary and subordinate bus
> numbers on
> + * _all_ bridges if _any_ PCI devices are preserved (i.e.  even
> bridges without
> + * any downstream endpoints that were preserved).  This avoids
> accidentally
> + * assigning a bridge a new window that overlaps with a preserved
> device that is
> + * downstream of a different bridge.
> + */
> +static atomic_t inherit_buses;
> +
>  struct pci_flb_outgoing {
>  	/* The pci_ser struct to be passed to the next kernel */
>  	struct pci_ser *ser;
> @@ -141,6 +155,29 @@ static unsigned long pci_ser_xa_key(unsigned
> long domain, unsigned long bdf) return domain << 16 | bdf;
>  }
>  
> +bool pci_liveupdate_inherit_buses(void)
> +{
> +	return atomic_read(&inherit_buses);
> +}
> +
> +static void pci_set_liveupdate_inherit_buses(bool enable)
> +{
> +	/* Ensure updates to inherit_buses do not race with rescans
> */
> +	pci_lock_rescan_remove();
> +
> +	/*
> +	 * Increment/decrement instead of setting directly to
> true/false so that
> +	 * pci_liveupdate_inherit_buses() returns true if any device
> is outgoing
> +	 * preserved or incoming preserved.
> +	 */
> +	if (enable)
> +		atomic_inc(&inherit_buses);
> +	else
> +		atomic_dec(&inherit_buses);
> +
> +	pci_unlock_rescan_remove();
> +}
> +
>  static int pci_flb_preserve(struct liveupdate_flb_op_args *args)
>  {
>  	struct pci_flb_outgoing *outgoing;
> @@ -180,6 +217,8 @@ static int pci_flb_preserve(struct
> liveupdate_flb_op_args *args) 
>  	args->obj = outgoing;
>  	args->data = virt_to_phys(outgoing->ser);
> +
> +	pci_set_liveupdate_inherit_buses(true);
>  	return 0;
>  }
>  
> @@ -187,6 +226,8 @@ static void pci_flb_unpreserve(struct
> liveupdate_flb_op_args *args) {
>  	struct pci_flb_outgoing *outgoing = args->obj;
>  
> +	pci_set_liveupdate_inherit_buses(false);
> +
>  	pr_debug("Unpreserving struct pci_ser\n");
>  	WARN_ON_ONCE(outgoing->ser->nr_devices);
>  	kho_unpreserve_free(outgoing->ser);
> @@ -223,6 +264,8 @@ static int pci_flb_retrieve(struct
> liveupdate_flb_op_args *args) }
>  
>  	args->obj = incoming;
> +
> +	pci_set_liveupdate_inherit_buses(true);
>  	return 0;
>  }
>  
> @@ -230,6 +273,8 @@ static void pci_flb_finish(struct
> liveupdate_flb_op_args *args) {
>  	struct pci_flb_incoming *incoming = args->obj;
>  
> +	pci_set_liveupdate_inherit_buses(false);
> +
>  	xa_destroy(&incoming->xa);
>  	kho_restore_free(incoming->ser);
>  	kfree(incoming);
> @@ -385,21 +430,6 @@ void pci_liveupdate_setup_device(struct pci_dev
> *dev) if (!xa)
>  		return;
>  
> -	/*
> -	 * During a Live Update, preserved devices are allowed to
> continue
> -	 * performing memory transactions. The kernel must not
> change the fabric
> -	 * topology, including bus numbers, since that would require
> disabling
> -	 * and flushing any memory transactions first.
> -	 *
> -	 * To keep things simple, inherit the secondary and
> subordinate bus
> -	 * numbers on _all_ bridges if _any_ PCI devices were
> preserved (i.e.
> -	 * even bridges without any downstream endpoints that were
> preserved).
> -	 * This avoids accidentally assigning a bridge a new window
> that
> -	 * overlaps with a preserved device that is downstream of a
> different
> -	 * bridge.
> -	 */
> -	dev->liveupdate_inherit_buses = true;
> -
>  	key = pci_ser_xa_key(pci_domain_nr(dev->bus),
> pci_dev_id(dev)); dev_ser = xa_load(xa, key);
>  
> diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
> index 09bab39738d7..abd8379b99cf 100644
> --- a/drivers/pci/pci.h
> +++ b/drivers/pci/pci.h
> @@ -1442,6 +1442,7 @@ static inline int pci_msix_write_tph_tag(struct
> pci_dev *pdev, unsigned int inde #ifdef CONFIG_PCI_LIVEUPDATE
>  void pci_liveupdate_setup_device(struct pci_dev *dev);
>  void pci_liveupdate_cleanup_device(struct pci_dev *dev);
> +bool pci_liveupdate_inherit_buses(void);
>  #else
>  static inline void pci_liveupdate_setup_device(struct pci_dev *dev)
>  {
> @@ -1450,6 +1451,11 @@ static inline void
> pci_liveupdate_setup_device(struct pci_dev *dev) static inline void
> pci_liveupdate_cleanup_device(struct pci_dev *dev) {
>  }
> +
> +static inline bool pci_liveupdate_inherit_buses(void)
> +{
> +	return false;
> +}
>  #endif
>  
>  #endif /* DRIVERS_PCI_H */
> diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
> index fa26f4170add..f94fa1fc76cc 100644
> --- a/drivers/pci/probe.c
> +++ b/drivers/pci/probe.c
> @@ -1374,9 +1374,9 @@ bool pci_ea_fixed_busnrs(struct pci_dev *dev,
> u8 *sec, u8 *sub) return true;
>  }
>  
> -static bool pci_should_assign_new_buses(struct pci_dev *dev)
> +static bool pci_should_assign_new_buses(void)
>  {
> -	if (dev->liveupdate_inherit_buses)
> +	if (pci_liveupdate_inherit_buses())
>  		return false;
>  
>  	return pcibios_assign_all_busses();
> @@ -1409,7 +1409,7 @@ static int pci_scan_bridge_extend(struct
> pci_bus *bus, struct pci_dev *dev, int max, unsigned int
> available_buses, int pass)
>  {
> -	const bool assign_new_buses =
> pci_should_assign_new_buses(dev);
> +	const bool assign_new_buses = pci_should_assign_new_buses();
>  	struct pci_bus *child;
>  	u32 buses;
>  	u16 bctl;
> @@ -1518,7 +1518,7 @@ static int pci_scan_bridge_extend(struct
> pci_bus *bus, struct pci_dev *dev, goto out;
>  		}
>  
> -		if (dev->liveupdate_inherit_buses) {
> +		if (pci_liveupdate_inherit_buses()) {
>  			pci_err(dev, "Cannot reconfigure bridge
> during Live Update!\n"); pci_err(dev, "Downstream devices will not be
> enumerated!\n"); goto out;
> diff --git a/include/linux/pci.h b/include/linux/pci.h
> index 9a602b322e3c..dd6b26ca9462 100644
> --- a/include/linux/pci.h
> +++ b/include/linux/pci.h
> @@ -511,7 +511,6 @@ struct pci_dev {
>  	unsigned int	rom_bar_overlap:1;	/* ROM BAR
> disable broken */ unsigned int	rom_attr_enabled:1;	/*
> Display of ROM attribute enabled? */ unsigned int
> non_mappable_bars:1;	/* BARs can't be mapped to user-space  */
> -	unsigned int	liveupdate_inherit_buses:1; /* Inherit
> bus numbers due to Live Update */ pci_dev_flags_t dev_flags;
>  	atomic_t	enable_cnt;	/* pci_enable_device has
> been called */ 


^ permalink raw reply

* Re: [PATCH v2 11/11] docs: smb: document SMB3 over QUIC setup for cifs.ko and ksmbd.ko
From: Namjae Jeon @ 2026-04-29 22:26 UTC (permalink / raw)
  To: Henrique Carvalho
  Cc: corbet, linux-doc, linux-cifs, sfrench, metze, ematsumiya
In-Reply-To: <20260428160020.226512-1-henrique.carvalho@suse.com>

On Wed, Apr 29, 2026 at 1:00 AM Henrique Carvalho
<henrique.carvalho@suse.com> wrote:
>
> Add quic.rst covering setup for SMB over QUIC between the kernel SMB
> server (ksmbd.ko) and client (cifs.ko).
>
> Update index.rst to include quic.rst in the SMB documentation tree.
>
> Update ksmbd.rst feature table: SMB3.1.1 over QUIC is now Experimental
> (previously listed as Planned for future).
>
> Signed-off-by: Henrique Carvalho <henrique.carvalho@suse.com>
Can you add to this document how to test with Windows clients?
Thanks.

^ permalink raw reply

* [PATCH] Documentation/scheduler: Fix duplicated word in sched-deadline
From: Miles Krause @ 2026-04-29 22:24 UTC (permalink / raw)
  To: Jonathan Corbet; +Cc: Juri Lelli, linux-doc, linux-kernel, Miles Krause

The SCHED_DEADLINE documentation has a duplicated the in the CPU 
affinity section.

Remove the extra word.

Signed-off-by: Miles Krause <mileskrause5200@gmail.com>
---
 Documentation/scheduler/sched-deadline.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/scheduler/sched-deadline.rst b/Documentation/scheduler/sched-deadline.rst
index 3ad93cd7b59a..9019b66f6a5b 100644
--- a/Documentation/scheduler/sched-deadline.rst
+++ b/Documentation/scheduler/sched-deadline.rst
@@ -685,7 +685,7 @@ Deadline Task Scheduling
 
  Deadline tasks cannot have a cpu affinity mask smaller than the root domain they
  are created on. So, using ``sched_setaffinity(2)`` won't work. Instead, the
- the deadline task should be created in a restricted root domain. This can be
+ deadline task should be created in a restricted root domain. This can be
  done using the cpuset controller of either cgroup v1 (deprecated) or cgroup v2.
  See :ref:`Documentation/admin-guide/cgroup-v1/cpusets.rst <cpusets>` and
  :ref:`Documentation/admin-guide/cgroup-v2.rst <cgroup-v2>` for more information.
-- 
2.54.0.windows.1


^ permalink raw reply related

* Re: [PATCH] kbuild: document generation of offset header files
From: Nathan Chancellor @ 2026-04-29 22:23 UTC (permalink / raw)
  To: Nathan Chancellor, Nicolas Schier, Jonathan Corbet, linux-kbuild,
	linux-doc, Piyush Patle
  Cc: Shuah Khan, Mark Rutland, Chen Pei, Randy Dunlap, Arnd Bergmann,
	Masahiro Yamada, linux-kernel
In-Reply-To: <20260410221257.191517-1-piyushpatle228@gmail.com>

On Sat, 11 Apr 2026 03:42:54 +0530, Piyush Patle wrote:
> Replace the placeholder reference with a description of how Kbuild
> generates offset header files such as include/generated/asm-offsets.h.
> 
> Remove the corresponding TODO entry now that this is documented.

Applied to

  https://git.kernel.org/pub/scm/linux/kernel/git/kbuild/linux.git kbuild-next-unstable

Thanks!

[1/1] kbuild: document generation of offset header files
      https://git.kernel.org/kbuild/c/a0d7be4ab3ece

Please look out for regression or issue reports or other follow up
comments, as they may result in the patch/series getting dropped or
reverted. Patches applied to an "unstable" branch are accepted pending
wider testing in -next and any post-commit review; they will generally
be moved to the main branch in a week if no issues are found.

Best regards,
-- 
Cheers,
Nathan



^ permalink raw reply

* Re: [PATCH] kbuild: document generation of offset header files
From: Nathan Chancellor @ 2026-04-29 22:16 UTC (permalink / raw)
  To: Piyush Patle
  Cc: Nathan Chancellor, Nicolas Schier, Jonathan Corbet, linux-kbuild,
	linux-doc, Shuah Khan, Mark Rutland, Chen Pei, Randy Dunlap,
	Arnd Bergmann, Masahiro Yamada, linux-kernel
In-Reply-To: <20260410221257.191517-1-piyushpatle228@gmail.com>

On Sat, 11 Apr 2026 03:42:54 +0530, Piyush Patle <piyushpatle228@gmail.com> wrote:

Hi Piyush,

> Replace the placeholder reference with a description of how Kbuild
> generates offset header files such as include/generated/asm-offsets.h.
> 
> Remove the corresponding TODO entry now that this is documented.
> 
> Signed-off-by: Piyush Patle <piyushpatle228@gmail.com>

Thanks, this seems generally good to me. I had wondered if it was worth
adding a "lay person's explanation" of the example but I think it is
fairly easy to understand.

>
>
> diff --git a/Documentation/kbuild/makefiles.rst b/Documentation/kbuild/makefiles.rst
> index 24a4708d26e8..7521cae7d56f 100644
> --- a/Documentation/kbuild/makefiles.rst
> +++ b/Documentation/kbuild/makefiles.rst
> @@ -1690,9 +1721,3 @@ Credits
>  - Updates by Kai Germaschewski <kai@tp1.ruhr-uni-bochum.de>
>  - Updates by Sam Ravnborg <sam@ravnborg.org>
>  - Language QA by Jan Engelhardt <jengelh@gmx.de>
> -
> -TODO
> -====
> -
> -- Generating offset header files.
> -- Add more variables to chapters 7 or 9?

Sashiko notes that you also removed the TODO around variables:

  https://sashiko.dev/#/message/20260410221257.191517-1-piyushpatle228@gmail.com

I don't think it is a big enough deal to warrant a respin but just
something to be aware of for the future.

-- 
Cheers,
Nathan


^ permalink raw reply

* Re: [PATCH v13 3/4] gpio: rpmsg: add generic rpmsg GPIO driver
From: Shenwei Wang @ 2026-04-29 19:26 UTC (permalink / raw)
  To: Padhi, Beleswar, Linus Walleij, Bartosz Golaszewski,
	Jonathan Corbet, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Mathieu Poirier, Frank Li, Sascha Hauer
  Cc: Shuah Khan, linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org,
	linux-kernel@vger.kernel.org, Pengutronix Kernel Team,
	Fabio Estevam, Peng Fan, devicetree@vger.kernel.org,
	linux-remoteproc@vger.kernel.org, imx@lists.linux.dev,
	linux-arm-kernel@lists.infradead.org, dl-linux-imx,
	Bartosz Golaszewski, Andrew Lunn
In-Reply-To: <a067452a-9a8d-45ea-8bef-b44f851da7b2@ti.com>



> -----Original Message-----
> From: Padhi, Beleswar <b-padhi@ti.com>
> Sent: Wednesday, April 29, 2026 9:36 AM
> To: Shenwei Wang <shenwei.wang@nxp.com>; Linus Walleij
> <linusw@kernel.org>; Bartosz Golaszewski <brgl@kernel.org>; Jonathan Corbet
> <corbet@lwn.net>; Rob Herring <robh@kernel.org>; Krzysztof Kozlowski
> <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>; Bjorn Andersson
> <andersson@kernel.org>; Mathieu Poirier <mathieu.poirier@linaro.org>; Frank Li
> <frank.li@nxp.com>; Sascha Hauer <s.hauer@pengutronix.de>
> Cc: Shuah Khan <skhan@linuxfoundation.org>; linux-gpio@vger.kernel.org; linux-
> doc@vger.kernel.org; linux-kernel@vger.kernel.org; Pengutronix Kernel Team
> <kernel@pengutronix.de>; Fabio Estevam <festevam@gmail.com>; Peng Fan
> <peng.fan@nxp.com>; devicetree@vger.kernel.org; linux-
> remoteproc@vger.kernel.org; imx@lists.linux.dev; linux-arm-
> kernel@lists.infradead.org; dl-linux-imx <linux-imx@nxp.com>; Bartosz
> Golaszewski <brgl@bgdev.pl>; Andrew Lunn <andrew@lunn.ch>
> Subject: [EXT] Re: [PATCH v13 3/4] gpio: rpmsg: add generic rpmsg GPIO driver
> On 4/28/2026 10:06 PM, Shenwei Wang wrote:
> >
> >> -----Original Message-----
> >> From: Padhi, Beleswar <b-padhi@ti.com>
> >> Sent: Tuesday, April 28, 2026 10:53 AM
> >> To: Shenwei Wang <shenwei.wang@nxp.com>; Linus Walleij
> >> <linusw@kernel.org>; Bartosz Golaszewski <brgl@kernel.org>; Jonathan
> >> Corbet <corbet@lwn.net>; Rob Herring <robh@kernel.org>; Krzysztof
> >> Kozlowski <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>;
> >> Bjorn Andersson <andersson@kernel.org>; Mathieu Poirier
> >> <mathieu.poirier@linaro.org>; Frank Li <frank.li@nxp.com>; Sascha
> >> Hauer <s.hauer@pengutronix.de>
> >> Cc: Shuah Khan <skhan@linuxfoundation.org>;
> >> linux-gpio@vger.kernel.org; linux- doc@vger.kernel.org;
> >> linux-kernel@vger.kernel.org; Pengutronix Kernel Team
> >> <kernel@pengutronix.de>; Fabio Estevam <festevam@gmail.com>; Peng Fan
> >> <peng.fan@nxp.com>; devicetree@vger.kernel.org; linux-
> >> remoteproc@vger.kernel.org; imx@lists.linux.dev; linux-arm-
> >> kernel@lists.infradead.org; dl-linux-imx <linux-imx@nxp.com>; Bartosz
> >> Golaszewski <brgl@bgdev.pl>; Andrew Lunn <andrew@lunn.ch>
> >> Subject: [EXT] Re: [PATCH v13 3/4] gpio: rpmsg: add generic rpmsg
> >> GPIO driver
> >>>> Nothing extra in my opinion. rpmsg_create_ept() just creates a
> >>>> dynamic local endpoint address for Linux's usage. The firmware just
> >>>> has to make sure to reply to the same endpoint address where it
> >>>> received the message. This should already be in place IMO, because
> >>>> currently you are sending all messages in the default
> >>> Since rpmsg_create_ept creates a new local endpoint address on the
> >>> Linux side, how is the remote system expected to learn and use this
> >>> new address for communication if no additional logic is added on the
> >>> remote
> >> side?
> >>
> >>
> >> Remote side learns the endpoint when it receives any message from
> >> Linux from the dynamic endpoint.
> >>
> >> Lets say rpmsg_create_ept() allocates a dynamic local ept of 1026.
> >> When you send the message from this endpoint, the standard rpmsg header
> would have:
> >>
> >>       85 struct rpmsg_hdr {
> >>       86         __rpmsg32 src; // 1026
> >>       87         __rpmsg32 dst; // rpdev->dst (e.g. 400)
> >>       88         __rpmsg32 reserved;
> >>       89         __rpmsg16 len;
> >>       90         __rpmsg16 flags;
> >>       91         u8 data[];
> >>       92 } __packed;
> >>
> >> Remote side tracks the dynamic endpoint by reading src = 1026. And
> >> while sending the response it fills the header as:
> >>
> >>       85 struct rpmsg_hdr {
> >>       86         __rpmsg32 src; // 400
> >>       87         __rpmsg32 dst; // 1026
> >>       88         __rpmsg32 reserved;
> >>       89         __rpmsg16 len;
> >>       90         __rpmsg16 flags;
> >>       91         u8 data[];
> >>       92 } __packed;
> >>
> > This explains how reply messages work in this scenario: the remote
> > side can simply send the response back to the source address of the incoming
> message.
> >
> > How does this work for notification messages initiated by the remote
> > side? Should the remote system need to add additional logic to track the source
> address based on the GPIO instance?
> 
> 
> You should already have the tracking logic in firmware. How else are you sending
> the notification messages from firmware with your current v13 implementation?
> Are you assuming the channel address to be always the same? If so, this is a bug
> and should be fixed in firmware because the address is generated dynamically.
> For example, if another core announces its name service first, then the channel
> address for your core would be different and the functionality would break.
> 

In the v13 implementation, all GPIO instances share the default endpoint. As a result, the 
firmware does not need to track the port index, since the port index is embedded in each 
message.

With the new approach you proposed, we would create a separate endpoint for each GPIO 
controller. The GPIO controller information is derived from the corresponding DTS node, and 
the port index continues to be embedded in every message exchanged with the remote side, in 
accordance with the current message protocol.

However, in this model, the firmware still needs to add logic to track the mapping between the 
port index and the corresponding endpoint for notifications initiated by the remote side. Am I 
understanding this correctly?

Thanks,
Shenwei

> Instead, you should have a map of ept to port idx in the firmware side when you
> receive a message from Linux (just like we would maintain it in struct
> rpmsg_gpio_port in Linux too).
> 
> Thanks,
> Beleswar
> 
> [...]


^ permalink raw reply

* Re: [PATCH v13 3/4] gpio: rpmsg: add generic rpmsg GPIO driver
From: Mathieu Poirier @ 2026-04-29 19:20 UTC (permalink / raw)
  To: Padhi, Beleswar
  Cc: Shenwei Wang, Andrew Lunn, Linus Walleij, Bartosz Golaszewski,
	Jonathan Corbet, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Frank Li, Sascha Hauer, Shuah Khan,
	linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org,
	linux-kernel@vger.kernel.org, Pengutronix Kernel Team,
	Fabio Estevam, Peng Fan, devicetree@vger.kernel.org,
	linux-remoteproc@vger.kernel.org, imx@lists.linux.dev,
	linux-arm-kernel@lists.infradead.org, dl-linux-imx,
	Bartosz Golaszewski
In-Reply-To: <472f85bd-42c2-40c6-abfd-b76924797069@ti.com>

On Wed, 29 Apr 2026 at 12:07, Padhi, Beleswar <b-padhi@ti.com> wrote:
>
> Hi Mathieu,
>
> On 4/29/2026 11:03 PM, Mathieu Poirier wrote:
> > On Wed, 29 Apr 2026 at 10:53, Shenwei Wang <shenwei.wang@nxp.com> wrote:
> >>
> >>
> >>> -----Original Message-----
> >>> From: Mathieu Poirier <mathieu.poirier@linaro.org>
> >>> Sent: Wednesday, April 29, 2026 10:42 AM
> >>> To: Shenwei Wang <shenwei.wang@nxp.com>
> >>> Cc: Andrew Lunn <andrew@lunn.ch>; Padhi, Beleswar <b-padhi@ti.com>; Linus
> >>> Walleij <linusw@kernel.org>; Bartosz Golaszewski <brgl@kernel.org>; Jonathan
> >>> Corbet <corbet@lwn.net>; Rob Herring <robh@kernel.org>; Krzysztof Kozlowski
> >>> <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>; Bjorn Andersson
> >>> <andersson@kernel.org>; Frank Li <frank.li@nxp.com>; Sascha Hauer
> >>> <s.hauer@pengutronix.de>; Shuah Khan <skhan@linuxfoundation.org>; linux-
> >>> gpio@vger.kernel.org; linux-doc@vger.kernel.org; linux-kernel@vger.kernel.org;
> >>> Pengutronix Kernel Team <kernel@pengutronix.de>; Fabio Estevam
> >>> <festevam@gmail.com>; Peng Fan <peng.fan@nxp.com>;
> >>> devicetree@vger.kernel.org; linux-remoteproc@vger.kernel.org;
> >>> imx@lists.linux.dev; linux-arm-kernel@lists.infradead.org; dl-linux-imx <linux-
> >>> imx@nxp.com>; Bartosz Golaszewski <brgl@bgdev.pl>
> >>> Subject: [EXT] Re: [PATCH v13 3/4] gpio: rpmsg: add generic rpmsg GPIO driver
> >>> On Tue, Apr 28, 2026 at 03:24:59PM +0000, Shenwei Wang wrote:
> >>>>
> >>>>> -----Original Message-----
> >>>>> From: Andrew Lunn <andrew@lunn.ch>
> >>>>> Sent: Monday, April 27, 2026 3:49 PM
> >>>>> To: Shenwei Wang <shenwei.wang@nxp.com>
> >>>>> Cc: Padhi, Beleswar <b-padhi@ti.com>; Linus Walleij
> >>>>> <linusw@kernel.org>; Bartosz Golaszewski <brgl@kernel.org>; Jonathan
> >>>>> Corbet <corbet@lwn.net>; Rob Herring <robh@kernel.org>; Krzysztof
> >>>>> Kozlowski <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>;
> >>>>> Bjorn Andersson <andersson@kernel.org>; Mathieu Poirier
> >>>>> <mathieu.poirier@linaro.org>; Frank Li <frank.li@nxp.com>; Sascha
> >>>>> Hauer <s.hauer@pengutronix.de>; Shuah Khan
> >>>>> <skhan@linuxfoundation.org>; linux-gpio@vger.kernel.org; linux-
> >>>>> doc@vger.kernel.org; linux-kernel@vger.kernel.org; Pengutronix
> >>>>> Kernel Team <kernel@pengutronix.de>; Fabio Estevam
> >>>>> <festevam@gmail.com>; Peng Fan <peng.fan@nxp.com>;
> >>>>> devicetree@vger.kernel.org; linux- remoteproc@vger.kernel.org;
> >>>>> imx@lists.linux.dev; linux-arm- kernel@lists.infradead.org;
> >>>>> dl-linux-imx <linux-imx@nxp.com>; Bartosz Golaszewski
> >>>>> <brgl@bgdev.pl>
> >>>>> Subject: [EXT] Re: [PATCH v13 3/4] gpio: rpmsg: add generic rpmsg
> >>>>> GPIO driver
> >>>>>>> struct virtio_gpio_response {
> >>>>>>>          __u8 status;
> >>>>>>>          __u8 value;
> >>>>>>> };
> >>>>>> It is the same message format. Please see the message definition
> >>>>> (GET_DIRECTION) below:
> >>>>>
> >>>>>> +   +-----+-----+-----+-----+-----+----+
> >>>>>> +   |0x00 |0x01 |0x02 |0x03 |0x04 |0x05|
> >>>>>> +   | 1   | 2   |port |line | err | dir|
> >>>>>> +   +-----+-----+-----+-----+-----+----+
> >>>>> Sorry, but i don't see how two u8 vs six u8 are the same message format.
> >>>>>
> >>>> Some changes to the message format are necessary.
> >>>>
> >>>> Virtio uses two communication channels (virtqueues): one for requests and
> >>> replies, and a second one for events.
> >>>> In contrast, rpmsg provides only a single communication channel, so a
> >>>> type field is required to distinguish between different kinds of messages.
> >>>>
> >>>> Since rpmsg replies and events share the same message format, an additional
> >>> line is introduced to handle both cases.
> >>>> Finally, rpmsg supports multiple GPIO controllers, so a port field is added to
> >>> uniquely identify the target controller.
> >>>
> >>> I have commented on this before - RPMSG is already providing multiplexing
> >>> capability by way of endpoints.  There is no need for a port field.  One endpoint,
> >>> one GPIO controller.
> >>>
> >> You still need a way to let the remote side know which port the endpoint maps to, either
> >> by embedding the port information in the message (the current way), or by sending it
> >> separately.
> >>
> > An endpoint is created with every namespace request.  There should be
> > one namespace request for every GPIO controller, which yields a unique
> > endpoint for each controller and eliminates the need for an extra
> > field to identify them.
>
>
> Right, but this can still be done by just having one namespace request.
> We can create new endpoints bound to an existing namespace/channel by
> invoking rpmsg_create_ept(). This is what I suggested here too:
> https://lore.kernel.org/all/29485742-6e49-482e-b73d-228295daaeec@ti.com/
>

I will look at your suggestion (i.e link above) later this week or next week.

> My mental model looks like this for the complete picture:
>
> 1. namespace/channel#1 = rpmsg-io
>     a. ept1 -> gpio-controller@1
>     b. ept2 -> gpio-controller@2
>

I've asked for one endpoint per GPIO controller since the very
beginning.  I don't yet have a strong opinion on whether to use one
namespace request per GPIO controller or a single request that spins
off multiple endpoints.  I'll have to look at your link and reflect on
that.  Regardless of how we proceed on that front, multiplexing needs
to happen at the endpoint level rather than the packet level.  This is
the only way this work can move forward.

> 2. namespace/channel#2 = rpmsg-i2c
>     a. ept1 -> i2c@1
>     b. ept2 -> i2c@2
>     c. ept3 -> i2c@3
>
> etc...
>
> This way device groups are isolated with each channel/namespace, and
> instances within each device groups are also respected with specific
> endpoints.
>
> Thanks,
> Beleswar
>

^ permalink raw reply

* [v6 10/10] ipe: Add BPF program load policy enforcement via Hornet integration
From: Blaise Boscaccy @ 2026-04-29 19:14 UTC (permalink / raw)
  To: Blaise Boscaccy, Jonathan Corbet, Paul Moore, James Morris,
	Serge E. Hallyn, Mickaël Salaün, Günther Noack,
	Dr. David Alan Gilbert, Andrew Morton, James.Bottomley, dhowells,
	Fan Wu, Ryan Foster, Randy Dunlap, linux-security-module,
	linux-doc, linux-kernel, bpf, Song Liu
In-Reply-To: <20260429191431.2345448-1-bboscaccy@linux.microsoft.com>

Add support for the bpf_prog_load_post_integrity LSM hook, enabling IPE
to make policy decisions about BPF program loading based on integrity
verdicts provided by the Hornet LSM.

New policy operation:
  op=BPF_PROG_LOAD - Matches BPF program load events

New policy properties:
  bpf_signature=NONE      - No Verdict
  bpf_signature=OK        - Program signature and map hashes verified
  bpf_signature=UNSIGNED  - No signature provided
  bpf_signature=PARTIALSIG - Signature OK but no map hash data
  bpf_signature=UNKNOWNKEY - Cert not trusted
  bpf_signature=UNEXPECTED - An unexpected hash value was encountered
  bpf_signature=FAULT 	   - System error during verification
  bpf_signature=BADSIG    - Signature or map hash verification failed
  bpf_keyring=BUILTIN     - Program was signed using a builtin keyring
  bpf_keyring=SECONDARY   - Program was signed using the secondary keyring
  bpf_keyring=PLATFORM    - Program was signed using the platform keyring
  bpf_kernel=TRUE         - Program originated from kernelspace
  bpf_kernel=FALSE        - Program originated from userspace

These properties map directly to the lsm_integrity_verdict enum values
provided by the Hornet LSM through security_bpf_prog_load_post_integrity.

The feature is gated on CONFIG_IPE_PROP_BPF_SIGNATURE which depends on
CONFIG_SECURITY_HORNET.

Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
 Documentation/admin-guide/LSM/ipe.rst | 162 +++++++++++++++++++++++++-
 Documentation/security/ipe.rst        |  39 +++++++
 security/ipe/Kconfig                  |  14 +++
 security/ipe/audit.c                  |  15 +++
 security/ipe/eval.c                   |  73 +++++++++++-
 security/ipe/eval.h                   |  11 ++
 security/ipe/hooks.c                  |  63 ++++++++++
 security/ipe/hooks.h                  |  15 +++
 security/ipe/ipe.c                    |  14 +++
 security/ipe/ipe.h                    |   3 +
 security/ipe/policy.h                 |  14 +++
 security/ipe/policy_parser.c          |  27 +++++
 12 files changed, 448 insertions(+), 2 deletions(-)

diff --git a/Documentation/admin-guide/LSM/ipe.rst b/Documentation/admin-guide/LSM/ipe.rst
index a756d81585317..4dfbf0d325a8a 100644
--- a/Documentation/admin-guide/LSM/ipe.rst
+++ b/Documentation/admin-guide/LSM/ipe.rst
@@ -559,7 +559,8 @@ policy. Two properties are built-into the policy parser: 'op' and 'action'.
 The other properties are used to restrict immutable security properties
 about the files being evaluated. Currently those properties are:
 '``boot_verified``', '``dmverity_signature``', '``dmverity_roothash``',
-'``fsverity_signature``', '``fsverity_digest``'. A description of all
+'``fsverity_signature``', '``fsverity_digest``', '``bpf_signature``',
+'``bpf_keyring``', '``bpf_kernel``'. A description of all
 properties supported by IPE are listed below:
 
 op
@@ -603,6 +604,14 @@ as the first token. IPE supports the following operations:
       Controls loading IMA certificates through the Kconfigs,
       ``CONFIG_IMA_X509_PATH`` and ``CONFIG_EVM_X509_PATH``.
 
+   ``BPF_PROG_LOAD``:
+
+      Pertains to BPF programs being loaded via the ``bpf()`` syscall.
+      This operation is used in conjunction with the ``bpf_signature``,
+      ``bpf_keyring``, and ``bpf_kernel`` properties to control BPF
+      program loading based on integrity verification provided by the
+      Hornet LSM.
+
 action
 ~~~~~~
 
@@ -713,6 +722,105 @@ fsverity_signature
 
       fsverity_signature=(TRUE|FALSE)
 
+bpf_signature
+~~~~~~~~~~~~~
+
+   This property can be utilized for authorization of BPF program loads based
+   on the integrity verdict provided by the Hornet LSM. When a BPF program is
+   loaded, Hornet performs cryptographic verification of the program's PKCS#7
+   signature (if present) and passes an integrity verdict to IPE via the
+   ``security_bpf_prog_load_post_integrity`` hook. IPE can then allow or deny
+   the load based on the verdict.
+
+   This property depends on ``SECURITY_HORNET`` and is controlled by the
+   ``IPE_PROP_BPF_SIGNATURE`` config option.
+   The format of this property is::
+
+      bpf_signature=(NONE|OK|UNSIGNED|PARTIALSIG|UNKNOWNKEY|UNEXPECTED|FAULT|BADSIG)
+
+   The possible values correspond to the integrity verdicts from Hornet:
+
+      ``NONE``
+
+         No integrity verdict was set (default/uninitialized).
+
+      ``OK``
+
+         The BPF program's signature and all map hashes were successfully
+         verified.
+
+      ``UNSIGNED``
+
+         No signature was provided with the BPF program.
+
+      ``PARTIALSIG``
+
+         The program signature was verified, but no authenticated map hash
+         data was present.
+
+      ``UNKNOWNKEY``
+
+         The signing certificate is not trusted by the specified keyring.
+
+      ``UNEXPECTED``
+
+         An unexpected map hash value was encountered during verification.
+
+      ``FAULT``
+
+         A system error occurred during signature verification.
+
+      ``BADSIG``
+
+         The signature or hash verification failed.
+
+bpf_keyring
+~~~~~~~~~~~~
+
+   This property can be utilized for authorization of BPF program loads based
+   on the keyring specified in the ``bpf_attr`` during the ``BPF_PROG_LOAD``
+   syscall. This allows policies to restrict which keyring must be used for
+   signature verification of BPF programs.
+
+   This property shares the ``IPE_PROP_BPF_SIGNATURE`` config option with
+   ``bpf_signature``.
+   The format of this property is::
+
+      bpf_keyring=(BUILTIN|SECONDARY|PLATFORM)
+
+   The possible values correspond to the system keyrings:
+
+      ``BUILTIN``
+
+         The builtin trusted keyring (``.builtin_trusted_keys``), which
+         contains keys embedded at kernel compile time.
+
+      ``SECONDARY``
+
+         The secondary trusted keyring (``.secondary_trusted_keys``), which
+         includes both builtin trusted keys and keys added at runtime.
+
+      ``PLATFORM``
+
+         The platform keyring (``.platform``), which contains keys provided
+         by the platform firmware (e.g. UEFI db keys).
+
+bpf_kernel
+~~~~~~~~~~
+
+   This property can be utilized for authorization of BPF program loads based
+   on whether the load originated from kernel space or user space. The BPF
+   light skeleton infrastructure performs a secondary kernel-originated program
+   load that will not carry a signature. This property allows policies to
+   permit such kernel-originated loads while still requiring signatures for
+   user-space loads.
+
+   This property shares the ``IPE_PROP_BPF_SIGNATURE`` config option with
+   ``bpf_signature``.
+   The format of this property is::
+
+      bpf_kernel=(TRUE|FALSE)
+
 Policy Examples
 ---------------
 
@@ -788,6 +896,58 @@ Allow execution of a specific fs-verity file
 
    op=EXECUTE fsverity_digest=sha256:fd88f2b8824e197f850bf4c5109bea5cf0ee38104f710843bb72da796ba5af9e action=ALLOW
 
+Allow only signed BPF programs
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+   policy_name=Allow_Signed_BPF policy_version=0.0.0
+   DEFAULT action=ALLOW
+
+   DEFAULT op=BPF_PROG_LOAD action=DENY
+   op=BPF_PROG_LOAD bpf_kernel=TRUE action=ALLOW
+   op=BPF_PROG_LOAD bpf_signature=OK action=ALLOW
+
+This policy allows all other operations but restricts BPF program loading
+to only programs that either originate from kernel space (e.g. light skeleton
+reloads) or have a valid signature verified by the Hornet LSM. Unsigned or
+improperly signed BPF programs from user space will be denied.
+
+Allow signed BPF programs from a specific keyring
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+   policy_name=Allow_BPF_Builtin_Keyring policy_version=0.0.0
+   DEFAULT action=ALLOW
+
+   DEFAULT op=BPF_PROG_LOAD action=DENY
+   op=BPF_PROG_LOAD bpf_kernel=TRUE action=ALLOW
+   op=BPF_PROG_LOAD bpf_signature=OK bpf_keyring=BUILTIN action=ALLOW
+
+This policy further restricts BPF program loading to only accept programs
+whose signatures were verified using the builtin trusted keyring. Programs
+signed against the secondary or platform keyrings will be denied, providing
+tighter control over which signing keys are acceptable.
+
+Allow signed BPF programs with relaxed partial signatures
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+   policy_name=Allow_BPF_Partial policy_version=0.0.0
+   DEFAULT action=ALLOW
+
+   DEFAULT op=BPF_PROG_LOAD action=DENY
+   op=BPF_PROG_LOAD bpf_kernel=TRUE action=ALLOW
+   op=BPF_PROG_LOAD bpf_signature=OK action=ALLOW
+   op=BPF_PROG_LOAD bpf_signature=PARTIALSIG action=ALLOW
+
+This policy allows BPF programs that have been fully verified (``OK``) as
+well as programs with a valid program signature but without authenticated
+map hash data (``PARTIALSIG``). This can be useful during development or
+for programs that do not use maps.
+
 Additional Information
 ----------------------
 
diff --git a/Documentation/security/ipe.rst b/Documentation/security/ipe.rst
index 4a7d953abcdc3..de8fcf1dc173d 100644
--- a/Documentation/security/ipe.rst
+++ b/Documentation/security/ipe.rst
@@ -412,6 +412,44 @@ a standard securityfs policy tree::
 
 The policy is stored in the ``->i_private`` data of the MyPolicy inode.
 
+BPF/Hornet Integration
+~~~~~~~~~~~~~~~~~~~~~~
+
+IPE integrates with the Hornet LSM to enforce integrity policies on BPF
+program loading. Hornet performs cryptographic verification of BPF program
+signatures (PKCS#7 with authenticated attributes containing map hashes) and
+provides an integrity verdict to IPE via the
+``security_bpf_prog_load_post_integrity`` hook.
+
+The hook flow is:
+
+  1. User space invokes ``BPF_PROG_LOAD`` via the ``bpf()`` syscall.
+  2. Hornet's ``bpf_prog_load_integrity`` hook calls ``hornet_check_program()``
+     to verify the program's signature and map hashes.
+  3. Hornet calls ``security_bpf_prog_load_post_integrity()`` with the
+     resulting ``lsm_integrity_verdict``.
+  4. IPE evaluates the verdict against the active policy's ``BPF_PROG_LOAD``
+     rules and returns ``-EACCES`` if denied.
+
+Three properties are available for BPF policy rules:
+
+  - ``bpf_signature``: Matches against the integrity verdict (OK, UNSIGNED,
+    BADSIG, etc.)
+  - ``bpf_keyring``: Matches against the keyring specified in ``bpf_attr``
+    (BUILTIN, SECONDARY, PLATFORM)
+  - ``bpf_kernel``: Matches whether the load originated from kernel space
+    (TRUE/FALSE). This is important because the BPF light skeleton
+    infrastructure performs a secondary kernel-originated program load that
+    does not carry a signature.
+
+All three properties are gated on ``CONFIG_IPE_PROP_BPF_SIGNATURE`` which
+depends on ``CONFIG_SECURITY_HORNET``.
+
+The evaluation context (``struct ipe_eval_ctx``) carries three BPF-specific
+fields: ``bpf_verdict`` (the integrity verdict enum), ``bpf_keyring_id``
+(the ``s32`` keyring ID from ``bpf_attr``), and ``bpf_kernel`` (bool
+indicating kernel origin).
+
 Tests
 -----
 
@@ -439,6 +477,7 @@ IPE has KUnit Tests for the policy parser. Recommended kunitconfig::
   CONFIG_IPE_PROP_DM_VERITY_SIGNATURE=y
   CONFIG_IPE_PROP_FS_VERITY=y
   CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG=y
+  CONFIG_IPE_PROP_BPF_SIGNATURE=y
   CONFIG_SECURITY_IPE_KUNIT_TEST=y
 
 In addition, IPE has a python based integration
diff --git a/security/ipe/Kconfig b/security/ipe/Kconfig
index a110a6cd848b7..4c1d46847582b 100644
--- a/security/ipe/Kconfig
+++ b/security/ipe/Kconfig
@@ -95,6 +95,20 @@ config IPE_PROP_FS_VERITY_BUILTIN_SIG
 
 	  if unsure, answer Y.
 
+config IPE_PROP_BPF_SIGNATURE
+	bool "Enable support for Hornet BPF program signature verification"
+	depends on SECURITY_HORNET
+	help
+	  This option enables the 'bpf_signature' and 'bpf_keyring'
+	  properties within IPE policies. The 'bpf_signature' property
+	  allows IPE to make policy decisions based on the integrity
+	  verdict provided by the Hornet LSM when a BPF program is loaded.
+	  Verdicts include OK, UNSIGNED, PARTIALSIG, BADSIG, and others.
+	  The 'bpf_keyring' property allows policies to match against the
+	  keyring specified in bpf_attr (BUILTIN, SECONDARY, PLATFORM).
+
+	  If unsure, answer Y.
+
 endmenu
 
 config SECURITY_IPE_KUNIT_TEST
diff --git a/security/ipe/audit.c b/security/ipe/audit.c
index 3f0deeb549127..251c6ec2f8423 100644
--- a/security/ipe/audit.c
+++ b/security/ipe/audit.c
@@ -41,6 +41,7 @@ static const char *const audit_op_names[__IPE_OP_MAX + 1] = {
 	"KEXEC_INITRAMFS",
 	"POLICY",
 	"X509_CERT",
+	"BPF_PROG_LOAD",
 	"UNKNOWN",
 };
 
@@ -51,6 +52,7 @@ static const char *const audit_hook_names[__IPE_HOOK_MAX] = {
 	"MPROTECT",
 	"KERNEL_READ",
 	"KERNEL_LOAD",
+	"BPF_PROG_LOAD",
 };
 
 static const char *const audit_prop_names[__IPE_PROP_MAX] = {
@@ -62,6 +64,19 @@ static const char *const audit_prop_names[__IPE_PROP_MAX] = {
 	"fsverity_digest=",
 	"fsverity_signature=FALSE",
 	"fsverity_signature=TRUE",
+	"bpf_signature=NONE",
+	"bpf_signature=OK",
+	"bpf_signature=UNSIGNED",
+	"bpf_signature=PARTIALSIG",
+	"bpf_signature=UNKNOWNKEY",
+	"bpf_signature=UNEXPECTED",
+	"bpf_signature=FAULT",
+	"bpf_signature=BADSIG",
+	"bpf_keyring=BUILTIN",
+	"bpf_keyring=SECONDARY",
+	"bpf_keyring=PLATFORM",
+	"bpf_kernel=FALSE",
+	"bpf_kernel=TRUE",
 };
 
 /**
diff --git a/security/ipe/eval.c b/security/ipe/eval.c
index 21439c5be3364..9a6d583fea125 100644
--- a/security/ipe/eval.c
+++ b/security/ipe/eval.c
@@ -11,6 +11,7 @@
 #include <linux/rcupdate.h>
 #include <linux/moduleparam.h>
 #include <linux/fsverity.h>
+#include <linux/verification.h>
 
 #include "ipe.h"
 #include "eval.h"
@@ -265,8 +266,52 @@ static bool evaluate_fsv_sig_true(const struct ipe_eval_ctx *const ctx)
 }
 #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
 
+#ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
+/**
+ * evaluate_bpf_sig() - Evaluate @ctx against a bpf_signature property.
+ * @ctx: Supplies a pointer to the context being evaluated.
+ * @expected: The expected lsm_integrity_verdict to match against.
+ *
+ * Return:
+ * * %true	- The current @ctx matches the expected verdict
+ * * %false	- The current @ctx doesn't match the expected verdict
+ */
+static bool evaluate_bpf_sig(const struct ipe_eval_ctx *const ctx,
+			     enum lsm_integrity_verdict expected)
+{
+	return ctx->bpf_verdict == expected;
+}
+#else
+static bool evaluate_bpf_sig(const struct ipe_eval_ctx *const ctx,
+			     enum lsm_integrity_verdict expected)
+{
+	return false;
+}
+#endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
+
+#ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
+/**
+ * evaluate_bpf_keyring() - Evaluate @ctx against a bpf_keyring property.
+ * @ctx: Supplies a pointer to the context being evaluated.
+ * @expected: The expected keyring_id to match against.
+ *
+ * Return:
+ * * %true	- The current @ctx matches the expected keyring
+ * * %false	- The current @ctx doesn't match the expected keyring
+ */
+static bool evaluate_bpf_keyring(const struct ipe_eval_ctx *const ctx,
+				 s32 expected)
+{
+	return ctx->bpf_keyring_id == expected;
+}
+#else
+static bool evaluate_bpf_keyring(const struct ipe_eval_ctx *const ctx,
+				 s32 expected)
+{
+	return false;
+}
+#endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
 /**
- * evaluate_property() - Analyze @ctx against a rule property.
  * @ctx: Supplies a pointer to the context to be evaluated.
  * @p: Supplies a pointer to the property to be evaluated.
  *
@@ -297,6 +342,32 @@ static bool evaluate_property(const struct ipe_eval_ctx *const ctx,
 		return evaluate_fsv_sig_false(ctx);
 	case IPE_PROP_FSV_SIG_TRUE:
 		return evaluate_fsv_sig_true(ctx);
+	case IPE_PROP_BPF_SIG_NONE:
+		return evaluate_bpf_sig(ctx, LSM_INT_VERDICT_NONE);
+	case IPE_PROP_BPF_SIG_OK:
+		return evaluate_bpf_sig(ctx, LSM_INT_VERDICT_OK);
+	case IPE_PROP_BPF_SIG_UNSIGNED:
+		return evaluate_bpf_sig(ctx, LSM_INT_VERDICT_UNSIGNED);
+	case IPE_PROP_BPF_SIG_PARTIALSIG:
+		return evaluate_bpf_sig(ctx, LSM_INT_VERDICT_PARTIALSIG);
+	case IPE_PROP_BPF_SIG_UNKNOWNKEY:
+		return evaluate_bpf_sig(ctx, LSM_INT_VERDICT_UNKNOWNKEY);
+	case IPE_PROP_BPF_SIG_UNEXPECTED:
+		return evaluate_bpf_sig(ctx, LSM_INT_VERDICT_UNEXPECTED);
+	case IPE_PROP_BPF_SIG_FAULT:
+		return evaluate_bpf_sig(ctx, LSM_INT_VERDICT_FAULT);
+	case IPE_PROP_BPF_SIG_BADSIG:
+		return evaluate_bpf_sig(ctx, LSM_INT_VERDICT_BADSIG);
+	case IPE_PROP_BPF_KEYRING_BUILTIN:
+		return evaluate_bpf_keyring(ctx, 0);
+	case IPE_PROP_BPF_KEYRING_SECONDARY:
+		return evaluate_bpf_keyring(ctx, (s32)(unsigned long)VERIFY_USE_SECONDARY_KEYRING);
+	case IPE_PROP_BPF_KEYRING_PLATFORM:
+		return evaluate_bpf_keyring(ctx, (s32)(unsigned long)VERIFY_USE_PLATFORM_KEYRING);
+	case IPE_PROP_BPF_KERNEL_FALSE:
+		return !ctx->bpf_kernel;
+	case IPE_PROP_BPF_KERNEL_TRUE:
+		return ctx->bpf_kernel;
 	default:
 		return false;
 	}
diff --git a/security/ipe/eval.h b/security/ipe/eval.h
index fef65a36468cb..b061cb5ade27e 100644
--- a/security/ipe/eval.h
+++ b/security/ipe/eval.h
@@ -37,6 +37,12 @@ struct ipe_inode {
 };
 #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
 
+#ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
+struct ipe_bpf_prog {
+	enum lsm_integrity_verdict verdict;
+};
+#endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
+
 struct ipe_eval_ctx {
 	enum ipe_op_type op;
 	enum ipe_hook_type hook;
@@ -52,6 +58,11 @@ struct ipe_eval_ctx {
 #ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG
 	const struct ipe_inode *ipe_inode;
 #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
+#ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
+	enum lsm_integrity_verdict bpf_verdict;
+	s32 bpf_keyring_id;
+	bool bpf_kernel;
+#endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
 };
 
 enum ipe_match {
diff --git a/security/ipe/hooks.c b/security/ipe/hooks.c
index 0ae54a880405a..9271e129a2cf2 100644
--- a/security/ipe/hooks.c
+++ b/security/ipe/hooks.c
@@ -340,3 +340,66 @@ int ipe_inode_setintegrity(const struct inode *inode,
 	return -EINVAL;
 }
 #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
+
+#ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
+/**
+ * ipe_bpf_prog_load_post_integrity() - Store integrity verdict in per-prog blob.
+ * @prog: Supplies the BPF program being loaded.
+ * @attr: Supplies the bpf syscall attributes.
+ * @token: Supplies the BPF token, if any.
+ * @kernel: Whether the call originated from the kernel.
+ * @lsmid: Supplies the LSM ID of the integrity provider.
+ * @verdict: Supplies the integrity verdict from the provider (e.g. Hornet).
+ *
+ * This hook stores the integrity verdict in IPE's per-prog security blob
+ * so that ipe_bpf_prog_load() can later read it for policy evaluation.
+ *
+ * Return:
+ * * %0		- Always succeeds (policy is evaluated in bpf_prog_load)
+ */
+int ipe_bpf_prog_load_post_integrity(struct bpf_prog *prog,
+				     union bpf_attr *attr,
+				     struct bpf_token *token,
+				     bool kernel,
+				     const struct lsm_id *lsmid,
+				     enum lsm_integrity_verdict verdict)
+{
+	struct ipe_bpf_prog *blob = ipe_bpf_prog(prog);
+
+	blob->verdict = verdict;
+
+	return 0;
+}
+
+/**
+ * ipe_bpf_prog_load() - IPE policy evaluation for BPF program load.
+ * @prog: Supplies the BPF program being loaded.
+ * @attr: Supplies the bpf syscall attributes.
+ * @token: Supplies the BPF token, if any.
+ * @kernel: Whether the call originated from the kernel.
+ *
+ * Reads the integrity verdict previously stored by post_integrity (if any)
+ * and evaluates IPE policy. If no integrity provider ran, the verdict
+ * defaults to LSM_INT_VERDICT_NONE.
+ *
+ * Return:
+ * * %0		- Success
+ * * %-EACCES	- Did not pass IPE policy
+ */
+int ipe_bpf_prog_load(struct bpf_prog *prog,
+		      union bpf_attr *attr,
+		      struct bpf_token *token,
+		      bool kernel)
+{
+	struct ipe_bpf_prog *blob = ipe_bpf_prog(prog);
+	struct ipe_eval_ctx ctx = IPE_EVAL_CTX_INIT;
+
+	ctx.op = IPE_OP_BPF_PROG_LOAD;
+	ctx.hook = IPE_HOOK_BPF_PROG_LOAD;
+	ctx.bpf_verdict = blob->verdict;
+	ctx.bpf_keyring_id = attr->keyring_id;
+	ctx.bpf_kernel = kernel;
+
+	return ipe_evaluate_event(&ctx);
+}
+#endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
diff --git a/security/ipe/hooks.h b/security/ipe/hooks.h
index 07db373327402..8a6d1a459e00c 100644
--- a/security/ipe/hooks.h
+++ b/security/ipe/hooks.h
@@ -10,6 +10,7 @@
 #include <linux/security.h>
 #include <linux/blk_types.h>
 #include <linux/fsverity.h>
+#include <linux/bpf.h>
 
 enum ipe_hook_type {
 	IPE_HOOK_BPRM_CHECK = 0,
@@ -18,6 +19,7 @@ enum ipe_hook_type {
 	IPE_HOOK_MPROTECT,
 	IPE_HOOK_KERNEL_READ,
 	IPE_HOOK_KERNEL_LOAD,
+	IPE_HOOK_BPF_PROG_LOAD,
 	__IPE_HOOK_MAX
 };
 
@@ -52,4 +54,17 @@ int ipe_inode_setintegrity(const struct inode *inode, enum lsm_integrity_type ty
 			   const void *value, size_t size);
 #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
 
+#ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
+int ipe_bpf_prog_load_post_integrity(struct bpf_prog *prog,
+				     union bpf_attr *attr,
+				     struct bpf_token *token,
+				     bool kernel,
+				     const struct lsm_id *lsmid,
+				     enum lsm_integrity_verdict verdict);
+int ipe_bpf_prog_load(struct bpf_prog *prog,
+		      union bpf_attr *attr,
+		      struct bpf_token *token,
+		      bool kernel);
+#endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
+
 #endif /* _IPE_HOOKS_H */
diff --git a/security/ipe/ipe.c b/security/ipe/ipe.c
index 495bb765de1b8..5af13903287fe 100644
--- a/security/ipe/ipe.c
+++ b/security/ipe/ipe.c
@@ -19,6 +19,9 @@ static struct lsm_blob_sizes ipe_blobs __ro_after_init = {
 #ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG
 	.lbs_inode = sizeof(struct ipe_inode),
 #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
+#ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
+	.lbs_bpf_prog = sizeof(struct ipe_bpf_prog),
+#endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
 };
 
 static const struct lsm_id ipe_lsmid = {
@@ -45,6 +48,13 @@ struct ipe_inode *ipe_inode(const struct inode *inode)
 }
 #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
 
+#ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
+struct ipe_bpf_prog *ipe_bpf_prog(const struct bpf_prog *prog)
+{
+	return prog->aux->security + ipe_blobs.lbs_bpf_prog;
+}
+#endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
+
 static struct security_hook_list ipe_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(bprm_check_security, ipe_bprm_check_security),
 	LSM_HOOK_INIT(bprm_creds_for_exec, ipe_bprm_creds_for_exec),
@@ -60,6 +70,10 @@ static struct security_hook_list ipe_hooks[] __ro_after_init = {
 #ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG
 	LSM_HOOK_INIT(inode_setintegrity, ipe_inode_setintegrity),
 #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
+#ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
+	LSM_HOOK_INIT(bpf_prog_load_post_integrity, ipe_bpf_prog_load_post_integrity),
+	LSM_HOOK_INIT(bpf_prog_load, ipe_bpf_prog_load),
+#endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
 };
 
 /**
diff --git a/security/ipe/ipe.h b/security/ipe/ipe.h
index 25cfdb8f0c20a..47de32b5bc938 100644
--- a/security/ipe/ipe.h
+++ b/security/ipe/ipe.h
@@ -22,6 +22,9 @@ struct ipe_bdev *ipe_bdev(struct block_device *b);
 #ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG
 struct ipe_inode *ipe_inode(const struct inode *inode);
 #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
+#ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
+struct ipe_bpf_prog *ipe_bpf_prog(const struct bpf_prog *prog);
+#endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
 
 int ipe_init_securityfs(void);
 
diff --git a/security/ipe/policy.h b/security/ipe/policy.h
index 5bfbdbddeef86..748bea92beb19 100644
--- a/security/ipe/policy.h
+++ b/security/ipe/policy.h
@@ -17,6 +17,7 @@ enum ipe_op_type {
 	IPE_OP_KEXEC_INITRAMFS,
 	IPE_OP_POLICY,
 	IPE_OP_X509,
+	IPE_OP_BPF_PROG_LOAD,
 	__IPE_OP_MAX,
 };
 
@@ -39,6 +40,19 @@ enum ipe_prop_type {
 	IPE_PROP_FSV_DIGEST,
 	IPE_PROP_FSV_SIG_FALSE,
 	IPE_PROP_FSV_SIG_TRUE,
+	IPE_PROP_BPF_SIG_NONE,
+	IPE_PROP_BPF_SIG_OK,
+	IPE_PROP_BPF_SIG_UNSIGNED,
+	IPE_PROP_BPF_SIG_PARTIALSIG,
+	IPE_PROP_BPF_SIG_UNKNOWNKEY,
+	IPE_PROP_BPF_SIG_UNEXPECTED,
+	IPE_PROP_BPF_SIG_FAULT,
+	IPE_PROP_BPF_SIG_BADSIG,
+	IPE_PROP_BPF_KEYRING_BUILTIN,
+	IPE_PROP_BPF_KEYRING_SECONDARY,
+	IPE_PROP_BPF_KEYRING_PLATFORM,
+	IPE_PROP_BPF_KERNEL_FALSE,
+	IPE_PROP_BPF_KERNEL_TRUE,
 	__IPE_PROP_MAX
 };
 
diff --git a/security/ipe/policy_parser.c b/security/ipe/policy_parser.c
index 6fa5bebf84714..71f63de56616b 100644
--- a/security/ipe/policy_parser.c
+++ b/security/ipe/policy_parser.c
@@ -237,6 +237,7 @@ static const match_table_t operation_tokens = {
 	{IPE_OP_KEXEC_INITRAMFS,	"op=KEXEC_INITRAMFS"},
 	{IPE_OP_POLICY,			"op=POLICY"},
 	{IPE_OP_X509,			"op=X509_CERT"},
+	{IPE_OP_BPF_PROG_LOAD,		"op=BPF_PROG_LOAD"},
 	{IPE_OP_INVALID,		NULL}
 };
 
@@ -281,6 +282,19 @@ static const match_table_t property_tokens = {
 	{IPE_PROP_FSV_DIGEST,		"fsverity_digest=%s"},
 	{IPE_PROP_FSV_SIG_FALSE,	"fsverity_signature=FALSE"},
 	{IPE_PROP_FSV_SIG_TRUE,		"fsverity_signature=TRUE"},
+	{IPE_PROP_BPF_SIG_NONE,		"bpf_signature=NONE"},
+	{IPE_PROP_BPF_SIG_OK,		"bpf_signature=OK"},
+	{IPE_PROP_BPF_SIG_UNSIGNED,	"bpf_signature=UNSIGNED"},
+	{IPE_PROP_BPF_SIG_PARTIALSIG,	"bpf_signature=PARTIALSIG"},
+	{IPE_PROP_BPF_SIG_UNKNOWNKEY,	"bpf_signature=UNKNOWNKEY"},
+	{IPE_PROP_BPF_SIG_UNEXPECTED,	"bpf_signature=UNEXPECTED"},
+	{IPE_PROP_BPF_SIG_FAULT,	"bpf_signature=FAULT"},
+	{IPE_PROP_BPF_SIG_BADSIG,	"bpf_signature=BADSIG"},
+	{IPE_PROP_BPF_KEYRING_BUILTIN,	"bpf_keyring=BUILTIN"},
+	{IPE_PROP_BPF_KEYRING_SECONDARY,	"bpf_keyring=SECONDARY"},
+	{IPE_PROP_BPF_KEYRING_PLATFORM,	"bpf_keyring=PLATFORM"},
+	{IPE_PROP_BPF_KERNEL_FALSE,	"bpf_kernel=FALSE"},
+	{IPE_PROP_BPF_KERNEL_TRUE,	"bpf_kernel=TRUE"},
 	{IPE_PROP_INVALID,		NULL}
 };
 
@@ -331,6 +345,19 @@ static int parse_property(char *t, struct ipe_rule *r)
 	case IPE_PROP_DMV_SIG_TRUE:
 	case IPE_PROP_FSV_SIG_FALSE:
 	case IPE_PROP_FSV_SIG_TRUE:
+	case IPE_PROP_BPF_SIG_NONE:
+	case IPE_PROP_BPF_SIG_OK:
+	case IPE_PROP_BPF_SIG_UNSIGNED:
+	case IPE_PROP_BPF_SIG_PARTIALSIG:
+	case IPE_PROP_BPF_SIG_UNKNOWNKEY:
+	case IPE_PROP_BPF_SIG_UNEXPECTED:
+	case IPE_PROP_BPF_SIG_FAULT:
+	case IPE_PROP_BPF_SIG_BADSIG:
+	case IPE_PROP_BPF_KEYRING_BUILTIN:
+	case IPE_PROP_BPF_KEYRING_SECONDARY:
+	case IPE_PROP_BPF_KEYRING_PLATFORM:
+	case IPE_PROP_BPF_KERNEL_FALSE:
+	case IPE_PROP_BPF_KERNEL_TRUE:
 		p->type = token;
 		break;
 	default:
-- 
2.53.0


^ permalink raw reply related

* [v6 09/10] selftests/hornet: Add a selftest for the Hornet LSM
From: Blaise Boscaccy @ 2026-04-29 19:14 UTC (permalink / raw)
  To: Blaise Boscaccy, Jonathan Corbet, Paul Moore, James Morris,
	Serge E. Hallyn, Mickaël Salaün, Günther Noack,
	Dr. David Alan Gilbert, Andrew Morton, James.Bottomley, dhowells,
	Fan Wu, Ryan Foster, Randy Dunlap, linux-security-module,
	linux-doc, linux-kernel, bpf, Song Liu
In-Reply-To: <20260429191431.2345448-1-bboscaccy@linux.microsoft.com>

This selftest contains a testcase that utilizes light skeleton eBPF
loaders and exercises hornet's map validation.

Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
 tools/testing/selftests/Makefile             |  1 +
 tools/testing/selftests/hornet/Makefile      | 63 ++++++++++++++++++++
 tools/testing/selftests/hornet/loader.c      | 21 +++++++
 tools/testing/selftests/hornet/trivial.bpf.c | 33 ++++++++++
 4 files changed, 118 insertions(+)
 create mode 100644 tools/testing/selftests/hornet/Makefile
 create mode 100644 tools/testing/selftests/hornet/loader.c
 create mode 100644 tools/testing/selftests/hornet/trivial.bpf.c

diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 450f13ba4cca9..4e2d1cd88c825 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -44,6 +44,7 @@ TARGETS += ftrace
 TARGETS += futex
 TARGETS += gpio
 TARGETS += hid
+TARGETS += hornet
 TARGETS += intel_pstate
 TARGETS += iommu
 TARGETS += ipc
diff --git a/tools/testing/selftests/hornet/Makefile b/tools/testing/selftests/hornet/Makefile
new file mode 100644
index 0000000000000..432bce59f54e7
--- /dev/null
+++ b/tools/testing/selftests/hornet/Makefile
@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: GPL-2.0
+include ../../../build/Build.include
+include ../../../scripts/Makefile.arch
+include ../../../scripts/Makefile.include
+
+CLANG ?= clang
+CFLAGS := -g -O2 -Wall
+BPFTOOL ?= $(TOOLSDIR)/bpf/bpftool/bpftool
+SCRIPTSDIR := $(abspath ../../../../scripts/hornet)
+TOOLSDIR := $(abspath ../../..)
+LIBDIR := $(TOOLSDIR)/lib
+BPFDIR := $(LIBDIR)/bpf
+TOOLSINCDIR := $(TOOLSDIR)/include
+APIDIR := $(TOOLSINCDIR)/uapi
+CERTDIR := $(abspath ../../../../certs)
+PKG_CONFIG ?= $(CROSS_COMPILE)pkg-config
+
+TEST_GEN_PROGS := loader
+TEST_GEN_FILES := vmlinux.h loader.h trivial.bpf.o map.bin sig.bin insn.bin signed_loader.h
+$(TEST_GEN_PROGS): LDLIBS += -lbpf
+$(TEST_GEN_PROGS): $(TEST_GEN_FILES)
+
+include ../lib.mk
+
+BPF_CFLAGS := -target bpf \
+	-D__TARGET_ARCH_$(ARCH) \
+	-I/usr/include/$(shell uname -m)-linux-gnu \
+	$(KHDR_INCLUDES)
+
+vmlinux.h:
+	$(BPFTOOL) btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
+
+trivial.bpf.o: trivial.bpf.c vmlinux.h
+	$(CLANG) $(CFLAGS) $(BPF_CFLAGS) -c $< -o $@
+
+loader.h: trivial.bpf.o
+	$(BPFTOOL) gen skeleton -S -k $(CERTDIR)/signing_key.pem -i $(CERTDIR)/signing_key.x509 \
+		-L $< name trivial > $@
+
+insn.bin: loader.h
+	$(SCRIPTSDIR)/extract-insn.sh $< > $@
+
+map.bin: loader.h
+	$(SCRIPTSDIR)/extract-map.sh $< > $@
+
+$(OUTPUT)/gen_sig: ../../../../scripts/hornet/gen_sig.c
+	$(call msg,GEN_SIG,,$@)
+	$(Q)$(CC) $(shell $(PKG_CONFIG) --cflags libcrypto 2> /dev/null) \
+		  $< -o $@ \
+		  $(shell $(PKG_CONFIG) --libs libcrypto 2> /dev/null || echo -lcrypto)
+
+sig.bin: insn.bin map.bin $(OUTPUT)/gen_sig
+	$(OUTPUT)/gen_sig --key $(CERTDIR)/signing_key.pem --cert $(CERTDIR)/signing_key.x509 \
+		--data insn.bin --add map.bin:0 --out sig.bin
+
+signed_loader.h: sig.bin
+	$(SCRIPTSDIR)/write-sig.sh loader.h sig.bin > $@
+
+loader: loader.c signed_loader.h
+	$(CC) $(CFLAGS) -I$(LIBDIR) -I$(APIDIR) $< -o $@ -lbpf
+
+
+EXTRA_CLEAN = $(OUTPUT)/gen_sig
diff --git a/tools/testing/selftests/hornet/loader.c b/tools/testing/selftests/hornet/loader.c
new file mode 100644
index 0000000000000..f27580c7262b3
--- /dev/null
+++ b/tools/testing/selftests/hornet/loader.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <sys/resource.h>
+#include <bpf/libbpf.h>
+#include <errno.h>
+#include  "signed_loader.h"
+
+int main(int argc, char **argv)
+{
+	struct trivial *skel;
+
+	skel = trivial__open_and_load();
+	if (!skel)
+		return -1;
+
+	trivial__destroy(skel);
+	return 0;
+}
diff --git a/tools/testing/selftests/hornet/trivial.bpf.c b/tools/testing/selftests/hornet/trivial.bpf.c
new file mode 100644
index 0000000000000..d38c5b53ff932
--- /dev/null
+++ b/tools/testing/selftests/hornet/trivial.bpf.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+
+#include "vmlinux.h"
+
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_core_read.h>
+
+char LICENSE[] SEC("license") = "Dual BSD/GPL";
+
+int monitored_pid = 0;
+
+SEC("tracepoint/syscalls/sys_enter_unlinkat")
+int handle_enter_unlink(struct trace_event_raw_sys_enter *ctx)
+{
+	char filename[128] = { 0 };
+	struct task_struct *task;
+	unsigned long start_time = 0;
+	int pid = bpf_get_current_pid_tgid() >> 32;
+	char *pathname_ptr = (char *) BPF_CORE_READ(ctx, args[1]);
+
+	bpf_probe_read_str(filename, sizeof(filename), pathname_ptr);
+	task = (struct task_struct *)bpf_get_current_task();
+	start_time = BPF_CORE_READ(task, start_time);
+
+	bpf_printk("BPF triggered unlinkat by PID: %d, start_time %ld. pathname = %s",
+		   pid, start_time, filename);
+
+	if (monitored_pid == pid)
+		bpf_printk("target pid found");
+
+	return 0;
+}
-- 
2.53.0


^ permalink raw reply related

* [v6 08/10] hornet: Add a light skeleton data extractor scripts
From: Blaise Boscaccy @ 2026-04-29 19:14 UTC (permalink / raw)
  To: Blaise Boscaccy, Jonathan Corbet, Paul Moore, James Morris,
	Serge E. Hallyn, Mickaël Salaün, Günther Noack,
	Dr. David Alan Gilbert, Andrew Morton, James.Bottomley, dhowells,
	Fan Wu, Ryan Foster, Randy Dunlap, linux-security-module,
	linux-doc, linux-kernel, bpf, Song Liu
In-Reply-To: <20260429191431.2345448-1-bboscaccy@linux.microsoft.com>

These script eases light skeleton development against Hornet by
generating a data payloads which can be used for signing a light
skeleton binary using gen_sig.

Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
 scripts/hornet/extract-insn.sh | 27 +++++++++++++++++++++++++++
 scripts/hornet/extract-map.sh  | 27 +++++++++++++++++++++++++++
 scripts/hornet/extract-skel.sh | 27 +++++++++++++++++++++++++++
 3 files changed, 81 insertions(+)
 create mode 100755 scripts/hornet/extract-insn.sh
 create mode 100755 scripts/hornet/extract-map.sh
 create mode 100755 scripts/hornet/extract-skel.sh

diff --git a/scripts/hornet/extract-insn.sh b/scripts/hornet/extract-insn.sh
new file mode 100755
index 0000000000000..52338f057ff6b
--- /dev/null
+++ b/scripts/hornet/extract-insn.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2025 Microsoft Corporation
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of version 2 of the GNU General Public
+# License as published by the Free Software Foundation.
+
+function usage() {
+    echo "Sample script for extracting instructions"
+    echo "autogenerated eBPF lskel headers"
+    echo ""
+    echo "USAGE: header_file"
+    exit
+}
+
+ARGC=$#
+
+EXPECTED_ARGS=1
+
+if [ $ARGC -ne $EXPECTED_ARGS ] ; then
+    usage
+else
+    printf $(gcc -E $1 | grep "opts_insn" | \
+		 awk -F"=" '{print $2}' | sed 's/;\+$//' | sed 's/\"//g')
+fi
diff --git a/scripts/hornet/extract-map.sh b/scripts/hornet/extract-map.sh
new file mode 100755
index 0000000000000..c309f505c6238
--- /dev/null
+++ b/scripts/hornet/extract-map.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2025 Microsoft Corporation
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of version 2 of the GNU General Public
+# License as published by the Free Software Foundation.
+
+function usage() {
+    echo "Sample script for extracting instructions"
+    echo "autogenerated eBPF lskel headers"
+    echo ""
+    echo "USAGE: header_file"
+    exit
+}
+
+ARGC=$#
+
+EXPECTED_ARGS=1
+
+if [ $ARGC -ne $EXPECTED_ARGS ] ; then
+    usage
+else
+    printf $(gcc -E $1 | grep "opts_data" | \
+		 awk -F"=" '{print $2}' | sed 's/;\+$//' | sed 's/\"//g')
+fi
diff --git a/scripts/hornet/extract-skel.sh b/scripts/hornet/extract-skel.sh
new file mode 100755
index 0000000000000..6550a86b89917
--- /dev/null
+++ b/scripts/hornet/extract-skel.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2025 Microsoft Corporation
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of version 2 of the GNU General Public
+# License as published by the Free Software Foundation.
+
+function usage() {
+    echo "Sample script for extracting instructions and map data out of"
+    echo "autogenerated eBPF lskel headers"
+    echo ""
+    echo "USAGE: header_file field"
+    exit
+}
+
+ARGC=$#
+
+EXPECTED_ARGS=2
+
+if [ $ARGC -ne $EXPECTED_ARGS ] ; then
+    usage
+else
+    printf $(gcc -E $1 | grep "static const char opts_$2" | \
+		 awk -F"=" '{print $2}' | sed 's/;\+$//' | sed 's/\"//g')
+fi
-- 
2.53.0


^ permalink raw reply related

* [v6 07/10] hornet: Introduce gen_sig
From: Blaise Boscaccy @ 2026-04-29 19:14 UTC (permalink / raw)
  To: Blaise Boscaccy, Jonathan Corbet, Paul Moore, James Morris,
	Serge E. Hallyn, Mickaël Salaün, Günther Noack,
	Dr. David Alan Gilbert, Andrew Morton, James.Bottomley, dhowells,
	Fan Wu, Ryan Foster, Randy Dunlap, linux-security-module,
	linux-doc, linux-kernel, bpf, Song Liu
In-Reply-To: <20260429191431.2345448-1-bboscaccy@linux.microsoft.com>

This introduces the gen_sig tool. It creates a pkcs#7 signature of a
data payload. Additionally it appends a signed attribute containing a
set of hashes.

Typical usage is to provide a payload containing the light skeleton
ebpf syscall program binary and it's associated maps, which can be
extracted from the auto-generated skeleton header.

Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
 scripts/Makefile            |   1 +
 scripts/hornet/Makefile     |   5 +
 scripts/hornet/gen_sig.c    | 397 ++++++++++++++++++++++++++++++++++++
 scripts/hornet/write-sig.sh |  27 +++
 4 files changed, 430 insertions(+)
 create mode 100644 scripts/hornet/Makefile
 create mode 100644 scripts/hornet/gen_sig.c
 create mode 100755 scripts/hornet/write-sig.sh

diff --git a/scripts/Makefile b/scripts/Makefile
index 0941e5ce7b575..dea8ab91bbe4e 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -63,6 +63,7 @@ subdir-$(CONFIG_GENKSYMS) += genksyms
 subdir-$(CONFIG_GENDWARFKSYMS) += gendwarfksyms
 subdir-$(CONFIG_SECURITY_SELINUX) += selinux
 subdir-$(CONFIG_SECURITY_IPE) += ipe
+subdir-$(CONFIG_SECURITY_HORNET) += hornet
 
 # Let clean descend into subdirs
 subdir-	+= basic dtc gdb kconfig mod
diff --git a/scripts/hornet/Makefile b/scripts/hornet/Makefile
new file mode 100644
index 0000000000000..3ee41e5e9a9ff
--- /dev/null
+++ b/scripts/hornet/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+hostprogs-always-y	:= gen_sig
+
+HOSTCFLAGS_gen_sig.o = $(shell $(HOSTPKG_CONFIG) --cflags libcrypto 2> /dev/null)
+HOSTLDLIBS_gen_sig = $(shell $(HOSTPKG_CONFIG) --libs libcrypto 2> /dev/null || echo -lcrypto)
diff --git a/scripts/hornet/gen_sig.c b/scripts/hornet/gen_sig.c
new file mode 100644
index 0000000000000..8e328b4ca120a
--- /dev/null
+++ b/scripts/hornet/gen_sig.c
@@ -0,0 +1,397 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+ *
+ * Generate a signature for an eBPF program along with appending
+ * map hashes as signed attributes
+ *
+ * Copyright © 2025      Microsoft Corporation.
+ *
+ * Authors: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the licence, or (at your option) any later version.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <err.h>
+#include <getopt.h>
+
+#include <openssl/cms.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/pkcs7.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/objects.h>
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/opensslv.h>
+#include <openssl/bio.h>
+#include <openssl/stack.h>
+
+#if OPENSSL_VERSION_MAJOR >= 3
+# define USE_PKCS11_PROVIDER
+# include <openssl/provider.h>
+# include <openssl/store.h>
+#else
+# if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_DEPRECATED_3_0)
+#  define USE_PKCS11_ENGINE
+#  include <openssl/engine.h>
+# endif
+#endif
+#include "../ssl-common.h"
+
+#define SHA256_LEN 32
+#define BUF_SIZE   (1 << 15) // 32 KiB
+#define MAX_HASHES 64
+
+struct hash_spec {
+	char *file;
+	int index;
+};
+
+typedef struct {
+	ASN1_INTEGER *index;
+	ASN1_OCTET_STRING *hash;
+
+} HORNET_MAP;
+
+DECLARE_ASN1_FUNCTIONS(HORNET_MAP)
+ASN1_SEQUENCE(HORNET_MAP) = {
+	ASN1_SIMPLE(HORNET_MAP, index, ASN1_INTEGER),
+	ASN1_SIMPLE(HORNET_MAP, hash, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(HORNET_MAP);
+
+IMPLEMENT_ASN1_FUNCTIONS(HORNET_MAP)
+
+DEFINE_STACK_OF(HORNET_MAP)
+
+typedef struct {
+	STACK_OF(HORNET_MAP) * maps;
+} MAP_SET;
+
+DECLARE_ASN1_FUNCTIONS(MAP_SET)
+ASN1_SEQUENCE(MAP_SET) = {
+	ASN1_SET_OF(MAP_SET, maps, HORNET_MAP)
+} ASN1_SEQUENCE_END(MAP_SET);
+
+IMPLEMENT_ASN1_FUNCTIONS(MAP_SET)
+
+#define DIE(...) do { fprintf(stderr, __VA_ARGS__); fputc('\n', stderr); \
+		exit(EXIT_FAILURE); } while (0)
+
+static BIO *bio_open_wr(const char *path)
+{
+	BIO *b = BIO_new_file(path, "wb");
+
+	if (!b) {
+		perror(path);
+		ERR_print_errors_fp(stderr);
+		exit(EXIT_FAILURE);
+	}
+	return b;
+}
+
+static void usage(const char *prog)
+{
+	fprintf(stderr,
+		"Usage:\n"
+		"  %s --data content.bin --cert signer.crt --key signer.key [-pass pass]\n"
+		"     --out newsig.p7b \n"
+		"     --add FILE:index [--add FILE:index ...]\n",
+		prog);
+}
+
+static const char *key_pass;
+
+static int pem_pw_cb(char *buf, int len, int w, void *v)
+{
+	int pwlen;
+
+	if (!key_pass)
+		return -1;
+
+	pwlen = strlen(key_pass);
+	if (pwlen >= len)
+		return -1;
+
+	strcpy(buf, key_pass);
+
+	key_pass = NULL;
+
+	return pwlen;
+}
+
+static EVP_PKEY *read_private_key(const char *private_key_name)
+{
+	EVP_PKEY *private_key;
+	BIO *b;
+
+	b = BIO_new_file(private_key_name, "rb");
+	ERR(!b, "%s", private_key_name);
+	private_key = PEM_read_bio_PrivateKey(b, NULL, pem_pw_cb,
+					      NULL);
+	ERR(!private_key, "%s", private_key_name);
+	BIO_free(b);
+
+	return private_key;
+}
+
+static X509 *read_x509(const char *x509_name)
+{
+	unsigned char buf[2];
+	X509 *x509;
+	BIO *b;
+	int n;
+
+	b = BIO_new_file(x509_name, "rb");
+	ERR(!b, "%s", x509_name);
+
+	/* Look at the first two bytes of the file to determine the encoding */
+	n = BIO_read(b, buf, 2);
+	if (n != 2) {
+		if (BIO_should_retry(b)) {
+			fprintf(stderr, "%s: Read wanted retry\n", x509_name);
+			exit(1);
+		}
+		if (n >= 0) {
+			fprintf(stderr, "%s: Short read\n", x509_name);
+			exit(1);
+		}
+		ERR(1, "%s", x509_name);
+	}
+
+	ERR(BIO_reset(b) != 0, "%s", x509_name);
+
+	if (buf[0] == 0x30 && buf[1] >= 0x81 && buf[1] <= 0x84)
+		/* Assume raw DER encoded X.509 */
+		x509 = d2i_X509_bio(b, NULL);
+	else
+		/* Assume PEM encoded X.509 */
+		x509 = PEM_read_bio_X509(b, NULL, NULL, NULL);
+
+	BIO_free(b);
+	ERR(!x509, "%s", x509_name);
+
+	return x509;
+}
+
+static int sha256(const char *path, unsigned char out[SHA256_LEN], unsigned int *out_len)
+{
+	FILE *f;
+	int rc;
+	EVP_MD_CTX *ctx;
+	unsigned char buf[BUF_SIZE];
+	size_t n;
+	unsigned int mdlen = 0;
+
+	if (!path || !out)
+		return -1;
+
+	f = fopen(path, "rb");
+	if (!f) {
+		perror("fopen");
+		return -2;
+	}
+
+	ERR_load_crypto_strings();
+
+	rc = -3;
+	ctx = EVP_MD_CTX_new();
+	if (!ctx) {
+		rc = -4;
+		goto done;
+	}
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	if (EVP_DigestInit_ex2(ctx, EVP_sha256(), NULL) != 1) {
+		rc = -5;
+		goto done;
+	}
+#else
+	if (EVP_DigestInit_ex(ctx, EVP_sha256(), NULL) != 1) {
+		rc = -5;
+		goto done;
+	}
+#endif
+	while ((n = fread(buf, 1, sizeof(buf), f)) > 0) {
+		if (EVP_DigestUpdate(ctx, buf, n) != 1) {
+			rc = -6;
+			goto done;
+		}
+	}
+	if (ferror(f)) {
+		rc = -7;
+		goto done;
+	}
+
+	if (EVP_DigestFinal_ex(ctx, out, &mdlen) != 1) {
+		rc = -8;
+		goto done;
+	}
+	if (mdlen != SHA256_LEN) {
+		rc = -9;
+		goto done;
+	}
+
+	if (out_len)
+		*out_len = mdlen;
+	rc = 0;
+
+done:
+	EVP_MD_CTX_free(ctx);
+	fclose(f);
+	ERR_free_strings();
+	return rc;
+}
+
+static void add_hash(MAP_SET *set, unsigned char *buffer, int buffer_len, int index)
+{
+	HORNET_MAP *map = NULL;
+
+	map = HORNET_MAP_new();
+	ASN1_INTEGER_set(map->index, index);
+	ASN1_OCTET_STRING_set(map->hash, buffer, buffer_len);
+	sk_HORNET_MAP_push(set->maps, map);
+}
+
+int main(int argc, char **argv)
+{
+	const char *cert_path = NULL;
+	const char *key_path = NULL;
+	const char *data_path = NULL;
+	const char *out_path = NULL;
+
+	X509 *signer;
+	EVP_PKEY *pkey;
+	BIO *data_in;
+	CMS_ContentInfo *cms_out;
+	struct hash_spec hashes[MAX_HASHES];
+	int hash_count = 0;
+	int flags;
+	CMS_SignerInfo *si;
+	MAP_SET *set;
+	unsigned char hash_buffer[SHA256_LEN];
+	unsigned int hash_len;
+	ASN1_OBJECT *oid;
+	unsigned char *der = NULL;
+	int der_len;
+	int err;
+	BIO *b_out;
+	int i;
+	char opt;
+
+	const char *short_opts = "C:K:P:O:A:Sh";
+
+	static const struct option long_opts[] = {
+		{"cert", required_argument, 0, 'C'},
+		{"key",  required_argument, 0, 'K'},
+		{"pass",  required_argument, 0, 'P'},
+		{"out",  required_argument, 0, 'O'},
+		{"data",  required_argument, 0, 'D'},
+		{"add",  required_argument, 0, 'A'},
+		{"help",    no_argument,       0, 'h'},
+		{0, 0, 0, 0}
+	};
+
+	while ((opt = getopt_long_only(argc, argv, short_opts, long_opts, NULL)) != -1) {
+		switch (opt) {
+		case 'C':
+			cert_path = optarg;
+			break;
+		case 'K':
+			key_path = optarg;
+			break;
+		case 'P':
+			key_pass = optarg;
+			break;
+		case 'O':
+			out_path = optarg;
+			break;
+		case 'D':
+			data_path = optarg;
+			break;
+		case 'A':
+			if (strchr(optarg, ':')) {
+				hashes[hash_count].file = strsep(&optarg, ":");
+				hashes[hash_count].index = atoi(optarg);
+				if (++hash_count >= MAX_HASHES) {
+					usage(argv[0]);
+					return EXIT_FAILURE;
+				}
+			} else {
+				usage(argv[0]);
+				return EXIT_FAILURE;
+			}
+		}
+	}
+
+	if (!cert_path || !key_path || !out_path || !data_path) {
+		usage(argv[0]);
+		return EXIT_FAILURE;
+	}
+
+	OpenSSL_add_all_algorithms();
+	ERR_load_crypto_strings();
+
+	signer = read_x509(cert_path);
+	ERR(!signer, "Load cert failed");
+
+	pkey = read_private_key(key_path);
+	ERR(!pkey, "Load key failed");
+
+	data_in = BIO_new_file(data_path, "rb");
+	ERR(!data_in, "Load data failed");
+
+	cms_out = CMS_sign(NULL, NULL, NULL, NULL,
+			   CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY | CMS_DETACHED);
+	ERR(!cms_out, "create cms failed");
+
+	flags = CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY | CMS_NOSMIMECAP | CMS_DETACHED;
+
+	si = CMS_add1_signer(cms_out, signer, pkey, EVP_sha256(), flags);
+	ERR(!si, "add signer failed");
+
+	set = MAP_SET_new();
+	set->maps = sk_HORNET_MAP_new_null();
+
+	for (i = 0; i < hash_count; i++) {
+		if (sha256(hashes[i].file, hash_buffer, &hash_len) != 0) {
+			DIE("failed to hash input");
+		}
+		add_hash(set, hash_buffer, hash_len, hashes[i].index);
+	}
+
+	oid = OBJ_txt2obj("2.25.316487325684022475439036912669789383960", 1);
+	if (!oid) {
+		ERR_print_errors_fp(stderr);
+		DIE("create oid failed");
+	}
+
+	der_len = ASN1_item_i2d((ASN1_VALUE *)set, &der, ASN1_ITEM_rptr(MAP_SET));
+	CMS_signed_add1_attr_by_OBJ(si, oid, V_ASN1_SEQUENCE, der, der_len);
+
+	err = CMS_final(cms_out, data_in, NULL, CMS_NOCERTS | CMS_BINARY);
+	ERR(!err, "cms final failed");
+
+	OPENSSL_free(der);
+	MAP_SET_free(set);
+
+	b_out = bio_open_wr(out_path);
+	ERR(!b_out, "opening output path failed");
+
+	i2d_CMS_bio_stream(b_out, cms_out, NULL, 0);
+
+	BIO_free(data_in);
+	BIO_free(b_out);
+	EVP_cleanup();
+	ERR_free_strings();
+	return 0;
+}
diff --git a/scripts/hornet/write-sig.sh b/scripts/hornet/write-sig.sh
new file mode 100755
index 0000000000000..7eaabe3bab9aa
--- /dev/null
+++ b/scripts/hornet/write-sig.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2025 Microsoft Corporation
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of version 2 of the GNU General Public
+# License as published by the Free Software Foundation.
+
+function usage() {
+    echo "Sample for rewriting an autogenerated eBPF lskel headers"
+    echo "with a new signature"
+    echo ""
+    echo "USAGE: header_file sig"
+    exit
+}
+
+ARGC=$#
+
+EXPECTED_ARGS=2
+
+if [ $ARGC -ne $EXPECTED_ARGS ] ; then
+    usage
+else
+    SIG=$(xxd -p $2 | tr -d '\n' | sed 's/\(..\)/\\\\x\1/g')
+    sed '/const char opts_sig/,/;/c\\tstatic const char opts_sig[] __attribute__((__aligned__(8))) = "\\\n'"$(printf '%s\n' "$SIG")"'\";' $1
+fi
-- 
2.53.0


^ permalink raw reply related

* [v6 06/10] security: Hornet LSM
From: Blaise Boscaccy @ 2026-04-29 19:14 UTC (permalink / raw)
  To: Blaise Boscaccy, Jonathan Corbet, Paul Moore, James Morris,
	Serge E. Hallyn, Mickaël Salaün, Günther Noack,
	Dr. David Alan Gilbert, Andrew Morton, James.Bottomley, dhowells,
	Fan Wu, Ryan Foster, Randy Dunlap, linux-security-module,
	linux-doc, linux-kernel, bpf, Song Liu
In-Reply-To: <20260429191431.2345448-1-bboscaccy@linux.microsoft.com>

This adds the Hornet Linux Security Module which provides enhanced
signature verification and data validation for eBPF programs. This
allows users to continue to maintain an invariant that all code
running inside of the kernel has actually been signed and verified, by
the kernel.

This effort builds upon the currently excepted upstream solution. It
further hardens it by providing deterministic, in-kernel checking of
map hashes to solidify auditing along with preventing TOCTOU attacks
against lskel map hashes.

Target map hashes are passed in via PKCS#7 signed attributes. Hornet
determines the extent which the eBFP program is signed and defers to
other LSMs for policy decisions.

Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
Nacked-by: Alexei Starovoitov <alexei.starovoitov@gmail.com>
---
 Documentation/admin-guide/LSM/Hornet.rst | 323 +++++++++++++++++++++
 Documentation/admin-guide/LSM/index.rst  |   1 +
 MAINTAINERS                              |   9 +
 include/linux/oid_registry.h             |   3 +
 include/uapi/linux/lsm.h                 |   1 +
 security/Kconfig                         |   3 +-
 security/Makefile                        |   1 +
 security/hornet/Kconfig                  |  13 +
 security/hornet/Makefile                 |   7 +
 security/hornet/hornet.asn1              |  12 +
 security/hornet/hornet_lsm.c             | 350 +++++++++++++++++++++++
 11 files changed, 722 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/admin-guide/LSM/Hornet.rst
 create mode 100644 security/hornet/Kconfig
 create mode 100644 security/hornet/Makefile
 create mode 100644 security/hornet/hornet.asn1
 create mode 100644 security/hornet/hornet_lsm.c

diff --git a/Documentation/admin-guide/LSM/Hornet.rst b/Documentation/admin-guide/LSM/Hornet.rst
new file mode 100644
index 0000000000000..ef1605b162f57
--- /dev/null
+++ b/Documentation/admin-guide/LSM/Hornet.rst
@@ -0,0 +1,323 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======
+Hornet
+======
+
+Hornet is a Linux Security Module that provides extensible signature
+verification for eBPF programs. This is selectable at build-time with
+``CONFIG_SECURITY_HORNET``.
+
+Overview
+========
+
+Hornet addresses concerns from users who require strict audit trails and
+verification guarantees for eBPF programs, especially in
+security-sensitive environments. Many production systems need assurance
+that only authorized, unmodified eBPF programs are loaded into the
+kernel. Hornet provides this assurance through cryptographic signature
+verification.
+
+When an eBPF program is loaded via the ``bpf()`` syscall, Hornet
+verifies a PKCS#7 signature attached to the program instructions. The
+signature is checked against whichever keyring was specified by the user
+existing kernel cryptographic infrastructure. In addition to signing the
+program bytecode, Hornet supports signing SHA-256 hashes of associated
+BPF maps, enabling integrity verification of map contents at load time
+and at runtime.
+
+After verification, Hornet classifies the program into one of the
+following integrity states and passes the result to a downstream LSM hook
+(``bpf_prog_load_post_integrity``), allowing other security modules to
+make policy decisions based on the verification outcome:
+
+``LSM_INT_VERDICT_OK``
+  The program signature and all map hashes verified successfully.
+
+``LSM_INT_VERDICT_UNSIGNED``
+  No signature was provided with the program.
+
+``LSM_INT_VERDICT_PARTIALSIG``
+  The program signature verified, but the signature did not contain
+  hornet map hash data.
+
+``LSM_INT_VERDICT_UNKNOWNKEY``
+  The keyring requested by the user is invalid.
+
+``LSM_INT_VERDICT_FAULT``
+  A system error occured during verification.
+
+``LSM_INT_VERDICT_UNEXPECTED``
+  An unexpected map hash value was encountered.
+
+``LSM_INT_VERDICT_BADSIG``
+  The signature or a map hash failed verification.
+
+Hornet itself does not enforce a policy on whether unsigned or partially
+signed programs should be rejected. It delegates that decision to
+downstream LSMs via the ``bpf_prog_load_post_integrity`` hook, making it
+a composable building block in a larger security architecture.
+
+Use Cases
+=========
+
+- **Locked-down production environments**: Ensure only eBPF programs
+  signed by a trusted authority can be loaded, preventing unauthorized
+  or tampered programs from running in the kernel.
+
+- **Audit and compliance**: Provide cryptographic evidence that loaded
+  eBPF programs match their expected build artifacts, supporting
+  compliance requirements in regulated industries.
+
+- **Supply chain integrity**: Verify that eBPF programs and their
+  associated map data have not been modified since they were built and
+  signed, protecting against supply chain attacks.
+
+Threat Model
+============
+
+Hornet protects against the following threats:
+
+- **Unauthorized eBPF program loading**: Programs that have not been
+  signed by a trusted key will be reported as unsigned or badly signed.
+
+- **Tampering with program instructions**: Any modification to the eBPF
+  bytecode after signing will cause signature verification to fail.
+
+- **Tampering with map data**: When map hashes are included in the
+  signature, Hornet verifies that frozen BPF maps match their expected
+  SHA-256 hashes at load time. Maps are also re-verified before program
+  execution via ``BPF_PROG_RUN``.
+
+Hornet does **not** protect against:
+
+- Compromise of the signing key itself.
+- Attacks that occur after a program has been loaded and verified.
+- Programs loaded by the kernel itself (kernel-internal loads bypass
+  the ``BPF_PROG_RUN`` map check).
+
+Known Limitations
+=================
+
+- Hornet requires programs to use :doc:`light skeletons
+  </bpf/libbpf/libbpf_naming_convention>` (lskels) for the signing
+  workflow, as the tooling operates on lskel-generated headers.
+
+- A maximum of 64 maps per program can be tracked for hash
+  verification.
+
+- Map hash verification requires the maps to be frozen before loading.
+  Maps that are not frozen at load time will cause verification to fail
+  when their hashes are included in the signature.
+
+- The only hashing algorithm available is SHA256 due to it be hardcoded
+  in the bpf subsystem.
+
+- Hornet guarantees that the signed program runs only with signed map
+  data. It does not guarantee positional binding of maps to specific
+  fd_array slots.
+
+- BPF_MAP_TYPE_PROG_ARRAY maps must be frozen for Hornet to verify
+  them. Unfrozen prog array maps are not covered by verification.
+
+Configuration
+=============
+
+Build Configuration
+-------------------
+
+Enable Hornet by setting the following kernel configuration option::
+
+  CONFIG_SECURITY_HORNET=y
+
+This option is found under :menuselection:`Security options --> Hornet
+support` and depends on ``CONFIG_SECURITY``.
+
+When enabled, Hornet is included in the default LSM initialization order
+and will appear in ``/sys/kernel/security/lsm``.
+
+Architecture
+============
+
+Signature Verification Flow
+---------------------------
+
+The following describes what happens when a userspace program calls
+``bpf(BPF_PROG_LOAD, ...)`` with a signature attached:
+
+1. The ``bpf_prog_load_integrity`` LSM hook is invoked.
+
+2. Hornet reads the signature from the userspace buffer specified by
+   ``attr->signature`` (with length ``attr->signature_size``).
+
+3. The PKCS#7 signature is verified against the program instructions
+   using ``verify_pkcs7_message_sig()`` with the user specified keyring.
+
+4. The PKCS#7 message is parsed and its trust chain is validated via
+   ``validate_pkcs7_trust()``.
+
+5. Hornet extracts the authenticated attribute identified by
+   ``OID_hornet_data`` (OID ``2.25.316487325684022475439036912669789383960``)
+   from the PKCS#7 message. This attribute contains an ASN.1-encoded set
+   of map index/hash pairs.
+
+6. For each map hash entry, Hornet retrieves the corresponding BPF map
+   via its file descriptor, confirms it is frozen, computes its SHA-256
+   hash, and compares it against the signed hash.
+
+7. The resulting integrity verdict is passed to the
+   ``bpf_prog_load_post_integrity`` hook so that downstream LSMs can
+   enforce policy.
+
+Runtime Map Verification
+------------------------
+
+When ``bpf(BPF_PROG_RUN, ...)`` is called from userspace, Hornet
+re-verifies the hashes of all maps associated with the program. This
+ensures that map contents have not been modified between program load
+and execution. If any map hash no longer matches, the ``BPF_PROG_RUN``
+command is denied.
+
+Userspace Interface
+-------------------
+
+Signatures are passed to the kernel through fields in ``union bpf_attr``
+when using the ``BPF_PROG_LOAD`` command:
+
+``signature``
+  A pointer to a userspace buffer containing the PKCS#7 signature.
+
+``signature_size``
+  The size of the signature buffer in bytes.
+
+ASN.1 Schema
+------------
+
+Map hashes are encoded as a signed attribute in the PKCS#7 message using
+the following ASN.1 schema::
+
+  HornetData ::= SET OF Map
+
+  Map ::= SEQUENCE {
+      index   INTEGER,
+      sha     OCTET STRING
+  }
+
+Each ``Map`` entry contains the index of the map in the program's
+``fd_array`` and its expected SHA-256 hash. A zero-length ``sha`` field
+indicates that the map at that index should be skipped during
+verification.
+
+Tooling
+=======
+
+Helper scripts and a signature generation tool are provided in
+``scripts/hornet/`` to support the development of signed eBPF light
+skeletons.
+
+gen_sig
+-------
+
+``gen_sig`` is a C program (using OpenSSL) that creates a PKCS#7
+signature over eBPF program instructions and optionally includes
+SHA-256 hashes of BPF maps as signed attributes.
+
+Usage::
+
+  gen_sig --data <instructions.bin> \
+          --cert <signer.crt> \
+          --key <signer.key> \
+          [--pass <passphrase>] \
+          --out <signature.p7b> \
+          [--add <mapfile.bin>:<index> ...]
+
+``--data``
+  Path to the binary file containing eBPF program instructions to sign.
+
+``--cert``
+  Path to the signing certificate (PEM or DER format).
+
+``--key``
+  Path to the private key (PEM or DER format).
+
+``--pass``
+  Optional passphrase for the private key.
+
+``--out``
+  Path to write the output PKCS#7 signature.
+
+``--add``
+  Attach a map hash as a signed attribute. The argument is a path to a
+  binary map file followed by a colon and the map's index in the
+  ``fd_array``. This option may be specified multiple times.
+
+extract-skel.sh
+---------------
+
+Extracts a named field from an autogenerated eBPF lskel header file.
+Used internally by other helper scripts.
+
+extract-insn.sh
+---------------
+
+Extracts the eBPF program instructions (``opts_insn``) from an lskel
+header into a binary file suitable for signing with ``gen_sig``.
+
+extract-map.sh
+--------------
+
+Extracts the map data (``opts_data``) from an lskel header into a
+binary file suitable for hashing with ``gen_sig``.
+
+write-sig.sh
+------------
+
+Replaces the signature data in an lskel header with a new signature
+from a binary file. This is used to embed a freshly generated signature
+back into the header after signing.
+
+Signing Workflow
+================
+
+A typical workflow for building and signing an eBPF light skeleton is:
+
+1. **Compile the eBPF program**::
+
+     clang -O2 -target bpf -c program.bpf.c -o program.bpf.o
+
+2. **Generate the light skeleton header** using ``bpftool``::
+
+     bpftool gen skeleton -S program.bpf.o > loader.h
+
+3. **Extract instructions and map data** from the generated header::
+
+     scripts/hornet/extract-insn.sh loader.h > insn.bin
+     scripts/hornet/extract-map.sh loader.h > map.bin
+
+4. **Generate the signature** with ``gen_sig``::
+
+     scripts/hornet/gen_sig \
+       --key signing_key.pem \
+       --cert signing_key.x509 \
+       --data insn.bin \
+       --add map.bin:0 \
+       --out sig.bin
+
+5. **Embed the signature** back into the header::
+
+     scripts/hornet/write-sig.sh loader.h sig.bin > signed_loader.h
+
+6. **Build the loader program** using the signed header::
+
+     cc -o loader loader.c -lbpf
+
+The resulting loader program will pass the embedded signature to the
+kernel when loading the eBPF program, enabling Hornet to verify it.
+
+Testing
+=======
+
+Self-tests are provided in ``tools/testing/selftests/hornet/``. The test
+suite builds a minimal eBPF program (``trivial.bpf.c``), signs it using
+the workflow described above, and verifies that the signed program loads
+successfully.
diff --git a/Documentation/admin-guide/LSM/index.rst b/Documentation/admin-guide/LSM/index.rst
index b44ef68f6e4da..57f6e9fbe5fd1 100644
--- a/Documentation/admin-guide/LSM/index.rst
+++ b/Documentation/admin-guide/LSM/index.rst
@@ -49,3 +49,4 @@ subdirectories.
    SafeSetID
    ipe
    landlock
+   Hornet
diff --git a/MAINTAINERS b/MAINTAINERS
index d1cc0e12fe1f0..0942f5453c04d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11692,6 +11692,15 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/iio/pressure/honeywell,mprls0025pa.yaml
 F:	drivers/iio/pressure/mprls0025pa*
 
+HORNET SECURITY MODULE
+M:	Blaise Boscaccy <bboscaccy@linux.microsoft.com>
+L:	linux-security-module@vger.kernel.org
+S:	Supported
+T:	git https://github.com/blaiseboscaccy/hornet.git
+F:	Documentation/admin-guide/LSM/Hornet.rst
+F:	scripts/hornet/
+F:	security/hornet/
+
 HP BIOSCFG DRIVER
 M:	Jorge Lopez <jorge.lopez2@hp.com>
 L:	platform-driver-x86@vger.kernel.org
diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h
index ebce402854de4..bf852715aaea4 100644
--- a/include/linux/oid_registry.h
+++ b/include/linux/oid_registry.h
@@ -150,6 +150,9 @@ enum OID {
 	OID_id_ml_dsa_65,			/* 2.16.840.1.101.3.4.3.18 */
 	OID_id_ml_dsa_87,			/* 2.16.840.1.101.3.4.3.19 */
 
+	/* Hornet LSM */
+	OID_hornet_data,	  /* 2.25.316487325684022475439036912669789383960 */
+
 	OID__NR
 };
 
diff --git a/include/uapi/linux/lsm.h b/include/uapi/linux/lsm.h
index 938593dfd5daf..2ff9bcdd551e2 100644
--- a/include/uapi/linux/lsm.h
+++ b/include/uapi/linux/lsm.h
@@ -65,6 +65,7 @@ struct lsm_ctx {
 #define LSM_ID_IMA		111
 #define LSM_ID_EVM		112
 #define LSM_ID_IPE		113
+#define LSM_ID_HORNET		114
 
 /*
  * LSM_ATTR_XXX definitions identify different LSM attributes
diff --git a/security/Kconfig b/security/Kconfig
index 6a4393fce9a17..283c4a1032094 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -230,6 +230,7 @@ source "security/safesetid/Kconfig"
 source "security/lockdown/Kconfig"
 source "security/landlock/Kconfig"
 source "security/ipe/Kconfig"
+source "security/hornet/Kconfig"
 
 source "security/integrity/Kconfig"
 
@@ -274,7 +275,7 @@ config LSM
 	default "landlock,lockdown,yama,loadpin,safesetid,apparmor,selinux,smack,tomoyo,ipe,bpf" if DEFAULT_SECURITY_APPARMOR
 	default "landlock,lockdown,yama,loadpin,safesetid,tomoyo,ipe,bpf" if DEFAULT_SECURITY_TOMOYO
 	default "landlock,lockdown,yama,loadpin,safesetid,ipe,bpf" if DEFAULT_SECURITY_DAC
-	default "landlock,lockdown,yama,loadpin,safesetid,selinux,smack,tomoyo,apparmor,ipe,bpf"
+	default "landlock,lockdown,yama,loadpin,safesetid,selinux,smack,tomoyo,apparmor,ipe,hornet,bpf"
 	help
 	  A comma-separated list of LSMs, in initialization order.
 	  Any LSMs left off this list, except for those with order
diff --git a/security/Makefile b/security/Makefile
index 4601230ba442a..b68cb56e419bc 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_CGROUPS)			+= device_cgroup.o
 obj-$(CONFIG_BPF_LSM)			+= bpf/
 obj-$(CONFIG_SECURITY_LANDLOCK)		+= landlock/
 obj-$(CONFIG_SECURITY_IPE)		+= ipe/
+obj-$(CONFIG_SECURITY_HORNET)		+= hornet/
 
 # Object integrity file lists
 obj-$(CONFIG_INTEGRITY)			+= integrity/
diff --git a/security/hornet/Kconfig b/security/hornet/Kconfig
new file mode 100644
index 0000000000000..5be71d97daee2
--- /dev/null
+++ b/security/hornet/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config SECURITY_HORNET
+	bool "Hornet support"
+	select CRYPTO_LIB_SHA256
+	select PKCS7_MESSAGE_PARSER
+	select SYSTEM_DATA_VERIFICATION
+	default n
+	help
+	  This selects Hornet.
+	  Further information can be found in
+	  Documentation/admin-guide/LSM/Hornet.rst.
+
+	  If you are unsure how to answer this question, answer N.
diff --git a/security/hornet/Makefile b/security/hornet/Makefile
new file mode 100644
index 0000000000000..26b6f954f762e
--- /dev/null
+++ b/security/hornet/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_SECURITY_HORNET) := hornet.o
+
+hornet-y := hornet.asn1.o \
+	hornet_lsm.o \
+
+$(obj)/hornet.asn1.o: $(obj)/hornet.asn1.c $(obj)/hornet.asn1.h
diff --git a/security/hornet/hornet.asn1 b/security/hornet/hornet.asn1
new file mode 100644
index 0000000000000..e60abf451ae23
--- /dev/null
+++ b/security/hornet/hornet.asn1
@@ -0,0 +1,12 @@
+-- SPDX-License-Identifier: BSD-3-Clause
+--
+-- Copyright (C) 2026 Microsoft
+--
+-- https://www.rfc-editor.org/rfc/rfc5652#section-3
+
+HornetData ::= SET OF Map
+
+Map ::= SEQUENCE {
+	index			INTEGER ({ hornet_map_index }),
+	sha			OCTET STRING ({ hornet_map_hash })
+} ({ hornet_next_map })
diff --git a/security/hornet/hornet_lsm.c b/security/hornet/hornet_lsm.c
new file mode 100644
index 0000000000000..4656457657ccd
--- /dev/null
+++ b/security/hornet/hornet_lsm.c
@@ -0,0 +1,350 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Hornet Linux Security Module
+ *
+ * Author: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
+ *
+ * Copyright (C) 2026 Microsoft Corporation
+ */
+
+#include <linux/lsm_hooks.h>
+#include <uapi/linux/lsm.h>
+#include <linux/bpf.h>
+#include <linux/verification.h>
+#include <crypto/public_key.h>
+#include <linux/module_signature.h>
+#include <crypto/pkcs7.h>
+#include <linux/sort.h>
+#include <linux/asn1_decoder.h>
+#include <linux/oid_registry.h>
+#include "hornet.asn1.h"
+
+#define MAX_USED_MAPS 64
+
+struct hornet_maps {
+	bpfptr_t fd_array;
+};
+
+/* The only hashing algorithm available is SHA256 due to it be hardcoded
+   in the bpf subsystem. */
+
+struct hornet_parse_context {
+	int indexes[MAX_USED_MAPS];
+	bool skips[MAX_USED_MAPS];
+	unsigned char hashes[SHA256_DIGEST_SIZE * MAX_USED_MAPS];
+	int hash_count;
+};
+
+struct hornet_prog_security_struct {
+	int signed_hash_count;
+	unsigned char signed_hashes[SHA256_DIGEST_SIZE * MAX_USED_MAPS];
+};
+
+struct lsm_blob_sizes hornet_blob_sizes __ro_after_init = {
+	.lbs_bpf_prog = sizeof(struct hornet_prog_security_struct),
+};
+
+static inline struct hornet_prog_security_struct *
+hornet_bpf_prog_security(struct bpf_prog *prog)
+{
+	return prog->aux->security + hornet_blob_sizes.lbs_bpf_prog;
+}
+
+static int hornet_verify_hashes(struct hornet_maps *maps,
+				struct hornet_parse_context *ctx,
+				struct bpf_prog *prog)
+{
+	int map_fd;
+	u32 i;
+	struct bpf_map *map;
+	int err = 0;
+	unsigned char hash[SHA256_DIGEST_SIZE];
+	struct hornet_prog_security_struct *security = hornet_bpf_prog_security(prog);
+
+	for (i = 0; i < ctx->hash_count; i++) {
+		if (ctx->skips[i])
+			continue;
+
+		err = copy_from_bpfptr_offset(&map_fd, maps->fd_array,
+					      ctx->indexes[i] * sizeof(map_fd),
+					      sizeof(map_fd));
+		if (err != 0)
+			return LSM_INT_VERDICT_FAULT;
+
+		CLASS(fd, f)(map_fd);
+		if (fd_empty(f))
+			return LSM_INT_VERDICT_FAULT;
+		if (unlikely(fd_file(f)->f_op != &bpf_map_fops))
+			return LSM_INT_VERDICT_FAULT;
+
+		map = fd_file(f)->private_data;
+		if (!map->frozen)
+			return LSM_INT_VERDICT_FAULT;
+
+		if (!map->ops->map_get_hash)
+			return LSM_INT_VERDICT_FAULT;
+
+		if (map->ops->map_get_hash(map, SHA256_DIGEST_SIZE, hash))
+			return LSM_INT_VERDICT_FAULT;
+
+		err = memcmp(hash, &ctx->hashes[i * SHA256_DIGEST_SIZE],
+			      SHA256_DIGEST_SIZE);
+		if (err)
+			return LSM_INT_VERDICT_UNEXPECTED;
+
+		memcpy(&security->signed_hashes[security->signed_hash_count * SHA256_DIGEST_SIZE],
+		       &ctx->hashes[i * SHA256_DIGEST_SIZE], SHA256_DIGEST_SIZE);
+		security->signed_hash_count++;
+	}
+	return LSM_INT_VERDICT_OK;
+}
+
+int hornet_next_map(void *context, size_t hdrlen,
+		     unsigned char tag,
+		     const void *value, size_t vlen)
+{
+	struct hornet_parse_context *ctx = (struct hornet_parse_context *)context;
+
+	if (++ctx->hash_count >= MAX_USED_MAPS)
+		return -EINVAL;
+	return 0;
+}
+
+int hornet_map_index(void *context, size_t hdrlen,
+		     unsigned char tag,
+		     const void *value, size_t vlen)
+{
+	struct hornet_parse_context *ctx = (struct hornet_parse_context *)context;
+
+	if (vlen != 1)
+		return -EINVAL;
+
+	ctx->indexes[ctx->hash_count] = *(u8 *)value;
+	return 0;
+}
+
+int hornet_map_hash(void *context, size_t hdrlen,
+		    unsigned char tag,
+		    const void *value, size_t vlen)
+
+{
+	struct hornet_parse_context *ctx = (struct hornet_parse_context *)context;
+
+	if (vlen != SHA256_DIGEST_SIZE && vlen != 0)
+		return -EINVAL;
+
+	if (vlen) {
+		ctx->skips[ctx->hash_count] = false;
+		memcpy(&ctx->hashes[ctx->hash_count * SHA256_DIGEST_SIZE], value, vlen);
+	} else
+		ctx->skips[ctx->hash_count] = true;
+
+	return 0;
+}
+
+static int hornet_check_program(struct bpf_prog *prog, union bpf_attr *attr,
+				struct bpf_token *token, bool is_kernel,
+				enum lsm_integrity_verdict *verdict)
+{
+	struct hornet_maps maps = {0};
+	bpfptr_t usig = make_bpfptr(attr->signature, is_kernel);
+	struct pkcs7_message *msg;
+	struct hornet_parse_context *ctx;
+	void *sig;
+	int err;
+	const void *authattrs;
+	size_t authattrs_len;
+	struct key *key;
+	key_ref_t user_key = ERR_PTR(-ENOKEY);
+
+	if (!attr->signature) {
+		*verdict = LSM_INT_VERDICT_UNSIGNED;
+		return 0;
+	}
+
+	if (!attr->signature_size) {
+		*verdict = LSM_INT_VERDICT_BADSIG;
+		return 0;
+	}
+
+	ctx = kzalloc(sizeof(struct hornet_parse_context), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	maps.fd_array = make_bpfptr(attr->fd_array, is_kernel);
+	sig = kzalloc(attr->signature_size, GFP_KERNEL);
+	if (!sig) {
+		err = -ENOMEM;
+		goto out;
+	}
+	err = copy_from_bpfptr(sig, usig, attr->signature_size);
+	if (err != 0) {
+		err = -EFAULT;
+		goto cleanup_sig;
+	}
+
+	msg = pkcs7_parse_message(sig, attr->signature_size);
+	if (IS_ERR(msg)) {
+		*verdict = LSM_INT_VERDICT_BADSIG;
+		err = 0;
+		goto cleanup_sig;
+	}
+
+	if (system_keyring_id_check(attr->keyring_id) == 0)
+		key = (struct key*)(unsigned long)attr->keyring_id;
+	else {
+		user_key = lookup_user_key(attr->keyring_id, 0, KEY_DEFER_PERM_CHECK);
+		if (IS_ERR(user_key)) {
+			*verdict = LSM_INT_VERDICT_UNKNOWNKEY;
+			goto cleanup_msg;
+		}
+		key = key_ref_to_ptr(user_key);
+	}
+
+	if (verify_pkcs7_message_sig(prog->insnsi, prog->len * sizeof(struct bpf_insn), msg,
+				     key,
+				     VERIFYING_BPF_SIGNATURE,
+				     NULL, NULL)) {
+		*verdict = LSM_INT_VERDICT_BADSIG;
+		err = 0;
+		goto cleanup_msg;
+	}
+
+	if (pkcs7_get_authattr(msg, OID_hornet_data,
+			       &authattrs, &authattrs_len) == -ENODATA) {
+		*verdict = LSM_INT_VERDICT_PARTIALSIG;
+		err = 0;
+		goto cleanup_msg;
+	}
+
+	err = asn1_ber_decoder(&hornet_decoder, ctx, authattrs, authattrs_len);
+	if (err < 0 || authattrs == NULL) {
+		*verdict = LSM_INT_VERDICT_BADSIG;
+		err = 0;
+		goto cleanup_msg;
+	}
+
+	*verdict = hornet_verify_hashes(&maps, ctx, prog);
+	err = 0;
+
+cleanup_msg:
+	pkcs7_free_message(msg);
+	if (!IS_ERR(user_key))
+		key_put(key);
+cleanup_sig:
+	kfree(sig);
+out:
+	kfree(ctx);
+	return err;
+}
+
+static const struct lsm_id hornet_lsmid = {
+	.name = "hornet",
+	.id = LSM_ID_HORNET,
+};
+
+static int hornet_bpf_prog_load_integrity(struct bpf_prog *prog, union bpf_attr *attr,
+					  struct bpf_token *token, bool is_kernel)
+{
+	enum lsm_integrity_verdict verdict;
+	int result = hornet_check_program(prog, attr, token, is_kernel, &verdict);
+
+	if (result < 0)
+		return result;
+
+	return security_bpf_prog_load_post_integrity(prog, attr, token, is_kernel,
+						     &hornet_lsmid, verdict);
+}
+
+static int hornet_check_prog_maps(u32 ufd)
+{
+	CLASS(fd, f)(ufd);
+	struct bpf_prog *prog;
+	struct hornet_prog_security_struct *security;
+	unsigned char hash[SHA256_DIGEST_SIZE];
+	struct bpf_map *map;
+	int i, j;
+	bool found;
+	int covered_count = 0;
+
+	if (fd_empty(f))
+		return -EBADF;
+	if (fd_file(f)->f_op != &bpf_prog_fops)
+		return -EINVAL;
+
+	prog = fd_file(f)->private_data;
+	security = hornet_bpf_prog_security(prog);
+
+	if (!security->signed_hash_count)
+		return 0;
+
+	mutex_lock(&prog->aux->used_maps_mutex);
+
+	/* Verify every used_map has a matching signed hash */
+	for (j = 0; j < prog->aux->used_map_cnt; j++) {
+		map = prog->aux->used_maps[j];
+
+		if (!map->frozen || !map->ops->map_get_hash)
+			continue;
+
+		if (map->ops->map_get_hash(map, SHA256_DIGEST_SIZE, hash))
+			continue;
+
+		found = false;
+		for (i = 0; i < security->signed_hash_count; i++) {
+			if (memcmp(hash,
+				   &security->signed_hashes[i * SHA256_DIGEST_SIZE],
+				   SHA256_DIGEST_SIZE) == 0) {
+				found = true;
+				break;
+			}
+		}
+		if (!found) {
+			mutex_unlock(&prog->aux->used_maps_mutex);
+			return -EPERM;
+		}
+		covered_count++;
+	}
+
+	mutex_unlock(&prog->aux->used_maps_mutex);
+
+	/* Ensure all signed hashes were accounted for */
+	if (covered_count != security->signed_hash_count)
+		return -EPERM;
+
+	return 0;
+}
+
+static int hornet_bpf(int cmd, union bpf_attr *attr, unsigned int size, bool kernel)
+{
+	/* in horent_bpf(), anything that had originated from kernel space we assume
+	   has already been checked, in some form or another, so we don't bother
+	   checking the intergity of any maps. In hornet_bpf_prog_load_integrity(),
+	   hornet doesn't make any opinion on that and delegates that to the downstream
+	   policy enforcement. */
+
+	if (cmd != BPF_PROG_RUN)
+		return 0;
+	if (kernel)
+		return 0;
+
+	return hornet_check_prog_maps(attr->test.prog_fd);
+}
+
+static struct security_hook_list hornet_hooks[] __ro_after_init = {
+	LSM_HOOK_INIT(bpf_prog_load_integrity, hornet_bpf_prog_load_integrity),
+	LSM_HOOK_INIT(bpf, hornet_bpf),
+};
+
+static int __init hornet_init(void)
+{
+	pr_info("Hornet: eBPF signature verification enabled\n");
+	security_add_hooks(hornet_hooks, ARRAY_SIZE(hornet_hooks), &hornet_lsmid);
+	return 0;
+}
+
+DEFINE_LSM(hornet) = {
+	.id = &hornet_lsmid,
+	.blobs = &hornet_blob_sizes,
+	.init = hornet_init,
+};
-- 
2.53.0


^ permalink raw reply related


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