From geert at linux-m68k.org  Mon Jun  1 01:31:18 2026
From: geert at linux-m68k.org (Geert Uytterhoeven)
Date: Mon, 1 Jun 2026 10:31:18 +0200
Subject: [PATCH 1/8] x86: remove ts5500 platforms support
In-Reply-To: <20260522141959.1071595-2-arnd@kernel.org>
References: <20260522141959.1071595-1-arnd@kernel.org> <20260522141959.1071595-2-arnd@kernel.org>
Message-ID: <CAMuHMdWroJRgr-p7CXLf08EJkbPxhro1WVYGe0pQ8Zu__jMobA@mail.gmail.com>

Hi Arnd,

On Fri, 22 May 2026 at 16:20, Arnd Bergmann <arnd at kernel.org> wrote:
> From: Arnd Bergmann <arnd at arndb.de>
>
> Support for ?lan was removed in 7.1, but the ts5500 platform
> still depends on it. Remove this one as well, not that it
> cannot be used any more.

https://www.embeddedts.com/products/TS-5500

   "Retired. Last Time Buy. This product is being retired. We will
    have a Last Time Buy until 01/07/26. Supplies are limited."

but

   "Available with Linux 2.4 kernel"

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert at linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds


From arnd at arndb.de  Mon Jun  1 05:27:52 2026
From: arnd at arndb.de (Arnd Bergmann)
Date: Mon, 01 Jun 2026 14:27:52 +0200
Subject: [PATCH 1/8] x86: remove ts5500 platforms support
In-Reply-To: <CAMuHMdWroJRgr-p7CXLf08EJkbPxhro1WVYGe0pQ8Zu__jMobA@mail.gmail.com>
References: <20260522141959.1071595-1-arnd@kernel.org>
 <20260522141959.1071595-2-arnd@kernel.org>
 <CAMuHMdWroJRgr-p7CXLf08EJkbPxhro1WVYGe0pQ8Zu__jMobA@mail.gmail.com>
Message-ID: <95b6f585-f52e-481e-9d55-cc1a5dd9ace2@app.fastmail.com>

On Mon, Jun 1, 2026, at 10:31, Geert Uytterhoeven wrote:
> Hi Arnd,
>
> On Fri, 22 May 2026 at 16:20, Arnd Bergmann <arnd at kernel.org> wrote:
>> From: Arnd Bergmann <arnd at arndb.de>
>>
>> Support for ?lan was removed in 7.1, but the ts5500 platform
>> still depends on it. Remove this one as well, not that it
>> cannot be used any more.
>
> https://www.embeddedts.com/products/TS-5500
>
>    "Retired. Last Time Buy. This product is being retired. We will
>     have a Last Time Buy until 01/07/26. Supplies are limited."
>
> but
>
>    "Available with Linux 2.4 kernel"

Linux-2.4 and DOS are probably more commonly used on these than
anything modern, given that this is a 486 with 32 MB.

There is a good chance it still would have worked with
linux-7.0 if anyone had tried.

        Arnd


From kees at kernel.org  Mon Jun  1 12:59:49 2026
From: kees at kernel.org (Kees Cook)
Date: Mon, 1 Jun 2026 12:59:49 -0700
Subject: [PATCH 00/11] Convert moduleparams to seq_buf
In-Reply-To: <88c5ca1d-eeda-4023-bc7a-397b92780db9@suse.com>
References: <20260521133315.work.845-kees@kernel.org>
 <88c5ca1d-eeda-4023-bc7a-397b92780db9@suse.com>
Message-ID: <202606011259.CE6E74CE@keescook>

On Tue, May 26, 2026 at 08:53:06AM +0200, Petr Pavlu wrote:
> On 5/21/26 3:33 PM, Kees Cook wrote:
> > Hi,
> > 
> > I tried to trim the CC list here, but it's still pretty huge...
> > 
> > We've had a long-standing issue with "write to a string pointer" callbacks
> > that don't bounds check the destination (and for which the bounds is
> > also not part of the callback prototype, even if it is "known" to be
> > PAGE_SIZE, which sysfs_emit() depends on). Both moduleparams and sysfs
> > use this pattern. As a first step, and to test the migration method,
> > migrate moduleparams first.
> > 
> > There are 2 "mechanical" treewide patches that are handled by Coccinelle:
> > - treewide: Convert struct kernel_param_ops initializers to DEFINE_KERNEL_PARAM_OPS
> > - treewide: Convert custom kernel_param_ops .get callbacks to seq_buf via cocci
> > 
> > The last treewide patch is manual, and may need to be broken up into
> > per-subsystem patches, though I'd prefer to avoid this, as it would
> > extend the migration from 1 relase to at least 2 releases. (1 to
> > release the migration infrastructure, then 1 release to collect all the
> > subsystem changes, and possibly 1 more release to remove the migration
> > infrastructure.)
> > 
> > Thoughts, questions?
> 
> This looks reasonable to me. I added a few minor comments on the patches
> but they already look solid.

Thanks for the review! I'll get a v2 prepared with your notes addressed. :)

-Kees

-- 
Kees Cook


From andriy.shevchenko at linux.intel.com  Tue Jun  2 04:26:46 2026
From: andriy.shevchenko at linux.intel.com (Andy Shevchenko)
Date: Tue, 2 Jun 2026 14:26:46 +0300
Subject: [PATCH 01/11] params: bound array element output to the caller's
 page buffer
In-Reply-To: <20260521133326.2465264-1-kees@kernel.org>
References: <20260521133315.work.845-kees@kernel.org>
 <20260521133326.2465264-1-kees@kernel.org>
Message-ID: <ah699hwLxIIOZ0-7@ashevche-desk.local>

On Thu, May 21, 2026 at 06:33:14AM -0700, Kees Cook wrote:
> 
> param_array_get() appends each element's string representation into the
> shared sysfs page buffer by passing buffer + off to the element getter.
> 
> That works for getters that only write a small bounded string, but
> param_get_charp() and similar helpers format against PAGE_SIZE from the
> pointer they receive. Once off is non-zero, an element getter can
> therefore write past the end of the original sysfs page buffer.
> 
> Collect each element into a temporary PAGE_SIZE buffer first and then
> copy only the remaining space into the caller's page buffer.

...

> +	elem_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);

get_free_page() (or how it is called)?

> +	if (!elem_buf)
> +		return -ENOMEM;
> +
>  	for (i = off = 0; i < (arr->num ? *arr->num : arr->max); i++) {
> -		/* Replace \n with comma */
> -		if (i)
> -			buffer[off - 1] = ',';
>  		p.arg = arr->elem + arr->elemsize * i;
>  		check_kparam_locked(p.mod);
> -		ret = arr->ops->get(buffer + off, &p);
> +		ret = arr->ops->get(elem_buf, &p);
>  		if (ret < 0)
> -			return ret;
> +			goto out;
> +		ret = min(ret, (int)(PAGE_SIZE - 1 - off));

It's usually discouraged to use castings in min/max/clamp. Can we make ret long
or do something different here?

> +		if (!ret)
> +			break;

> +		/* Replace the previous element's trailing newline with a comma. */
> +		if (i)
> +			buffer[off - 1] = ',';

Can't we do this after with help of strreplace()?

> +		memcpy(buffer + off, elem_buf, ret);
>  		off += ret;
> +		if (off == PAGE_SIZE - 1)
> +			break;
>  	}
>  	buffer[off] = '\0';
> -	return off;
> +	ret = off;
> +out:
> +	kfree(elem_buf);
> +	return ret;

-- 
With Best Regards,
Andy Shevchenko




From jgg at ziepe.ca  Tue Jun  2 05:33:44 2026
From: jgg at ziepe.ca (Jason Gunthorpe)
Date: Tue, 2 Jun 2026 09:33:44 -0300
Subject: [PATCH 01/11] params: bound array element output to the caller's
 page buffer
In-Reply-To: <ah699hwLxIIOZ0-7@ashevche-desk.local>
References: <20260521133315.work.845-kees@kernel.org>
 <20260521133326.2465264-1-kees@kernel.org>
 <ah699hwLxIIOZ0-7@ashevche-desk.local>
Message-ID: <20260602123344.GG2487554@ziepe.ca>

On Tue, Jun 02, 2026 at 02:26:46PM +0300, Andy Shevchenko wrote:
> On Thu, May 21, 2026 at 06:33:14AM -0700, Kees Cook wrote:
> > 
> > param_array_get() appends each element's string representation into the
> > shared sysfs page buffer by passing buffer + off to the element getter.
> > 
> > That works for getters that only write a small bounded string, but
> > param_get_charp() and similar helpers format against PAGE_SIZE from the
> > pointer they receive. Once off is non-zero, an element getter can
> > therefore write past the end of the original sysfs page buffer.
> > 
> > Collect each element into a temporary PAGE_SIZE buffer first and then
> > copy only the remaining space into the caller's page buffer.
> 
> ...
> 
> > +	elem_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
> 
> get_free_page() (or how it is called)?

I thought modern mm guidance was to use kmalloc whenever possible and
not use get_free_page() unless you intend to use the struct page bits?

Jason


From willy at infradead.org  Mon Jun  1 13:23:53 2026
From: willy at infradead.org (Matthew Wilcox)
Date: Mon, 1 Jun 2026 21:23:53 +0100
Subject: [PATCH 01/11] params: bound array element output to the caller's
 page buffer
In-Reply-To: <20260521174631.71a06440@pumpkin>
References: <20260521133315.work.845-kees@kernel.org>
 <20260521133326.2465264-1-kees@kernel.org>
 <20260521174631.71a06440@pumpkin>
Message-ID: <ah3qWZ4cqhrbHZcl@casper.infradead.org>

On Thu, May 21, 2026 at 05:46:31PM +0100, David Laight wrote:
> On Thu, 21 May 2026 06:33:14 -0700
> Kees Cook <kees at kernel.org> wrote:
> > Collect each element into a temporary PAGE_SIZE buffer first and then
> > copy only the remaining space into the caller's page buffer.
> 
> Should this be using a 4k buffer on all architectures?
> Initially perhaps just using a different name for the constant until
> all the associated PAGE_SIZE limits have been removed.

If we're acually going to think about this, even 4KiB is too big.
An 80x25 terminal is 2000 bytes (assuming no utf8), so 4KiB is two
entire screenfuls.  Limiting to 2048 would seem reasonable to me.


From david.laight.linux at gmail.com  Tue Jun  2 06:04:51 2026
From: david.laight.linux at gmail.com (David Laight)
Date: Tue, 2 Jun 2026 14:04:51 +0100
Subject: [PATCH 01/11] params: bound array element output to the
 caller's page buffer
In-Reply-To: <ah699hwLxIIOZ0-7@ashevche-desk.local>
References: <20260521133315.work.845-kees@kernel.org>
	<20260521133326.2465264-1-kees@kernel.org>
	<ah699hwLxIIOZ0-7@ashevche-desk.local>
Message-ID: <20260602140451.2e3e6622@pumpkin>

On Tue, 2 Jun 2026 14:26:46 +0300
Andy Shevchenko <andriy.shevchenko at linux.intel.com> wrote:

> On Thu, May 21, 2026 at 06:33:14AM -0700, Kees Cook wrote:
> > 
> > param_array_get() appends each element's string representation into the
> > shared sysfs page buffer by passing buffer + off to the element getter.
> > 
> > That works for getters that only write a small bounded string, but
> > param_get_charp() and similar helpers format against PAGE_SIZE from the
> > pointer they receive. Once off is non-zero, an element getter can
> > therefore write past the end of the original sysfs page buffer.
> > 
> > Collect each element into a temporary PAGE_SIZE buffer first and then
> > copy only the remaining space into the caller's page buffer.  
> 
> ...
> 
> > +	elem_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);  
> 
> get_free_page() (or how it is called)?

The kmalloc() should be faster and I think has to be aligned.
There is another patch set to replace get_free_pages() with kmalloc().

Although all these 'show' functions should really head to using a safer
interface.
Although, at the moment, it is really difficult to find the ones that
are guaranteed to be passed a page aligned buffer.

-- David

> 
> > +	if (!elem_buf)
> > +		return -ENOMEM;
> > +
> >  	for (i = off = 0; i < (arr->num ? *arr->num : arr->max); i++) {
> > -		/* Replace \n with comma */
> > -		if (i)
> > -			buffer[off - 1] = ',';
> >  		p.arg = arr->elem + arr->elemsize * i;
> >  		check_kparam_locked(p.mod);
> > -		ret = arr->ops->get(buffer + off, &p);
> > +		ret = arr->ops->get(elem_buf, &p);
> >  		if (ret < 0)
> > -			return ret;
> > +			goto out;
> > +		ret = min(ret, (int)(PAGE_SIZE - 1 - off));  
> 
> It's usually discouraged to use castings in min/max/clamp. Can we make ret long
> or do something different here?
> 
> > +		if (!ret)
> > +			break;  
> 
> > +		/* Replace the previous element's trailing newline with a comma. */
> > +		if (i)
> > +			buffer[off - 1] = ',';  
> 
> Can't we do this after with help of strreplace()?
> 
> > +		memcpy(buffer + off, elem_buf, ret);
> >  		off += ret;
> > +		if (off == PAGE_SIZE - 1)
> > +			break;
> >  	}
> >  	buffer[off] = '\0';
> > -	return off;
> > +	ret = off;
> > +out:
> > +	kfree(elem_buf);
> > +	return ret;  
> 



From alex.hung at amd.com  Tue Jun  2 17:19:20 2026
From: alex.hung at amd.com (Alex Hung)
Date: Tue, 2 Jun 2026 18:19:20 -0600
Subject: [PATCH 1/1[RFC] um: include .fini_array.* in linker scripts
In-Reply-To: <20260512232640.1937941-2-alex.hung@amd.com>
References: <20260512232640.1937941-1-alex.hung@amd.com>
 <20260512232640.1937941-2-alex.hung@amd.com>
Message-ID: <a497ca92-9157-4ab1-982b-4dab4cab2de1@amd.com>

Hi,

Are there any concerns or comments on this patch?

On 5/12/26 17:26, Alex Hung wrote:
> GCC emits per-translation-unit gcov destructors into
> .fini_array.NNNNN sections (prioritized). The UML linker
> scripts only captured *(.fini_array), silently discarding
> all prioritized destructor entries.
> 
> As a result, gcov's __gcov_exit never ran at process halt
> and no .gcda files were written after KUnit tests completed.
> 
> Add *(.fini_array.*) to common.lds.S and dyn.lds.S, mirroring
> the existing *(.init_array.*) pattern already present for
> constructors.
> 
> Signed-off-by: Alex Hung <alex.hung at amd.com>
> Assisted-by: Copilot:Claude-Sonnet-4.6
> ---
>   arch/um/include/asm/common.lds.S | 1 +
>   arch/um/kernel/dyn.lds.S         | 2 +-
>   2 files changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/um/include/asm/common.lds.S b/arch/um/include/asm/common.lds.S
> index fd481ac371de..336361412b47 100644
> --- a/arch/um/include/asm/common.lds.S
> +++ b/arch/um/include/asm/common.lds.S
> @@ -90,6 +90,7 @@
>     }
>     .fini_array : {
>   	__fini_array_start = .;
> +	*(.fini_array.*)
>   	*(.fini_array)
>   	__fini_array_end = .;
>     }
> diff --git a/arch/um/kernel/dyn.lds.S b/arch/um/kernel/dyn.lds.S
> index ad3cefeff2ac..fbd8d559f21f 100644
> --- a/arch/um/kernel/dyn.lds.S
> +++ b/arch/um/kernel/dyn.lds.S
> @@ -113,7 +113,7 @@ SECTIONS
>       *(.init_array.*)
>       *(.init_array)
>     }
> -  .fini_array     : { *(.fini_array) }
> +  .fini_array     : { *(.fini_array.*) *(.fini_array) }
>     .data           : {
>       INIT_TASK_DATA(KERNEL_STACK_SIZE)
>       DATA_DATA
> --
> 2.43.0
> 



From andriy.shevchenko at linux.intel.com  Wed Jun  3 00:29:58 2026
From: andriy.shevchenko at linux.intel.com (Andy Shevchenko)
Date: Wed,  3 Jun 2026 09:29:58 +0200
Subject: [PATCH v1 1/1] um: vector: Use %pM format specifier for MAC addresses
Message-ID: <20260603072958.107215-1-andriy.shevchenko@linux.intel.com>

Convert to %pM instead of using custom code.

Signed-off-by: Andy Shevchenko <andriy.shevchenko at linux.intel.com>
---
 arch/um/drivers/vector_kern.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/arch/um/drivers/vector_kern.c b/arch/um/drivers/vector_kern.c
index 2cc90055499a..e09c9db4f4a7 100644
--- a/arch/um/drivers/vector_kern.c
+++ b/arch/um/drivers/vector_kern.c
@@ -1557,13 +1557,16 @@ static void vector_setup_etheraddr(struct net_device *dev, char *str)
 			"Attempt to assign an invalid ethernet address to a device disallowed\n");
 		goto random;
 	}
+
+	eth_hw_addr_set(dev, addr);
+
 	if (!is_local_ether_addr(addr)) {
+		addr[0] |= 0x02;
 		netdev_warn(dev, "Warning: Assigning a globally valid ethernet address to a device\n");
 		netdev_warn(dev, "You should set the 2nd rightmost bit in the first byte of the MAC,\n");
-		netdev_warn(dev, "i.e. %02x:%02x:%02x:%02x:%02x:%02x\n",
-			addr[0] | 0x02, addr[1], addr[2], addr[3], addr[4], addr[5]);
+		netdev_warn(dev, "i.e. %pM\n", addr);
 	}
-	eth_hw_addr_set(dev, addr);
+
 	return;
 
 random:
-- 
2.50.1



From jim.cromie at gmail.com  Wed Jun 10 14:06:02 2026
From: jim.cromie at gmail.com (jim.cromie at gmail.com)
Date: Wed, 10 Jun 2026 15:06:02 -0600
Subject: [PATCH 04/11] treewide: Convert struct kernel_param_ops
 initializers to DEFINE_KERNEL_PARAM_OPS
In-Reply-To: <da358ae1-91b4-4a16-ac76-ffab99c230b9@suse.com>
References: <20260521133315.work.845-kees@kernel.org> <20260521133326.2465264-4-kees@kernel.org>
 <da358ae1-91b4-4a16-ac76-ffab99c230b9@suse.com>
Message-ID: <CAJfuBxwRuT1K=rjPX+sdNyYurEJ=OjqbJaSa_S6JnY6yzTwTvQ@mail.gmail.com>

On Mon, May 25, 2026 at 7:35?AM Petr Pavlu <petr.pavlu at suse.com> wrote:
>
> On 5/21/26 3:33 PM, Kees Cook wrote:
> > Using Coccinelle, rewrite every struct kernel_param_ops initializer that
> > sets .get into a DEFINE_KERNEL_PARAM_OPS-family macro invocation,
> > for example:
> >
> > @@
> > declarer name DEFINE_KERNEL_PARAM_OPS;
> > identifier OPS;
> > expression SET, GET;
> > @@
> > - const struct kernel_param_ops OPS = {
> > -       .set = SET,
> > -       .get = GET,
> > - };
> > + DEFINE_KERNEL_PARAM_OPS(OPS, SET, GET);
> >
> > Using the macro for initialization means future changes can manipulate
> > the struct layout and callback prototypes without having to change every
> > initializer.
>
> Nit: For consistency, I suggest also converting the few remaining
> kernel_param_ops instances that specify only .set and no .get, such as
> simdisk_param_ops_filename.
>
> --
> Thanks,
> Petr

for the dynamic-debug changes

Reviewed-by: Jim Cromie <jim.cromie at gmail.com>


From johan at kernel.org  Fri Jun 12 08:57:39 2026
From: johan at kernel.org (Johan Hovold)
Date: Fri, 12 Jun 2026 17:57:39 +0200
Subject: [PATCH] um: virtio_uml: switch to dynamic root device
In-Reply-To: <20260424103101.2616338-1-johan@kernel.org>
References: <20260424103101.2616338-1-johan@kernel.org>
Message-ID: <aiwsczUnvyWS3dYL@hovoldconsulting.com>

On Fri, Apr 24, 2026 at 12:31:01PM +0200, Johan Hovold wrote:
> Driver core expects devices to be dynamically allocated and will, for
> example, complain loudly when no release function has been provided.
> 
> Use __root_device_register() to allocate and register the root device
> instead of open coding using a static device.
> 
> Note that root_device_register(), which also creates a link to the
> module, cannot be used as the device is registered when parsing the
> module parameters which happens before the module kobject has been
> set up.
> 
> Signed-off-by: Johan Hovold <johan at kernel.org>
> ---

Can this one be picked up now?

Johan


From bp at alien8.de  Mon Jun 15 16:53:18 2026
From: bp at alien8.de (Borislav Petkov)
Date: Mon, 15 Jun 2026 16:53:18 -0700
Subject: [PATCH v3] lib/raid/xor: x86: Add AVX-512 optimized xor_gen()
In-Reply-To: <20260615212922.GA28589@quark>
References: <20260615190338.26581-1-ebiggers@kernel.org>
 <20260615201050.GB1764@quark>
 <255CAE3E-7FD3-4DC2-B3DE-46BE67EF22A8@alien8.de>
 <20260615212922.GA28589@quark>
Message-ID: <20260615235318.GBajCQbuy9dBgKH8L_@fat_crate.local>

On Mon, Jun 15, 2026 at 02:29:22PM -0700, Eric Biggers wrote:
> On Mon, Jun 15, 2026 at 09:16:55PM +0000, Borislav Petkov wrote:
> > On June 15, 2026 8:10:50 PM UTC, Eric Biggers <ebiggers at kernel.org> wrote:
> > >
> > >But I wanted to ask: do we really care about the case where features are
> > >"supported" but their XCR0 bits aren't set?  Perhaps the kernel just
> > >doesn't/shouldn't support weird cases like "-cpu max,xsave=off"?
> > >
> > 
> > Yes, our aim is to support only configurations which are actually
> > present in real hardware and not a "oh, it would be good if it did
> > that, just because..."
> 
> Seems reasonable to me.  Would the same apply to UML here?

Good question.

Richi?

> > >If this case indeed needs to be handled, could we make things easier for
> > >the kernel's AVX and AVX-512 optimized code?  Currently AVX-512 needs:
> > >
> > >        if (boot_cpu_has(X86_FEATURE_AVX512F) &&
> > >            cpu_has_xfeatures(XFEATURE_MASK_FP | XFEATURE_MASK_SSE |
> > >                              XFEATURE_MASK_YMM | XFEATURE_MASK_AVX512, NULL))
> > >
> > >How about we make X86_FEATURE_AVX512F depend on XCR0=111xx111, and
> > >X86_FEATURE_AVX depend on XCR0=xxxxx111?  Then the cpu_has_xfeatures()
> > >check wouldn't be needed.  Is there any reason not to do that?
> > 
> >  How do you want to accomplish that? Very early during boot on the BSP
> >  you sanity-check XCR0 and clear feature flags if components are not
> >  set? 
> 
> That would be the idea.  Something similar to what
> arch/x86/kernel/cpu/cpuid-deps.c does. 

Yap.

> Except that seems to only enforce the dependencies when the kernel itself is
> disabling things; if the hypervisor is broken then it just warns.

Not the kernel's problem. We deliberately don't want to maintain a zoo of
options which are not present in real hw. If HV is doing funny things, oh
well...


> In any case, I'd like these to go away:
> 
>     $ git grep cpu_has_xfeatures | wc -l
>     31

Yeah, all in crypto. I can certainly see why.

@dhansen, any other thoughts?

Thx.

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette


From dave.hansen at intel.com  Mon Jun 15 17:29:58 2026
From: dave.hansen at intel.com (Dave Hansen)
Date: Mon, 15 Jun 2026 17:29:58 -0700
Subject: [PATCH v3] lib/raid/xor: x86: Add AVX-512 optimized xor_gen()
In-Reply-To: <20260615235318.GBajCQbuy9dBgKH8L_@fat_crate.local>
References: <20260615190338.26581-1-ebiggers@kernel.org>
 <20260615201050.GB1764@quark>
 <255CAE3E-7FD3-4DC2-B3DE-46BE67EF22A8@alien8.de>
 <20260615212922.GA28589@quark>
 <20260615235318.GBajCQbuy9dBgKH8L_@fat_crate.local>
Message-ID: <a832eee3-55ec-4cf4-907f-346ff98870ca@intel.com>

On 6/15/26 16:53, Borislav Petkov wrote:
> 
>> In any case, I'd like these to go away:
>>
>>     $ git grep cpu_has_xfeatures | wc -l
>>     31
> Yeah, all in crypto. I can certainly see why.
> 
> @dhansen, any other thoughts?

If we can get rid of cpu_has_xfeatures(), I'm all for it. I'm not quite
sure how the code would look so I'm reserving judgement until I see the
patches. But it's worth a try.


From me at cherr.cc  Mon Jun 15 20:51:14 2026
From: me at cherr.cc (Shengzhuo Wei)
Date: Tue, 16 Jun 2026 11:51:14 +0800
Subject: [PATCH RESEND] um: mconsole: Fix out-of-bounds read in mconsole_log()
Message-ID: <20260616-mconsole-oob-read-leak-v1-1-528d711f63fb@cherr.cc>

mconsole_parse() matches the 3-byte prefix "log", but mconsole_log()
skips strlen("log ") = 4 bytes, advancing ptr past the NUL terminator
when a client sends "log" without a trailing space.

The length then comes from req->len, the raw recvfrom() count, instead
of req->request.len, so printk() reads up to req->len - 4 bytes of
stale data from a previous request left in the static mc_request
buffer, leaking it to the kernel log.

Use req->cmd->command for the actual command length, add skip_spaces(),
use req->request.len, and guard against non-positive length.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Shengzhuo Wei <me at cherr.cc>
---
 arch/um/drivers/mconsole_kern.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c
index e2a9e8879f584734cf2e94d47e403d03f8aa2131..0dd5aab1544648a7f7942c2eb47a5792e7dd702b 100644
--- a/arch/um/drivers/mconsole_kern.c
+++ b/arch/um/drivers/mconsole_kern.c
@@ -117,10 +117,12 @@ void mconsole_log(struct mc_request *req)
 	int len;
 	char *ptr = req->request.data;
 
-	ptr += strlen("log ");
+	ptr += strlen(req->cmd->command);
+	ptr = skip_spaces(ptr);
 
-	len = req->len - (ptr - req->request.data);
-	printk(KERN_WARNING "%.*s", len, ptr);
+	len = req->request.len - (ptr - req->request.data);
+	if (len > 0)
+		printk(KERN_WARNING "%.*s", len, ptr);
 	mconsole_reply(req, "", 0, 0);
 }
 

---
base-commit: dd6c438c3e64a5ff0b5d7e78f7f9be547803ef1b
change-id: 20260425-mconsole-oob-read-leak-a4b8696ac97b

Best regards,
-- 
Shengzhuo Wei <me at cherr.cc>


From hch at lst.de  Tue Jun 16 22:44:56 2026
From: hch at lst.de (Christoph Hellwig)
Date: Wed, 17 Jun 2026 07:44:56 +0200
Subject: [PATCH v3] lib/raid/xor: x86: Add AVX-512 optimized xor_gen()
In-Reply-To: <a832eee3-55ec-4cf4-907f-346ff98870ca@intel.com>
References: <20260615190338.26581-1-ebiggers@kernel.org> <20260615201050.GB1764@quark> <255CAE3E-7FD3-4DC2-B3DE-46BE67EF22A8@alien8.de> <20260615212922.GA28589@quark> <20260615235318.GBajCQbuy9dBgKH8L_@fat_crate.local> <a832eee3-55ec-4cf4-907f-346ff98870ca@intel.com>
Message-ID: <20260617054456.GA19125@lst.de>

On Mon, Jun 15, 2026 at 05:29:58PM -0700, Dave Hansen wrote:
> On 6/15/26 16:53, Borislav Petkov wrote:
> > 
> >> In any case, I'd like these to go away:
> >>
> >>     $ git grep cpu_has_xfeatures | wc -l
> >>     31
> > Yeah, all in crypto. I can certainly see why.
> > 
> > @dhansen, any other thoughts?
> 
> If we can get rid of cpu_has_xfeatures(), I'm all for it. I'm not quite
> sure how the code would look so I'm reserving judgement until I see the
> patches. But it's worth a try.

I think the most important part is to be consistent.  Either use it
everywhere or not at all.


From xiyou.wangcong at gmail.com  Fri Jun 19 20:22:17 2026
From: xiyou.wangcong at gmail.com (Cong Wang)
Date: Fri, 19 Jun 2026 20:22:17 -0700
Subject: [PATCH 0/7] um: skas: harden the seccomp userspace stub
Message-ID: <cover.1781925126.git.xiyou.wangcong@gmail.com>

From: Cong Wang <cwang at multikernel.io>

In the seccomp ("SECCOMP") userspace mode, each guest userspace process
runs in a stub under a seccomp filter and traps to the monitor (the UML
kernel) on every syscall. Two items on the stub.c "Known security issues"
list could not be addressed by the filter alone:

  - a hijacked stub could mmap() arbitrary physmem offsets, which is an
    intra-guest disclosure and, on this base (single physmem fd, no
    kernel/user split), a host escape; and

  - a hijacked stub could block SIGALRM via a crafted rt_sigreturn to
    evade preemption and wedge the monitor indefinitely.

This series closes both:

  1-2: route the stub's mmap() through a SECCOMP_RET_USER_NOTIF listener
       owned by the monitor (no behavioural change yet).
  3-4: validate each mmap() against the mm's page table -- allowed iff the
       PTE already maps the requested frame with no more access than it
       grants -- including out-of-batch mmaps a hijacked stub issues on
       its own.
  5:   route and validate munmap() the same way (range-confined below
       STUB_START).
  6:   add a watchdog thread that detects a stub which stops reporting
       back (e.g. blocked SIGALRM) and SIGKILLs it, letting the monitor
       recover via the existing teardown.
  7:   drop the now-resolved "Known security issues" note and refresh the
       seccomp= help text.

After the series a hijacked stub is confined to the frames its own page
tables reference and can no longer reach arbitrary guest/host memory; one
that evades preemption is detected out of band and killed rather than
wedging the monitor.

Verified on UML (UP and 2-CPU SMP): boots and survives fork/exec storms
and heavy mmap/munmap churn with zero false denials or false kills; an
artificially SIGALRM-blocked busy loop is killed in ~5s and the monitor
recovers, while syscall-making processes are untouched. Each patch builds
and the series is bisectable.

---
Cong Wang (7):
  um: skas: create a seccomp USER_NOTIF listener and hand it to the
    monitor
  um: skas: gate stub mmap() through the USER_NOTIF monitor
  um: skas: validate stub mmap() against the guest page table
  um: skas: handle out-of-batch stub mmap notifications
  um: skas: validate stub munmap() against the guest address range
  um: skas: kill stubs that block SIGALRM via a watchdog thread
  um: skas: refresh stub security notes after closing the known issues

 arch/um/include/shared/skas/mm_id.h |   1 +
 arch/um/include/shared/skas/skas.h  |   5 +
 arch/um/kernel/skas/stub.c          |  22 --
 arch/um/kernel/skas/stub_exe.c      |  19 +-
 arch/um/kernel/skas/uaccess.c       |  48 +++++
 arch/um/os-Linux/skas/process.c     | 315 ++++++++++++++++++++++++----
 arch/um/os-Linux/start_up.c         |   6 -
 7 files changed, 344 insertions(+), 72 deletions(-)


base-commit: 1a3746ccbb0a97bed3c06ccde6b880013b1dddc1
-- 
2.43.0



From xiyou.wangcong at gmail.com  Fri Jun 19 20:22:18 2026
From: xiyou.wangcong at gmail.com (Cong Wang)
Date: Fri, 19 Jun 2026 20:22:18 -0700
Subject: [PATCH 1/7] um: skas: create a seccomp USER_NOTIF listener and hand it to the monitor
In-Reply-To: <cover.1781925126.git.xiyou.wangcong@gmail.com>
References: <cover.1781925126.git.xiyou.wangcong@gmail.com>
Message-ID: <11160b08c2b828d6684983502e7d6c8c88e5b230.1781925126.git.xiyou.wangcong@gmail.com>

From: Cong Wang <cwang at multikernel.io>

First step toward validating stub mmap() calls (the intra-guest /
host-escape disclosure issue in stub.c "Known security issues"): give the
monitor a SECCOMP_RET_USER_NOTIF listener for each stub's filter, so a
later change can route mmap to the monitor for per-call validation.

The stub installs its seccomp filter with NEW_LISTENER (plus TSYNC_ESRCH,
which the kernel requires to combine NEW_LISTENER with TSYNC); the
seccomp() return value is the listener fd. mmap still returns RET_ALLOW,
so there is no behavioural change yet.

The stub cannot hand the fd over itself: once the filter is installed,
every syscall it makes from outside the stub page traps with SIGSYS
instead of executing, so it can neither sendmsg() nor close() the fd.
Instead the monitor pulls it with pidfd_getfd(): the listener is, by
construction, fd 1 in the stub (close_range() left only fd 0 open before
seccomp() allocated it). Leaving the stub's copy open is harmless:
ioctl (NOTIF_RECV/SEND) is not on the syscall allowlist, so a hijacked
stub cannot self-approve. The monitor stores the fd in mm_id.

Assisted-by: Claude:claude-opus-4.8
Signed-off-by: Cong Wang <cwang at multikernel.io>
---
 arch/um/include/shared/skas/mm_id.h |  1 +
 arch/um/kernel/skas/stub_exe.c      | 12 ++++++---
 arch/um/os-Linux/skas/process.c     | 38 +++++++++++++++++++++++++++--
 3 files changed, 45 insertions(+), 6 deletions(-)

diff --git a/arch/um/include/shared/skas/mm_id.h b/arch/um/include/shared/skas/mm_id.h
index 18c0621430d2..46164d71554b 100644
--- a/arch/um/include/shared/skas/mm_id.h
+++ b/arch/um/include/shared/skas/mm_id.h
@@ -17,6 +17,7 @@ struct mm_id {
 
 	/* Only used with SECCOMP mode */
 	int sock;
+	int seccomp_notify_fd;
 	int syscall_fd_num;
 	int syscall_fd_map[STUB_MAX_FDS];
 };
diff --git a/arch/um/kernel/skas/stub_exe.c b/arch/um/kernel/skas/stub_exe.c
index cbafaa684e66..b5432f6ccbc7 100644
--- a/arch/um/kernel/skas/stub_exe.c
+++ b/arch/um/kernel/skas/stub_exe.c
@@ -196,10 +196,14 @@ noinline static void real_init(void)
 			.len = sizeof(filter) / sizeof(filter[0]),
 			.filter = filter,
 		};
-
-		if (stub_syscall3(__NR_seccomp, SECCOMP_SET_MODE_FILTER,
-				  SECCOMP_FILTER_FLAG_TSYNC,
-				  (unsigned long)&prog) != 0)
+		long listener;
+
+		listener = stub_syscall3(__NR_seccomp, SECCOMP_SET_MODE_FILTER,
+					 SECCOMP_FILTER_FLAG_TSYNC |
+					 SECCOMP_FILTER_FLAG_NEW_LISTENER |
+					 SECCOMP_FILTER_FLAG_TSYNC_ESRCH,
+					 (unsigned long)&prog);
+		if (listener < 0)
 			stub_syscall1(__NR_exit, 21);
 
 		/* Fall through, the exit syscall will cause SIGSYS */
diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c
index d6c22f8aa06d..0ab1d109a68d 100644
--- a/arch/um/os-Linux/skas/process.c
+++ b/arch/um/os-Linux/skas/process.c
@@ -428,6 +428,34 @@ __initcall(init_stub_exe_fd);
 
 int using_seccomp;
 
+/*
+ * Obtain the SECCOMP_RET_USER_NOTIF listener fd the stub created. The stub
+ * cannot hand it over itself: after installing the filter, every syscall it
+ * makes traps with SIGSYS rather than executing (so it can neither sendmsg()
+ * it nor close() it). Instead the listener is, by construction, the first free
+ * fd in the stub -- fd 1, since close_range() left only fd 0 open before
+ * seccomp() allocated it -- so the monitor duplicates it directly with
+ * pidfd_getfd(). ptrace_may_access() holds because the monitor is the stub's
+ * parent.
+ */
+#define STUB_LISTENER_FD 1
+static int get_stub_listener(struct mm_id *mm_id)
+{
+	int pidfd, lfd;
+
+	pidfd = syscall(__NR_pidfd_open, mm_id->pid, 0);
+	if (pidfd < 0)
+		return -errno;
+
+	lfd = syscall(__NR_pidfd_getfd, pidfd, STUB_LISTENER_FD, 0);
+	close(pidfd);
+	if (lfd < 0)
+		return -errno;
+
+	mm_id->seccomp_notify_fd = lfd;
+	return 0;
+}
+
 /**
  * start_userspace() - prepare a new userspace process
  * @mm_id: The corresponding struct mm_id
@@ -449,6 +477,8 @@ int start_userspace(struct mm_id *mm_id)
 	unsigned long sp;
 	int status, n, err;
 
+	mm_id->seccomp_notify_fd = -1;
+
 	/* setup a temporary stack page */
 	stack = mmap(NULL, UM_KERN_PAGE_SIZE,
 		     PROT_READ | PROT_WRITE | PROT_EXEC,
@@ -522,10 +552,14 @@ int start_userspace(struct mm_id *mm_id)
 	}
 
 	close(tramp_data.sockpair[0]);
-	if (using_seccomp)
+	if (using_seccomp) {
 		mm_id->sock = tramp_data.sockpair[1];
-	else
+		err = get_stub_listener(mm_id);
+		if (err)
+			goto out_kill;
+	} else {
 		close(tramp_data.sockpair[1]);
+	}
 
 	return 0;
 
-- 
2.43.0



From xiyou.wangcong at gmail.com  Fri Jun 19 20:22:19 2026
From: xiyou.wangcong at gmail.com (Cong Wang)
Date: Fri, 19 Jun 2026 20:22:19 -0700
Subject: [PATCH 2/7] um: skas: gate stub mmap() through the USER_NOTIF monitor
In-Reply-To: <cover.1781925126.git.xiyou.wangcong@gmail.com>
References: <cover.1781925126.git.xiyou.wangcong@gmail.com>
Message-ID: <b256f2d9655225ab723c766c76c2005703bebe38.1781925126.git.xiyou.wangcong@gmail.com>

From: Cong Wang <cwang at multikernel.io>

Route the stub's mmap to SECCOMP_RET_USER_NOTIF instead of RET_ALLOW, and
have the monitor service the resulting notifications inline. This is the
mechanism that a following change uses to validate each mmap's arguments;
for now every stub mmap is allowed (responded with
SECCOMP_USER_NOTIF_FLAG_CONTINUE), so behaviour is unchanged.

CONTINUE is safe for mmap: its arguments are all scalars captured in
seccomp_data, so there is no TOCTOU re-read of user memory.

The stub runs queued mmap batches in two places: syscall_stub_flush()
and the userspace() resume path, both of which wake the stub via
wait_stub_done_seccomp(). Servicing therefore lives there: after waking,
the monitor issues one NOTIF_RECV/CONTINUE per STUB_SYSCALL_MMAP in the
batch the stub is about to run. Signals are masked while the stub
executes the batch inside its SIGSYS handler, so notifications arrive in
queued order with nothing interleaved, and a simple counted loop is
sufficient. The pre-existing wake logic is factored into
wake_seccomp_stub() so both the wake and the wait-only paths share it.

Verified on UML: guest boots and survives a fork/exec storm plus heavy
demand paging with every stub mmap round-tripping through the monitor.

Assisted-by: Claude:claude-opus-4.8
Signed-off-by: Cong Wang <cwang at multikernel.io>
---
 arch/um/kernel/skas/stub_exe.c  |   5 +-
 arch/um/os-Linux/skas/process.c | 121 ++++++++++++++++++++++----------
 2 files changed, 89 insertions(+), 37 deletions(-)

diff --git a/arch/um/kernel/skas/stub_exe.c b/arch/um/kernel/skas/stub_exe.c
index b5432f6ccbc7..65ea2af5ca73 100644
--- a/arch/um/kernel/skas/stub_exe.c
+++ b/arch/um/kernel/skas/stub_exe.c
@@ -173,7 +173,7 @@ noinline static void real_init(void)
 			BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,__NR_close,
 				 5, 0),
 			BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, STUB_MMAP_NR,
-				 4, 0),
+				 5, 0),
 			BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_munmap,
 				 3, 0),
 #ifdef __i386__
@@ -191,6 +191,9 @@ noinline static void real_init(void)
 
 			/* [18] Permitted call for the stub */
 			BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
+
+			/* [19] mmap: route to the monitor for validation */
+			BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF),
 		};
 		struct sock_fprog prog = {
 			.len = sizeof(filter) / sizeof(filter[0]),
diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c
index 0ab1d109a68d..63b426b2c523 100644
--- a/arch/um/os-Linux/skas/process.c
+++ b/arch/um/os-Linux/skas/process.c
@@ -29,7 +29,9 @@
 #include <sysdep/stub.h>
 #include <sysdep/mcontext.h>
 #include <linux/futex.h>
+#include <linux/seccomp.h>
 #include <linux/threads.h>
+#include <sys/ioctl.h>
 #include <timetravel.h>
 #include <asm-generic/rwonce.h>
 #include "../internal.h"
@@ -147,49 +149,89 @@ void wait_stub_done(int pid)
 	fatal_sigsegv();
 }
 
+static void wake_seccomp_stub(struct mm_id *mm_idp)
+{
+	struct stub_data *data = (void *)mm_idp->stack;
+	const char byte = 0;
+	struct iovec iov = {
+		.iov_base = (void *)&byte,
+		.iov_len = sizeof(byte),
+	};
+	union {
+		char data[CMSG_SPACE(sizeof(mm_idp->syscall_fd_map))];
+		struct cmsghdr align;
+	} ctrl;
+	struct msghdr msgh = {
+		.msg_iov = &iov,
+		.msg_iovlen = 1,
+	};
+
+	if (mm_idp->syscall_fd_num) {
+		unsigned int fds_size = sizeof(int) * mm_idp->syscall_fd_num;
+		struct cmsghdr *cmsg;
+
+		msgh.msg_control = ctrl.data;
+		msgh.msg_controllen = CMSG_SPACE(fds_size);
+		cmsg = CMSG_FIRSTHDR(&msgh);
+		cmsg->cmsg_level = SOL_SOCKET;
+		cmsg->cmsg_type = SCM_RIGHTS;
+		cmsg->cmsg_len = CMSG_LEN(fds_size);
+		memcpy(CMSG_DATA(cmsg), mm_idp->syscall_fd_map, fds_size);
+
+		CATCH_EINTR(syscall(__NR_sendmsg, mm_idp->sock, &msgh, 0));
+	}
+
+	data->signal = 0;
+	data->futex = FUTEX_IN_CHILD;
+	CATCH_EINTR(syscall(__NR_futex, &data->futex,
+			    FUTEX_WAKE, 1, NULL, NULL, 0));
+}
+
+static int seccomp_notify_serve(int notify_fd)
+{
+	struct seccomp_notif req = {};
+	struct seccomp_notif_resp resp = {};
+	int ret;
+
+	CATCH_EINTR(ret = ioctl(notify_fd, SECCOMP_IOCTL_NOTIF_RECV, &req));
+	if (ret < 0)
+		return -errno;
+
+	resp.id = req.id;
+	resp.flags = SECCOMP_USER_NOTIF_FLAG_CONTINUE;
+
+	CATCH_EINTR(ret = ioctl(notify_fd, SECCOMP_IOCTL_NOTIF_SEND, &resp));
+	if (ret < 0)
+		return -errno;
+
+	return 0;
+}
+
+static void seccomp_serve_mmaps(struct mm_id *mm_idp)
+{
+	struct stub_data *data = (void *)mm_idp->stack;
+	int i, n_mmaps = 0;
+
+	if (mm_idp->seccomp_notify_fd < 0)
+		return;
+
+	for (i = 0; i < data->syscall_data_len; i++)
+		if (data->syscall_data[i].syscall == STUB_SYSCALL_MMAP)
+			n_mmaps++;
+
+	for (i = 0; i < n_mmaps; i++)
+		seccomp_notify_serve(mm_idp->seccomp_notify_fd);
+}
+
 void wait_stub_done_seccomp(struct mm_id *mm_idp, int running, int wait_sigsys)
 {
 	struct stub_data *data = (void *)mm_idp->stack;
 	int ret;
 
 	do {
-		const char byte = 0;
-		struct iovec iov = {
-			.iov_base = (void *)&byte,
-			.iov_len = sizeof(byte),
-		};
-		union {
-			char data[CMSG_SPACE(sizeof(mm_idp->syscall_fd_map))];
-			struct cmsghdr align;
-		} ctrl;
-		struct msghdr msgh = {
-			.msg_iov = &iov,
-			.msg_iovlen = 1,
-		};
-
 		if (!running) {
-			if (mm_idp->syscall_fd_num) {
-				unsigned int fds_size =
-					sizeof(int) * mm_idp->syscall_fd_num;
-				struct cmsghdr *cmsg;
-
-				msgh.msg_control = ctrl.data;
-				msgh.msg_controllen = CMSG_SPACE(fds_size);
-				cmsg = CMSG_FIRSTHDR(&msgh);
-				cmsg->cmsg_level = SOL_SOCKET;
-				cmsg->cmsg_type = SCM_RIGHTS;
-				cmsg->cmsg_len = CMSG_LEN(fds_size);
-				memcpy(CMSG_DATA(cmsg), mm_idp->syscall_fd_map,
-				       fds_size);
-
-				CATCH_EINTR(syscall(__NR_sendmsg, mm_idp->sock,
-						&msgh, 0));
-			}
-
-			data->signal = 0;
-			data->futex = FUTEX_IN_CHILD;
-			CATCH_EINTR(syscall(__NR_futex, &data->futex,
-					    FUTEX_WAKE, 1, NULL, NULL, 0));
+			wake_seccomp_stub(mm_idp);
+			seccomp_serve_mmaps(mm_idp);
 		}
 
 		do {
@@ -246,6 +288,13 @@ void wait_stub_done_seccomp(struct mm_id *mm_idp, int running, int wait_sigsys)
 		fatal_sigsegv();
 }
 
+/*
+ * Service one SECCOMP_RET_USER_NOTIF notification from a stub mmap: read the
+ * suspended call, then respond CONTINUE so the stub's real mmap runs. CONTINUE
+ * is safe here because mmap takes only scalar arguments (no TOCTOU on user
+ * memory). Validation of (addr, len, prot, fd, offset) is added later; for now
+ * every stub mmap is allowed.
+ */
 extern unsigned long current_stub_stack(void);
 
 static void get_skas_faultinfo(int pid, struct faultinfo *fi)
-- 
2.43.0



From xiyou.wangcong at gmail.com  Fri Jun 19 20:22:20 2026
From: xiyou.wangcong at gmail.com (Cong Wang)
Date: Fri, 19 Jun 2026 20:22:20 -0700
Subject: [PATCH 3/7] um: skas: validate stub mmap() against the guest page table
In-Reply-To: <cover.1781925126.git.xiyou.wangcong@gmail.com>
References: <cover.1781925126.git.xiyou.wangcong@gmail.com>
Message-ID: <7c38eec25d3ddc70893f1debc415d613dcf90a3b.1781925126.git.xiyou.wangcong@gmail.com>

From: Cong Wang <cwang at multikernel.io>

Replace the allow-all USER_NOTIF response with the actual security check.
When a stub mmap traps to the monitor, validate its arguments against the
mm's page table: the call is allowed iff this mm's PTE for the target
address maps exactly the physical frame named by the mmap offset, and the
requested protection does not exceed what the PTE grants. Otherwise the
mmap is rejected with -EPERM.

This is what closes the disclosure issue documented in stub.c. A hijacked
stub (jumping to the in-stub mmap with crafted registers) can no longer
choose an arbitrary physmem offset: it is confined to the frames its own
mm's PTEs already reference. On this base (single physmem fd, no
kernel/user split) the same check also blocks host escape: a mapping of
a UML-kernel frame has no authorizing user PTE, so it is denied.

The check is a pure function of state the monitor already owns (it *is*
the UML kernel and holds the guest pgd, freshly synced before the stub
mmap is issued), so it needs no per-batch bookkeeping or fd-identity
tracking. stub_mmap_allowed() lives in skas/uaccess.c next to
virt_to_pte(); the os-Linux notify handler calls it and responds CONTINUE
or -EPERM.

Verified on UML: guest boots and survives a fork/exec storm plus heavy
demand paging with zero false denials.

Assisted-by: Claude:claude-opus-4.8
Signed-off-by: Cong Wang <cwang at multikernel.io>
---
 arch/um/include/shared/skas/skas.h |  3 +++
 arch/um/kernel/skas/uaccess.c      | 36 ++++++++++++++++++++++++++++++
 arch/um/os-Linux/skas/process.c    | 21 ++++++++---------
 3 files changed, 50 insertions(+), 10 deletions(-)

diff --git a/arch/um/include/shared/skas/skas.h b/arch/um/include/shared/skas/skas.h
index 2237ffedec75..ce1b67b06b4b 100644
--- a/arch/um/include/shared/skas/skas.h
+++ b/arch/um/include/shared/skas/skas.h
@@ -18,4 +18,7 @@ extern void current_mm_sync(void);
 void initial_jmpbuf_lock(void);
 void initial_jmpbuf_unlock(void);
 
+int stub_mmap_allowed(struct mm_id *id, unsigned long addr,
+		      unsigned long prot, unsigned long offset);
+
 #endif
diff --git a/arch/um/kernel/skas/uaccess.c b/arch/um/kernel/skas/uaccess.c
index caef1deef795..9359ede8a04b 100644
--- a/arch/um/kernel/skas/uaccess.c
+++ b/arch/um/kernel/skas/uaccess.c
@@ -13,6 +13,18 @@
 #include <kern_util.h>
 #include <asm/futex.h>
 #include <os.h>
+#include <skas.h>
+
+/*
+ * Same mapping as MMAP_OFFSET() in <sysdep/stub.h>, but usable from kernel
+ * code (that header pulls in the host <sys/mman.h>). 64-bit stubs use mmap()
+ * with a byte offset; 32-bit stubs use mmap2() with a page offset.
+ */
+#ifdef CONFIG_64BIT
+#define stub_mmap_offset(phys)	(phys)
+#else
+#define stub_mmap_offset(phys)	((phys) >> PAGE_SHIFT)
+#endif
 
 pte_t *virt_to_pte(struct mm_struct *mm, unsigned long addr)
 {
@@ -43,6 +55,30 @@ pte_t *virt_to_pte(struct mm_struct *mm, unsigned long addr)
 	return pte_offset_kernel(pmd, addr);
 }
 
+int stub_mmap_allowed(struct mm_id *id, unsigned long addr,
+		      unsigned long prot, unsigned long offset)
+{
+	struct mm_context *ctx = container_of(id, struct mm_context, id);
+	struct mm_struct *mm = container_of(ctx, struct mm_struct, context);
+	pte_t *pte;
+
+	pte = virt_to_pte(mm, addr);
+	if (pte == NULL || !pte_present(*pte))
+		return 0;
+
+	/* Must map exactly the frame this PTE references. */
+	if (stub_mmap_offset(pte_val(*pte) & PAGE_MASK) != offset)
+		return 0;
+
+	/* Must not grant more access than the PTE allows. */
+	if ((prot & UM_PROT_WRITE) && !pte_write(*pte))
+		return 0;
+	if ((prot & UM_PROT_EXEC) && !pte_exec(*pte))
+		return 0;
+
+	return 1;
+}
+
 static pte_t *maybe_map(unsigned long virt, int is_write)
 {
 	pte_t *pte = virt_to_pte(current->mm, virt);
diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c
index 63b426b2c523..3a31e52cdcf8 100644
--- a/arch/um/os-Linux/skas/process.c
+++ b/arch/um/os-Linux/skas/process.c
@@ -187,10 +187,11 @@ static void wake_seccomp_stub(struct mm_id *mm_idp)
 			    FUTEX_WAKE, 1, NULL, NULL, 0));
 }
 
-static int seccomp_notify_serve(int notify_fd)
+static int seccomp_notify_serve(struct mm_id *mm_idp)
 {
 	struct seccomp_notif req = {};
 	struct seccomp_notif_resp resp = {};
+	int notify_fd = mm_idp->seccomp_notify_fd;
 	int ret;
 
 	CATCH_EINTR(ret = ioctl(notify_fd, SECCOMP_IOCTL_NOTIF_RECV, &req));
@@ -198,7 +199,14 @@ static int seccomp_notify_serve(int notify_fd)
 		return -errno;
 
 	resp.id = req.id;
-	resp.flags = SECCOMP_USER_NOTIF_FLAG_CONTINUE;
+
+	if (req.data.nr == STUB_MMAP_NR &&
+	    stub_mmap_allowed(mm_idp, req.data.args[0], req.data.args[2],
+			      req.data.args[5])) {
+		resp.flags = SECCOMP_USER_NOTIF_FLAG_CONTINUE;
+	} else {
+		resp.error = -EPERM;
+	}
 
 	CATCH_EINTR(ret = ioctl(notify_fd, SECCOMP_IOCTL_NOTIF_SEND, &resp));
 	if (ret < 0)
@@ -220,7 +228,7 @@ static void seccomp_serve_mmaps(struct mm_id *mm_idp)
 			n_mmaps++;
 
 	for (i = 0; i < n_mmaps; i++)
-		seccomp_notify_serve(mm_idp->seccomp_notify_fd);
+		seccomp_notify_serve(mm_idp);
 }
 
 void wait_stub_done_seccomp(struct mm_id *mm_idp, int running, int wait_sigsys)
@@ -288,13 +296,6 @@ void wait_stub_done_seccomp(struct mm_id *mm_idp, int running, int wait_sigsys)
 		fatal_sigsegv();
 }
 
-/*
- * Service one SECCOMP_RET_USER_NOTIF notification from a stub mmap: read the
- * suspended call, then respond CONTINUE so the stub's real mmap runs. CONTINUE
- * is safe here because mmap takes only scalar arguments (no TOCTOU on user
- * memory). Validation of (addr, len, prot, fd, offset) is added later; for now
- * every stub mmap is allowed.
- */
 extern unsigned long current_stub_stack(void);
 
 static void get_skas_faultinfo(int pid, struct faultinfo *fi)
-- 
2.43.0



From xiyou.wangcong at gmail.com  Fri Jun 19 20:22:21 2026
From: xiyou.wangcong at gmail.com (Cong Wang)
Date: Fri, 19 Jun 2026 20:22:21 -0700
Subject: [PATCH 4/7] um: skas: handle out-of-batch stub mmap notifications
In-Reply-To: <cover.1781925126.git.xiyou.wangcong@gmail.com>
References: <cover.1781925126.git.xiyou.wangcong@gmail.com>
Message-ID: <599277ebecd4c7896c475553797d6969fd793efb.1781925126.git.xiyou.wangcong@gmail.com>

From: Cong Wang <cwang at multikernel.io>

The mmap validation so far only runs for mmaps the monitor itself queued.
A hijacked stub can instead jump to the in-stub mmap on its own, outside
any batch. Such an mmap traps to the USER_NOTIF listener and suspends
in-kernel (so it discloses nothing) but it never signals the monitor
via the futex, so the monitor, waiting only on the futex, would block
forever (a guest-triggered DoS of the whole UML).

Drain the listener whenever the wait wakes: after each FUTEX_WAIT in
wait_stub_done_seccomp() (which already wakes periodically on SIGALRM and
tolerates EINTR), poll the listener non-blocking and service anything
pending. An out-of-batch mmap therefore stays suspended only until the
next wakeup and is then validated like any other: denied with -EPERM
unless it maps exactly what its PTE allows. No disclosure occurs while it
is pending, and the monitor no longer hangs.

Assisted-by: Claude:claude-opus-4.8
Signed-off-by: Cong Wang <cwang at multikernel.io>
---
 arch/um/os-Linux/skas/process.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c
index 3a31e52cdcf8..0987eb79ce76 100644
--- a/arch/um/os-Linux/skas/process.c
+++ b/arch/um/os-Linux/skas/process.c
@@ -32,6 +32,7 @@
 #include <linux/seccomp.h>
 #include <linux/threads.h>
 #include <sys/ioctl.h>
+#include <poll.h>
 #include <timetravel.h>
 #include <asm-generic/rwonce.h>
 #include "../internal.h"
@@ -215,6 +216,21 @@ static int seccomp_notify_serve(struct mm_id *mm_idp)
 	return 0;
 }
 
+static void seccomp_notify_drain(struct mm_id *mm_idp)
+{
+	struct pollfd pfd = {
+		.fd = mm_idp->seccomp_notify_fd,
+		.events = POLLIN,
+	};
+
+	if (mm_idp->seccomp_notify_fd < 0)
+		return;
+
+	while (poll(&pfd, 1, 0) > 0 && (pfd.revents & POLLIN))
+		if (seccomp_notify_serve(mm_idp) < 0)
+			break;
+}
+
 static void seccomp_serve_mmaps(struct mm_id *mm_idp)
 {
 	struct stub_data *data = (void *)mm_idp->stack;
@@ -265,6 +281,9 @@ void wait_stub_done_seccomp(struct mm_id *mm_idp, int running, int wait_sigsys)
 				       __func__, errno);
 				goto out_kill;
 			}
+
+			/* Handle any stub mmap that trapped out of band. */
+			seccomp_notify_drain(mm_idp);
 		} while (data->futex == FUTEX_IN_CHILD);
 
 		if (__READ_ONCE(mm_idp->pid) < 0)
-- 
2.43.0



From xiyou.wangcong at gmail.com  Fri Jun 19 20:22:22 2026
From: xiyou.wangcong at gmail.com (Cong Wang)
Date: Fri, 19 Jun 2026 20:22:22 -0700
Subject: [PATCH 5/7] um: skas: validate stub munmap() against the guest address range
In-Reply-To: <cover.1781925126.git.xiyou.wangcong@gmail.com>
References: <cover.1781925126.git.xiyou.wangcong@gmail.com>
Message-ID: <79317fd985294cb54ec14aa6ae8837bc99b5f4cb.1781925126.git.xiyou.wangcong@gmail.com>

From: Cong Wang <cwang at multikernel.io>

Route stub munmap() through the USER_NOTIF monitor too, and validate it
before letting it run. munmap() was previously SECCOMP_RET_ALLOW, so a
hijacked stub (jumping to the in-stub munmap with crafted registers) could
unmap arbitrary ranges, including the stub's own code/data pages, which
would sever the monitor's control over it, or guest mappings outside what
it is allowed to manage. After mmap, munmap was the remaining memory
primitive a hijacked stub could invoke with arbitrary arguments.

Unlike mmap(), there is no PTE left to check: by the time the stub unmaps
a guest page the kernel has already cleared the corresponding entry. So
stub_munmap_allowed() is range-based instead: the request must be
non-empty, must not wrap, and must lie entirely below STUB_START. That
confines the stub to the guest address space and keeps its own reserved
region off-limits. Both arguments are scalars captured in seccomp_data, so
CONTINUE carries no TOCTOU risk, same as mmap().

stub_munmap_allowed() lives in skas/uaccess.c next to stub_mmap_allowed();
the os-Linux notify handler dispatches on the syscall number and responds
CONTINUE or -EPERM, and the batch server counts STUB_SYSCALL_MUNMAP as
well as STUB_SYSCALL_MMAP.

Verified on UML: guest boots and survives heavy mmap/munmap churn with
zero false denials; the legitimate boot-time clear of the whole user
address space [0, STUB_START) is allowed (end == STUB_START), while a
range overlapping the stub region is denied.

Assisted-by: Claude:claude-opus-4.8
Signed-off-by: Cong Wang <cwang at multikernel.io>
---
 arch/um/include/shared/skas/skas.h |  2 ++
 arch/um/kernel/skas/stub_exe.c     |  4 ++--
 arch/um/kernel/skas/uaccess.c      | 12 ++++++++++++
 arch/um/os-Linux/skas/process.c    | 31 ++++++++++++++++++------------
 4 files changed, 35 insertions(+), 14 deletions(-)

diff --git a/arch/um/include/shared/skas/skas.h b/arch/um/include/shared/skas/skas.h
index ce1b67b06b4b..ca2a62cef0c1 100644
--- a/arch/um/include/shared/skas/skas.h
+++ b/arch/um/include/shared/skas/skas.h
@@ -20,5 +20,7 @@ void initial_jmpbuf_unlock(void);
 
 int stub_mmap_allowed(struct mm_id *id, unsigned long addr,
 		      unsigned long prot, unsigned long offset);
+int stub_munmap_allowed(struct mm_id *id, unsigned long addr,
+			unsigned long len);
 
 #endif
diff --git a/arch/um/kernel/skas/stub_exe.c b/arch/um/kernel/skas/stub_exe.c
index 65ea2af5ca73..00eea0cb9463 100644
--- a/arch/um/kernel/skas/stub_exe.c
+++ b/arch/um/kernel/skas/stub_exe.c
@@ -175,7 +175,7 @@ noinline static void real_init(void)
 			BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, STUB_MMAP_NR,
 				 5, 0),
 			BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_munmap,
-				 3, 0),
+				 4, 0),
 #ifdef __i386__
 			BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_set_thread_area,
 				 2, 0),
@@ -192,7 +192,7 @@ noinline static void real_init(void)
 			/* [18] Permitted call for the stub */
 			BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
 
-			/* [19] mmap: route to the monitor for validation */
+			/* [19] mmap and munmap: route to the monitor for validation */
 			BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF),
 		};
 		struct sock_fprog prog = {
diff --git a/arch/um/kernel/skas/uaccess.c b/arch/um/kernel/skas/uaccess.c
index 9359ede8a04b..feb267637735 100644
--- a/arch/um/kernel/skas/uaccess.c
+++ b/arch/um/kernel/skas/uaccess.c
@@ -14,6 +14,7 @@
 #include <asm/futex.h>
 #include <os.h>
 #include <skas.h>
+#include <as-layout.h>
 
 /*
  * Same mapping as MMAP_OFFSET() in <sysdep/stub.h>, but usable from kernel
@@ -79,6 +80,17 @@ int stub_mmap_allowed(struct mm_id *id, unsigned long addr,
 	return 1;
 }
 
+int stub_munmap_allowed(struct mm_id *id, unsigned long addr, unsigned long len)
+{
+	if (len == 0 || addr + len < addr)
+		return 0;
+
+	if (addr + len > STUB_START)
+		return 0;
+
+	return 1;
+}
+
 static pte_t *maybe_map(unsigned long virt, int is_write)
 {
 	pte_t *pte = virt_to_pte(current->mm, virt);
diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c
index 0987eb79ce76..2010b4529c41 100644
--- a/arch/um/os-Linux/skas/process.c
+++ b/arch/um/os-Linux/skas/process.c
@@ -193,7 +193,7 @@ static int seccomp_notify_serve(struct mm_id *mm_idp)
 	struct seccomp_notif req = {};
 	struct seccomp_notif_resp resp = {};
 	int notify_fd = mm_idp->seccomp_notify_fd;
-	int ret;
+	int allowed, ret;
 
 	CATCH_EINTR(ret = ioctl(notify_fd, SECCOMP_IOCTL_NOTIF_RECV, &req));
 	if (ret < 0)
@@ -201,13 +201,19 @@ static int seccomp_notify_serve(struct mm_id *mm_idp)
 
 	resp.id = req.id;
 
-	if (req.data.nr == STUB_MMAP_NR &&
-	    stub_mmap_allowed(mm_idp, req.data.args[0], req.data.args[2],
-			      req.data.args[5])) {
+	if (req.data.nr == STUB_MMAP_NR)
+		allowed = stub_mmap_allowed(mm_idp, req.data.args[0],
+					    req.data.args[2], req.data.args[5]);
+	else if (req.data.nr == __NR_munmap)
+		allowed = stub_munmap_allowed(mm_idp, req.data.args[0],
+					      req.data.args[1]);
+	else
+		allowed = 0;
+
+	if (allowed)
 		resp.flags = SECCOMP_USER_NOTIF_FLAG_CONTINUE;
-	} else {
+	else
 		resp.error = -EPERM;
-	}
 
 	CATCH_EINTR(ret = ioctl(notify_fd, SECCOMP_IOCTL_NOTIF_SEND, &resp));
 	if (ret < 0)
@@ -231,19 +237,20 @@ static void seccomp_notify_drain(struct mm_id *mm_idp)
 			break;
 }
 
-static void seccomp_serve_mmaps(struct mm_id *mm_idp)
+static void seccomp_serve_stub_syscalls(struct mm_id *mm_idp)
 {
 	struct stub_data *data = (void *)mm_idp->stack;
-	int i, n_mmaps = 0;
+	int i, n_notif = 0;
 
 	if (mm_idp->seccomp_notify_fd < 0)
 		return;
 
 	for (i = 0; i < data->syscall_data_len; i++)
-		if (data->syscall_data[i].syscall == STUB_SYSCALL_MMAP)
-			n_mmaps++;
+		if (data->syscall_data[i].syscall == STUB_SYSCALL_MMAP ||
+		    data->syscall_data[i].syscall == STUB_SYSCALL_MUNMAP)
+			n_notif++;
 
-	for (i = 0; i < n_mmaps; i++)
+	for (i = 0; i < n_notif; i++)
 		seccomp_notify_serve(mm_idp);
 }
 
@@ -255,7 +262,7 @@ void wait_stub_done_seccomp(struct mm_id *mm_idp, int running, int wait_sigsys)
 	do {
 		if (!running) {
 			wake_seccomp_stub(mm_idp);
-			seccomp_serve_mmaps(mm_idp);
+			seccomp_serve_stub_syscalls(mm_idp);
 		}
 
 		do {
-- 
2.43.0



From xiyou.wangcong at gmail.com  Fri Jun 19 20:22:23 2026
From: xiyou.wangcong at gmail.com (Cong Wang)
Date: Fri, 19 Jun 2026 20:22:23 -0700
Subject: [PATCH 6/7] um: skas: kill stubs that block SIGALRM via a watchdog thread
In-Reply-To: <cover.1781925126.git.xiyou.wangcong@gmail.com>
References: <cover.1781925126.git.xiyou.wangcong@gmail.com>
Message-ID: <7c7c19f9f70e874f071c3ea66957d56e0ea2648d.1781925126.git.xiyou.wangcong@gmail.com>

From: Cong Wang <cwang at multikernel.io>

A hijacked stub can block SIGALRM with a crafted rt_sigreturn (the mask is
restored from the stack it controls), so it is never preempted and never
reports back. SIGALRM goes to the stub, not the monitor, so the monitor
then blocks indefinitely in wait_stub_done_seccomp(). This is the one item
("blocking e.g. SIGALRM") of the stub.c "Known security issues" list that a
seccomp filter cannot address, since rt_sigreturn is required and its
effect lives on the stack rather than in register arguments.

Detect it out of band. A helper thread blocks on its own timerfd and
watches a per-vCPU (pid, seq) pair the monitor updates around each wait:
pid is the stub being waited on, seq advances every time the stub reports.
A stub that stops reporting leaves pid pinned and seq frozen; after
SECCOMP_WD_STALL_TICKS ticks of no progress the watchdog SIGKILLs it, and
the resulting SIGCHLD unblocks the monitor through the existing "pid < 0"
teardown.

Each monitor writes only its own slot and the watchdog only reads, so the
word-sized state needs just __READ_ONCE()/__WRITE_ONCE(), no lock; the
watchdog scans every slot, covering all CPUs under SMP. A false kill cannot
happen without the same pid and an unchanged seq across many ticks. The
thread runs with all signals blocked (os_run_helper_thread()), uses
write(2) rather than printk() from its non-kernel context, and is started
once via a compare-and-swap guard. It is preferred over a bounded
FUTEX_WAIT timeout: it costs one counter bump on the per-syscall hot path
and catches a stall anywhere on stub input, not just the one futex.

Verified on UML (UP and 2-CPU SMP): heavy mmap/munmap churn and CPU-bound
loops on every vCPU run with zero false kills; a stub with SIGALRM blocked
is killed in ~5s and the monitor recovers, while syscall-making processes
are untouched.

Assisted-by: Claude:claude-opus-4.8
Signed-off-by: Cong Wang <cwang at multikernel.io>
---
 arch/um/os-Linux/skas/process.c | 129 ++++++++++++++++++++++++++++++++
 1 file changed, 129 insertions(+)

diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c
index 2010b4529c41..7ffde2b00b61 100644
--- a/arch/um/os-Linux/skas/process.c
+++ b/arch/um/os-Linux/skas/process.c
@@ -21,6 +21,7 @@
 #include <as-layout.h>
 #include <init.h>
 #include <kern_util.h>
+#include <smp.h>
 #include <mem.h>
 #include <os.h>
 #include <ptrace_user.h>
@@ -33,6 +34,8 @@
 #include <linux/threads.h>
 #include <sys/ioctl.h>
 #include <poll.h>
+#include <signal.h>
+#include <sys/timerfd.h>
 #include <timetravel.h>
 #include <asm-generic/rwonce.h>
 #include "../internal.h"
@@ -254,11 +257,131 @@ static void seccomp_serve_stub_syscalls(struct mm_id *mm_idp)
 		seccomp_notify_serve(mm_idp);
 }
 
+#define SECCOMP_WD_TICK_SECS	1
+#define SECCOMP_WD_STALL_TICKS	5	/* ~5s of no progress before killing */
+
+static int seccomp_wd_pid[CONFIG_NR_CPUS] = { [0 ... CONFIG_NR_CPUS - 1] = -1 };
+static unsigned long seccomp_wd_seq[CONFIG_NR_CPUS];
+
+static inline void seccomp_wd_enter(int pid)
+{
+	int cpu = uml_curr_cpu();
+
+	__WRITE_ONCE(seccomp_wd_seq[cpu], seccomp_wd_seq[cpu] + 1);
+	__WRITE_ONCE(seccomp_wd_pid[cpu], pid);
+}
+
+static inline void seccomp_wd_progress(void)
+{
+	int cpu = uml_curr_cpu();
+
+	__WRITE_ONCE(seccomp_wd_seq[cpu], seccomp_wd_seq[cpu] + 1);
+}
+
+static inline void seccomp_wd_exit(void)
+{
+	__WRITE_ONCE(seccomp_wd_pid[uml_curr_cpu()], -1);
+}
+
+/* Per-CPU snapshot the watchdog compares against the next tick. */
+struct seccomp_wd_cpu {
+	int prev_pid;
+	unsigned long prev_seq;
+	int stall;
+};
+
+static void seccomp_wd_check_cpu(int cpu, struct seccomp_wd_cpu *st)
+{
+	static const char kill_msg[] =
+		"seccomp watchdog: killing unresponsive stub (SIGALRM blocked?)\n";
+	int pid = __READ_ONCE(seccomp_wd_pid[cpu]);
+	unsigned long seq = __READ_ONCE(seccomp_wd_seq[cpu]);
+
+	if (pid >= 0 && pid == st->prev_pid && seq == st->prev_seq) {
+		if (++st->stall >= SECCOMP_WD_STALL_TICKS) {
+			/* printk() is unsafe from this thread. */
+			(void)!write(2, kill_msg, sizeof(kill_msg) - 1);
+			kill(pid, SIGKILL);
+			st->stall = 0;
+		}
+	} else {
+		st->stall = 0;
+	}
+
+	st->prev_pid = pid;
+	st->prev_seq = seq;
+}
+
+static void *seccomp_watchdog(void *arg)
+{
+	int tfd = (int)(long)arg;
+	struct seccomp_wd_cpu st[CONFIG_NR_CPUS];
+	int cpu;
+
+	for (cpu = 0; cpu < CONFIG_NR_CPUS; cpu++)
+		st[cpu] = (struct seccomp_wd_cpu){ .prev_pid = -1 };
+
+	for (;;) {
+		unsigned long long expirations;
+
+		/*
+		 * One check per wakeup; ignore the expiration count so a
+		 * descheduled watchdog accrues stalls more slowly, never faster.
+		 */
+		if (read(tfd, &expirations, sizeof(expirations)) !=
+		    sizeof(expirations))
+			continue;
+
+		for (cpu = 0; cpu < CONFIG_NR_CPUS; cpu++)
+			seccomp_wd_check_cpu(cpu, &st[cpu]);
+	}
+
+	return NULL;
+}
+
+static void start_seccomp_watchdog(void)
+{
+	static int started;
+	static struct os_helper_thread *wd_td;
+	struct itimerspec its = {
+		.it_value    = { .tv_sec = SECCOMP_WD_TICK_SECS },
+		.it_interval = { .tv_sec = SECCOMP_WD_TICK_SECS },
+	};
+	int tfd;
+
+	/* Several vCPU monitors may race here; only the first starts the thread. */
+	if (!__sync_bool_compare_and_swap(&started, 0, 1))
+		return;
+
+	tfd = timerfd_create(CLOCK_MONOTONIC, 0);
+	if (tfd < 0) {
+		printk(UM_KERN_ERR "%s : timerfd_create failed, errno = %d\n",
+		       __func__, errno);
+		return;
+	}
+
+	if (timerfd_settime(tfd, 0, &its, NULL) < 0) {
+		printk(UM_KERN_ERR "%s : timerfd_settime failed, errno = %d\n",
+		       __func__, errno);
+		close(tfd);
+		return;
+	}
+
+	if (os_run_helper_thread(&wd_td, seccomp_watchdog, (void *)(long)tfd) < 0) {
+		printk(UM_KERN_ERR "%s : failed to start watchdog thread\n",
+		       __func__);
+		close(tfd);
+		wd_td = NULL;
+	}
+}
+
 void wait_stub_done_seccomp(struct mm_id *mm_idp, int running, int wait_sigsys)
 {
 	struct stub_data *data = (void *)mm_idp->stack;
 	int ret;
 
+	seccomp_wd_enter(mm_idp->pid);
+
 	do {
 		if (!running) {
 			wake_seccomp_stub(mm_idp);
@@ -296,6 +419,9 @@ void wait_stub_done_seccomp(struct mm_id *mm_idp, int running, int wait_sigsys)
 		if (__READ_ONCE(mm_idp->pid) < 0)
 			goto out_kill;
 
+		/* The stub reported back: record progress for the watchdog. */
+		seccomp_wd_progress();
+
 		running = 0;
 
 		/* We may receive a SIGALRM before SIGSYS, iterate again. */
@@ -312,9 +438,11 @@ void wait_stub_done_seccomp(struct mm_id *mm_idp, int running, int wait_sigsys)
 		goto out_kill;
 	}
 
+	seccomp_wd_exit();
 	return;
 
 out_kill:
+	seccomp_wd_exit();
 	printk(UM_KERN_ERR "%s : failed to wait for stub, pid = %d, errno = %d\n",
 	       __func__, mm_idp->pid, errno);
 	/* This is not true inside start_userspace */
@@ -633,6 +761,7 @@ int start_userspace(struct mm_id *mm_id)
 		err = get_stub_listener(mm_id);
 		if (err)
 			goto out_kill;
+		start_seccomp_watchdog();
 	} else {
 		close(tramp_data.sockpair[1]);
 	}
-- 
2.43.0



From xiyou.wangcong at gmail.com  Fri Jun 19 20:22:24 2026
From: xiyou.wangcong at gmail.com (Cong Wang)
Date: Fri, 19 Jun 2026 20:22:24 -0700
Subject: [PATCH 7/7] um: skas: refresh stub security notes after closing the known issues
In-Reply-To: <cover.1781925126.git.xiyou.wangcong@gmail.com>
References: <cover.1781925126.git.xiyou.wangcong@gmail.com>
Message-ID: <d54d4b5fc21b2f3b027c73876292c07bdad58e3e.1781925126.git.xiyou.wangcong@gmail.com>

From: Cong Wang <cwang at multikernel.io>

Drop the stale comment and update the seccomp= setup help text, which still
warned about these exact issues and labelled the mode "insecure".

Assisted-by: Claude:claude-opus-4.8
Signed-off-by: Cong Wang <cwang at multikernel.io>
---
 arch/um/kernel/skas/stub.c  | 22 ----------------------
 arch/um/os-Linux/start_up.c |  6 ------
 2 files changed, 28 deletions(-)

diff --git a/arch/um/kernel/skas/stub.c b/arch/um/kernel/skas/stub.c
index e09216a20cb5..7845638d595d 100644
--- a/arch/um/kernel/skas/stub.c
+++ b/arch/um/kernel/skas/stub.c
@@ -9,28 +9,6 @@
 #include <sys/socket.h>
 #include <errno.h>
 
-/*
- * Known security issues
- *
- * Userspace can jump to this address to execute *any* syscall that is
- * permitted by the stub. As we will return afterwards, it can do
- * whatever it likes, including:
- * - Tricking the kernel into handing out the memory FD
- * - Using this memory FD to read/write all physical memory
- * - Running in parallel to the kernel processing a syscall
- *   (possibly creating data races?)
- * - Blocking e.g. SIGALRM to avoid time based scheduling
- *
- * To avoid this, the permitted location for each syscall needs to be
- * checked for in the SECCOMP filter (which is reasonably simple). Also,
- * more care will need to go into considerations how the code might be
- * tricked by using a prepared stack (or even modifying the stack from
- * another thread in case SMP support is added).
- *
- * As for the SIGALRM, the best counter measure will be to check in the
- * kernel that the process is reporting back the SIGALRM in a timely
- * fashion.
- */
 static __always_inline int syscall_handler(int fd_map[STUB_MAX_FDS])
 {
 	struct stub_data *d = get_stub_data();
diff --git a/arch/um/os-Linux/start_up.c b/arch/um/os-Linux/start_up.c
index 054ac03bbf5e..b01942bec953 100644
--- a/arch/um/os-Linux/start_up.c
+++ b/arch/um/os-Linux/start_up.c
@@ -452,12 +452,6 @@ __uml_setup("seccomp=", uml_seccomp_config,
 "    This method is overall faster than the ptrace based userspace, primarily\n"
 "    because it reduces the number of context switches for (minor) page faults.\n"
 "\n"
-"    However, the SECCOMP filter is not (yet) restrictive enough to prevent\n"
-"    userspace from reading and writing all physical memory. Userspace\n"
-"    processes could also trick the stub into disabling SIGALRM which\n"
-"    prevents it from being interrupted for scheduling purposes.\n"
-"\n"
-"    This is insecure and should only be used with a trusted userspace\n\n"
 );
 
 void __init os_early_checks(void)
-- 
2.43.0



From benjamin at sipsolutions.net  Mon Jun 22 05:08:21 2026
From: benjamin at sipsolutions.net (Benjamin Berg)
Date: Mon, 22 Jun 2026 14:08:21 +0200
Subject: [PATCH 0/7] um: skas: harden the seccomp userspace stub
In-Reply-To: <cover.1781925126.git.xiyou.wangcong@gmail.com>
References: <cover.1781925126.git.xiyou.wangcong@gmail.com>
Message-ID: <1e1681eecfa5dd30181534674b1a17756df802f8.camel@sipsolutions.net>

On Fri, 2026-06-19 at 20:22 -0700, Cong Wang wrote:
> From: Cong Wang <cwang at multikernel.io>
> 
> In the seccomp ("SECCOMP") userspace mode, each guest userspace process
> runs in a stub under a seccomp filter and traps to the monitor (the UML
> kernel) on every syscall. Two items on the stub.c "Known security issues"
> list could not be addressed by the filter alone:
> 
> ? - a hijacked stub could mmap() arbitrary physmem offsets, which is an
> ??? intra-guest disclosure and, on this base (single physmem fd, no
> ??? kernel/user split), a host escape; and
> 
> ? - a hijacked stub could block SIGALRM via a crafted rt_sigreturn to
> ??? evade preemption and wedge the monitor indefinitely.
> 
> This series closes both:
> 
> ? 1-2: route the stub's mmap() through a SECCOMP_RET_USER_NOTIF listener
> ?????? owned by the monitor (no behavioural change yet).
> ? 3-4: validate each mmap() against the mm's page table -- allowed iff the
> ?????? PTE already maps the requested frame with no more access than it
> ?????? grants -- including out-of-batch mmaps a hijacked stub issues on
> ?????? its own.
> ? 5:?? route and validate munmap() the same way (range-confined below
> ?????? STUB_START).


That approach seems odd to me. Adding an explicit out-of-band check
means you require two extra context switches per mmap syscall. I would
expect that this makes the SECCOMP approach a lot slower than ptrace().
My take is still that it is possible to carefully craft a SECCOMP
filter as well as stub/kernel code that makes exploitation impossible
for non-SMP.

The true SMP case is more complicated, but we do not have that anyway,
so I would not worry about it for now.

Did you run any performance tests?

> ? 6:?? add a watchdog thread that detects a stub which stops reporting
> ?????? back (e.g. blocked SIGALRM) and SIGKILLs it, letting the monitor
> ?????? recover via the existing teardown.

That also seems like an odd solution to me. Architecturally, UML first
receives the SIGALRM and forwards it to the child. It would seem much
easier to set a flag and clear it again when the process reports back
that it received the SIGALRM. Then, when the kernel receives the next
SIGALRM, just kill the child immediately if the flag is still set.

> ? 7:?? drop the now-resolved "Known security issues" note and refresh the
> ?????? seccomp= help text.


Benjamin

> After the series a hijacked stub is confined to the frames its own page
> tables reference and can no longer reach arbitrary guest/host memory; one
> that evades preemption is detected out of band and killed rather than
> wedging the monitor.
> 
> Verified on UML (UP and 2-CPU SMP): boots and survives fork/exec storms
> and heavy mmap/munmap churn with zero false denials or false kills; an
> artificially SIGALRM-blocked busy loop is killed in ~5s and the monitor
> recovers, while syscall-making processes are untouched. Each patch builds
> and the series is bisectable.
> 
> ---
> Cong Wang (7):
> ? um: skas: create a seccomp USER_NOTIF listener and hand it to the
> ??? monitor
> ? um: skas: gate stub mmap() through the USER_NOTIF monitor
> ? um: skas: validate stub mmap() against the guest page table
> ? um: skas: handle out-of-batch stub mmap notifications
> ? um: skas: validate stub munmap() against the guest address range
> ? um: skas: kill stubs that block SIGALRM via a watchdog thread
> ? um: skas: refresh stub security notes after closing the known issues
> 
> ?arch/um/include/shared/skas/mm_id.h |?? 1 +
> ?arch/um/include/shared/skas/skas.h? |?? 5 +
> ?arch/um/kernel/skas/stub.c????????? |? 22 --
> ?arch/um/kernel/skas/stub_exe.c????? |? 19 +-
> ?arch/um/kernel/skas/uaccess.c?????? |? 48 +++++
> ?arch/um/os-Linux/skas/process.c???? | 315 ++++++++++++++++++++++++----
> ?arch/um/os-Linux/start_up.c???????? |?? 6 -
> ?7 files changed, 344 insertions(+), 72 deletions(-)
> 
> 
> base-commit: 1a3746ccbb0a97bed3c06ccde6b880013b1dddc1


From michael.bommarito at gmail.com  Mon Jun 22 05:47:22 2026
From: michael.bommarito at gmail.com (Michael Bommarito)
Date: Mon, 22 Jun 2026 08:47:22 -0400
Subject: [PATCH] um: vector: fix use-after-free in vector_mmsg_rx()
Message-ID: <20260622124722.1694157-1-michael.bommarito@gmail.com>

When vector_mmsg_rx() discards a packet whose overlay header fails
verify_header(), it frees the skb and continues the loop:

	if (header_check < 0) {
		dev_kfree_skb_irq(skb);
		vp->estats.rx_encaps_errors++;
		continue;
	}

The normal and short-packet paths fall through to the bottom of the
loop body, which clears the consumed slot and advances the cursors:

	(*skbuff_vector) = NULL;
	mmsg_vector++;
	skbuff_vector++;

The verify_header() < 0 path skips that via continue, so the freed skb
is left in skbuff_vector[] and the cursors do not advance. The next
iteration reads the same slot, gets the freed skb, and frees it again,
producing a refcount underflow / use-after-free in the RX path.

Discard the slot the same way the other paths do before continuing.

Only transports whose verify_header() can return negative are affected:
GRE and L2TPv3 do so on a cookie/session-id mismatch (raw/tap do not),
so any peer on such a transport can trigger it without authentication.

Fixes: 49da7e64f33e ("High Performance UML Vector Network Driver")
Cc: stable at vger.kernel.org
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito at gmail.com>
---
Reproduced on a KASAN + refcount-full UML build: bring up a vec0
l2tpv3 device and send frames from the host with the right cookie but a
wrong session id. Stock kernel logs repeated "uml_l2tpv3: session
mismatch" then a refcount_t underflow / use-after-free in
dev_kfree_skb_irq_reason() <- vector_poll() <- vector_mmsg_rx(); with
this patch the mismatch is logged and the splat is gone (0 underflow, 0
use-after-free). Full dmesg and reproducer available on request.

 arch/um/drivers/vector_kern.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/arch/um/drivers/vector_kern.c b/arch/um/drivers/vector_kern.c
index 2cc90055499a5..8a70b3a625537 100644
--- a/arch/um/drivers/vector_kern.c
+++ b/arch/um/drivers/vector_kern.c
@@ -995,6 +995,9 @@ static int vector_mmsg_rx(struct vector_private *vp, int budget)
 				 */
 					dev_kfree_skb_irq(skb);
 					vp->estats.rx_encaps_errors++;
+					(*skbuff_vector) = NULL;
+					mmsg_vector++;
+					skbuff_vector++;
 					continue;
 				}
 				if (header_check > 0) {

base-commit: ef0c9f75a19532d7675384708fc8621e10850104
-- 
2.53.0



From michael.bommarito at gmail.com  Mon Jun 22 05:47:50 2026
From: michael.bommarito at gmail.com (Michael Bommarito)
Date: Mon, 22 Jun 2026 08:47:50 -0400
Subject: [PATCH] um: virtio_uml: fix heap overflow in vhost_user_get_config()
Message-ID: <20260622124750.1694957-1-michael.bommarito@gmail.com>

vhost_user_get_config() sizes its reply buffer from header + payload:

	size_t payload_size = sizeof(msg->payload.config) + cfg_size;
	size_t msg_size = sizeof(msg->header) + payload_size;
	...
	msg = kzalloc(msg_size, GFP_KERNEL);

but then receives the response with msg_size as the payload cap:

	rc = vhost_user_recv_resp(vu_dev, msg, msg_size);

vhost_user_recv() reads the header, then full_read()s header.size bytes
into &msg->payload after checking only "size > max_payload_size". The
payload area is msg_size - sizeof(msg->header) == payload_size bytes, so
passing msg_size as the cap lets a peer set header.size up to msg_size
and write sizeof(struct vhost_user_msg_header) (12) bytes past the end
of the kzalloc(msg_size) buffer -- a slab out-of-bounds write of
peer-controlled content.

Pass payload_size, which is the size the payload area was allocated for.

A malicious or compromised vhost-user backend triggers this during
device probe (vu_get -> vhost_user_get_config) with no guest action,
which matters in the confidential-computing model where the guest does
not trust the device backend.

Fixes: 5d38f324993f ("um: drivers: Add virtio vhost-user driver")
Cc: stable at vger.kernel.org
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito at gmail.com>
---
Reproduced on a UML KASAN build (ARCH=um, CONFIG_KASAN=y) off this base.
A stub vhost-user backend that answers GET_CONFIG with header.size set to
the full msg_size makes vhost_user_recv() full_read() 12 bytes past the
kzalloc'd reply buffer; KASAN reports a slab-out-of-bounds write in
vhost_user_get_config(). With this patch the oversized reply is rejected
(-EPROTO) and KASAN is clean. Benign control: a correctly-sized GET_CONFIG
reply succeeds on both stock and patched. Before/after logs available on
request.

 arch/um/drivers/virtio_uml.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/um/drivers/virtio_uml.c b/arch/um/drivers/virtio_uml.c
index 7425a8548141a..38e960bec9e65 100644
--- a/arch/um/drivers/virtio_uml.c
+++ b/arch/um/drivers/virtio_uml.c
@@ -568,7 +568,7 @@ static void vhost_user_get_config(struct virtio_uml_device *vu_dev,
 		goto free;
 	}
 
-	rc = vhost_user_recv_resp(vu_dev, msg, msg_size);
+	rc = vhost_user_recv_resp(vu_dev, msg, payload_size);
 	if (rc) {
 		vu_err(vu_dev,
 		       "receiving VHOST_USER_GET_CONFIG response failed: %d\n",

base-commit: ef0c9f75a19532d7675384708fc8621e10850104
-- 
2.53.0



From michael.bommarito at gmail.com  Mon Jun 22 05:48:23 2026
From: michael.bommarito at gmail.com (Michael Bommarito)
Date: Mon, 22 Jun 2026 08:48:23 -0400
Subject: [PATCH] um: ubd: validate COW header fields before use
Message-ID: <20260622124823.1695944-1-michael.bommarito@gmail.com>

read_cow_header() copies the backing-file path and computes geometry
from header fields without validating them, and the historical
"XXX Need to sanity-check the values read from the header" comment was
never addressed. Three image-controlled fields are unsafe:

  - backing_file[]: not guaranteed NUL-terminated. cow_strdup() and the
    later strlen()/printf() walk off the end of the header buffer when a
    crafted image leaves the array unterminated (heap over-read).

  - sectorsize: used as a divisor in cow_sizes()
    (bitmap_len = (size + sectorsize - 1) / (8 * sectorsize)); a zero or
    negative sectorsize causes a divide-by-zero / bogus geometry.

  - For the V3/V3_b layouts the existing "align == 0" check printed a
    warning but fell through and then computed
    ROUND_UP(sizeof(header), 0), another divide-by-zero.

Reject an unterminated backing_file for each header version, turn the
align == 0 warnings into hard errors (goto out), and reject a
non-positive sectorsize before the geometry math runs.

A COW image is parsed from a file the guest opens (ubdN=cow,backing on
the command line, or a cow file an unprivileged user can point ubd at),
so a crafted header is attacker-reachable.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable at vger.kernel.org
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito at gmail.com>
---
Reproduced on a UML KASAN build (ARCH=um) off this base, driving ubd with
crafted COW images. Stock: an unterminated v2 backing_file over-reads the
header buffer (KASAN slab-out-of-bounds read in the strdup/strlen path); a
sectorsize=0 image and a v3 alignment=0 image each hit a divide error in
the cow_sizes()/ROUND_UP geometry. Patched: each crafted image is rejected
in read_cow_header() ('not terminated' / 'invalid sectorsize' / 'align ==
0') and the driver continues. Benign control: a valid COW image opens
cleanly on both stock and patched. Before/after logs available on request.

 arch/um/drivers/cow_user.c | 34 +++++++++++++++++++++++++++++++++-
 1 file changed, 33 insertions(+), 1 deletion(-)

diff --git a/arch/um/drivers/cow_user.c b/arch/um/drivers/cow_user.c
index dc1d1bcd85ec2..bb9f05ac70cf4 100644
--- a/arch/um/drivers/cow_user.c
+++ b/arch/um/drivers/cow_user.c
@@ -279,7 +279,7 @@ int file_reader(__u64 offset, char *buf, int len, void *arg)
 	return pread(fd, buf, len, offset);
 }
 
-/* XXX Need to sanity-check the values read from the header */
+/* Sanity checks are performed after reading each header version below. */
 
 int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg,
 		    __u32 *version_out, char **backing_file_out,
@@ -325,6 +325,12 @@ int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg,
 		*sectorsize_out = header->v1.sectorsize;
 		*bitmap_offset_out = sizeof(header->v1);
 		*align_out = *sectorsize_out;
+		if (!memchr(header->v1.backing_file, '\0',
+			    sizeof(header->v1.backing_file))) {
+			cow_printf("%s - V1 backing_file not terminated\n",
+				   __func__);
+			goto out;
+		}
 		file = header->v1.backing_file;
 	}
 	else if (version == 2) {
@@ -338,6 +344,12 @@ int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg,
 		*sectorsize_out = be32toh(header->v2.sectorsize);
 		*bitmap_offset_out = sizeof(header->v2);
 		*align_out = *sectorsize_out;
+		if (!memchr(header->v2.backing_file, '\0',
+			    sizeof(header->v2.backing_file))) {
+			cow_printf("%s - V2 backing_file not terminated\n",
+				   __func__);
+			goto out;
+		}
 		file = header->v2.backing_file;
 	}
 	/* This is very subtle - see above at union cow_header definition */
@@ -354,8 +366,15 @@ int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg,
 		if (*align_out == 0) {
 			cow_printf("read_cow_header - invalid COW header, "
 				   "align == 0\n");
+			goto out;
 		}
 		*bitmap_offset_out = ROUND_UP(sizeof(header->v3), *align_out);
+		if (!memchr(header->v3.backing_file, '\0',
+			    sizeof(header->v3.backing_file))) {
+			cow_printf("%s - V3 backing_file not terminated\n",
+				   __func__);
+			goto out;
+		}
 		file = header->v3.backing_file;
 	}
 	else if (version == 3) {
@@ -385,14 +404,27 @@ int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg,
 		if (*align_out == 0) {
 			cow_printf("read_cow_header - invalid COW header, "
 				   "align == 0\n");
+			goto out;
 		}
 		*bitmap_offset_out = ROUND_UP(sizeof(header->v3_b), *align_out);
+		if (!memchr(header->v3_b.backing_file, '\0',
+			    sizeof(header->v3_b.backing_file))) {
+			cow_printf("%s - V3 backing_file not terminated\n",
+				   __func__);
+			goto out;
+		}
 		file = header->v3_b.backing_file;
 	}
 	else {
 		cow_printf("read_cow_header - invalid COW version\n");
 		goto out;
 	}
+
+	if (*sectorsize_out <= 0) {
+		cow_printf("%s - invalid sectorsize %d\n", __func__,
+			   *sectorsize_out);
+		goto out;
+	}
 	err = -ENOMEM;
 	*backing_file_out = cow_strdup(file);
 	if (*backing_file_out == NULL) {

base-commit: ef0c9f75a19532d7675384708fc8621e10850104
-- 
2.53.0



From xiyou.wangcong at gmail.com  Tue Jun 23 15:08:36 2026
From: xiyou.wangcong at gmail.com (Cong Wang)
Date: Tue, 23 Jun 2026 15:08:36 -0700
Subject: [PATCH 0/7] um: skas: harden the seccomp userspace stub
In-Reply-To: <1e1681eecfa5dd30181534674b1a17756df802f8.camel@sipsolutions.net>
References: <cover.1781925126.git.xiyou.wangcong@gmail.com> <1e1681eecfa5dd30181534674b1a17756df802f8.camel@sipsolutions.net>
Message-ID: <CAM_iQpWj+daY6tsPXKq=B4ekjr-8-tpfX+2twLhW_oGP_YqOHw@mail.gmail.com>

On Mon, Jun 22, 2026 at 5:08?AM Benjamin Berg <benjamin at sipsolutions.net> wrote:
>
> On Fri, 2026-06-19 at 20:22 -0700, Cong Wang wrote:
> > From: Cong Wang <cwang at multikernel.io>
> >
> > In the seccomp ("SECCOMP") userspace mode, each guest userspace process
> > runs in a stub under a seccomp filter and traps to the monitor (the UML
> > kernel) on every syscall. Two items on the stub.c "Known security issues"
> > list could not be addressed by the filter alone:
> >
> >   - a hijacked stub could mmap() arbitrary physmem offsets, which is an
> >     intra-guest disclosure and, on this base (single physmem fd, no
> >     kernel/user split), a host escape; and
> >
> >   - a hijacked stub could block SIGALRM via a crafted rt_sigreturn to
> >     evade preemption and wedge the monitor indefinitely.
> >
> > This series closes both:
> >
> >   1-2: route the stub's mmap() through a SECCOMP_RET_USER_NOTIF listener
> >        owned by the monitor (no behavioural change yet).
> >   3-4: validate each mmap() against the mm's page table -- allowed iff the
> >        PTE already maps the requested frame with no more access than it
> >        grants -- including out-of-batch mmaps a hijacked stub issues on
> >        its own.
> >   5:   route and validate munmap() the same way (range-confined below
> >        STUB_START).
>
>
> That approach seems odd to me. Adding an explicit out-of-band check
> means you require two extra context switches per mmap syscall. I would
> expect that this makes the SECCOMP approach a lot slower than ptrace().
> My take is still that it is possible to carefully craft a SECCOMP
> filter as well as stub/kernel code that makes exploitation impossible
> for non-SMP.
>
> The true SMP case is more complicated, but we do not have that anyway,
> so I would not worry about it for now.
>
> Did you run any performance tests?

Sorry, I thought I included them in patch 4/7, but I missed them.

Here they are:

mmap() -- ns/call
-----------------
size   WITH med   WITHOUT med   delta            WITH p99   WITHOUT p99
4K     13056      10368         +2688  (+26%)     17408      15104
16K    13824      11008         +2816  (+26%)     15616      12544
64K    19840      15616         +4224  (+27%)     21760      17408
256K   33152      28928         +4224  (+15%)     36352      31744
1M     95616      84608         +11008 (+13%)     117504     108032

munmap() -- ns/call
-------------------
size   WITH med   WITHOUT med   delta            WITH p99   WITHOUT p99
4K     11008      8448          +2560  (+30%)     13568      11264
16K    10752      8448          +2304  (+27%)     12288      9472
64K    12032      9728          +2304  (+24%)     13824      10752
256K   15360      12800         +2560  (+20%)     17152      14592
1M     27648      24832         +2816  (+11%)     30976      26880

Since this is a clear security-vs-performance balance, I picked security
over performance. Please let me know if you prefer otherwise.

Meanwhile, after a second thought, there is a zero-overhead solution.
pidfd_mmap(), which can be built on top of another patch of mine:
https://lore.kernel.org/all/20260613001533.314739-2-xiyou.wangcong at gmail.com/

Something like:

int map(struct mm_id *mm_idp, unsigned long virt, unsigned long len, int prot,
        int phys_fd, unsigned long long offset)
{
        return pidfd_mmap(mm_idp->stub_pidfd, (void *)virt, len,
                          prot_to_mmap(prot), MAP_FIXED | MAP_SHARED,
                          phys_fd, offset);
}
int unmap(struct mm_id *mm_idp, unsigned long addr, unsigned long len)
{
        return pidfd_munmap(mm_idp->stub_pidfd, (void *)addr, len);
}

Obviously, this one requires a new syscall for the host kernel first.

>
> >   6:   add a watchdog thread that detects a stub which stops reporting
> >        back (e.g. blocked SIGALRM) and SIGKILLs it, letting the monitor
> >        recover via the existing teardown.
>
> That also seems like an odd solution to me. Architecturally, UML first
> receives the SIGALRM and forwards it to the child. It would seem much
> easier to set a flag and clear it again when the process reports back
> that it received the SIGALRM. Then, when the kernel receives the next
> SIGALRM, just kill the child immediately if the flag is still set.

The flag-and-recheck scheme matches the ptrace path (wait_stub_done), where
the monitor is the tracer: it sees the stub's SIGALRM as a waitpid()
signal-stop and PTRACE_CONTs it, so "monitor receives, then deals with the
child" is accurate there. This patch is on the seccomp path
(wait_stub_done_seccomp), which is architecturally different.

Or am I missing anything?

Thanks for your review.

Regards,
Cong


From benjamin at sipsolutions.net  Wed Jun 24 00:54:49 2026
From: benjamin at sipsolutions.net (Benjamin Berg)
Date: Wed, 24 Jun 2026 09:54:49 +0200
Subject: [PATCH 0/7] um: skas: harden the seccomp userspace stub
In-Reply-To: <CAM_iQpWj+daY6tsPXKq=B4ekjr-8-tpfX+2twLhW_oGP_YqOHw@mail.gmail.com>
References: <cover.1781925126.git.xiyou.wangcong@gmail.com>
	 <1e1681eecfa5dd30181534674b1a17756df802f8.camel@sipsolutions.net>
	 <CAM_iQpWj+daY6tsPXKq=B4ekjr-8-tpfX+2twLhW_oGP_YqOHw@mail.gmail.com>
Message-ID: <03b2134eea62d664781069c8bd072705e0fc4aee.camel@sipsolutions.net>

On Tue, 2026-06-23 at 15:08 -0700, Cong Wang wrote:
> On Mon, Jun 22, 2026 at 5:08?AM Benjamin Berg <benjamin at sipsolutions.net> wrote:
> > 
> > On Fri, 2026-06-19 at 20:22 -0700, Cong Wang wrote:
> > > From: Cong Wang <cwang at multikernel.io>
> > > 
> > > In the seccomp ("SECCOMP") userspace mode, each guest userspace process
> > > runs in a stub under a seccomp filter and traps to the monitor (the UML
> > > kernel) on every syscall. Two items on the stub.c "Known security issues"
> > > list could not be addressed by the filter alone:
> > > 
> > > ? - a hijacked stub could mmap() arbitrary physmem offsets, which is an
> > > ??? intra-guest disclosure and, on this base (single physmem fd, no
> > > ??? kernel/user split), a host escape; and
> > > 
> > > ? - a hijacked stub could block SIGALRM via a crafted rt_sigreturn to
> > > ??? evade preemption and wedge the monitor indefinitely.
> > > 
> > > This series closes both:
> > > 
> > > ? 1-2: route the stub's mmap() through a SECCOMP_RET_USER_NOTIF listener
> > > ?????? owned by the monitor (no behavioural change yet).
> > > ? 3-4: validate each mmap() against the mm's page table -- allowed iff the
> > > ?????? PTE already maps the requested frame with no more access than it
> > > ?????? grants -- including out-of-batch mmaps a hijacked stub issues on
> > > ?????? its own.
> > > ? 5:?? route and validate munmap() the same way (range-confined below
> > > ?????? STUB_START).
> > 
> > 
> > That approach seems odd to me. Adding an explicit out-of-band check
> > means you require two extra context switches per mmap syscall. I would
> > expect that this makes the SECCOMP approach a lot slower than ptrace().
> > My take is still that it is possible to carefully craft a SECCOMP
> > filter as well as stub/kernel code that makes exploitation impossible
> > for non-SMP.
> > 
> > The true SMP case is more complicated, but we do not have that anyway,
> > so I would not worry about it for now.
> > 
> > Did you run any performance tests?
> 
> Sorry, I thought I included them in patch 4/7, but I missed them.
> 
> Here they are:
> 
> mmap() -- ns/call
> -----------------
> size?? WITH med?? WITHOUT med?? delta??????????? WITH p99?? WITHOUT p99
> 4K???? 13056????? 10368???????? +2688? (+26%)???? 17408????? 15104
> 16K??? 13824????? 11008???????? +2816? (+26%)???? 15616????? 12544
> 64K??? 19840????? 15616???????? +4224? (+27%)???? 21760????? 17408
> 256K?? 33152????? 28928???????? +4224? (+15%)???? 36352????? 31744
> 1M???? 95616????? 84608???????? +11008 (+13%)???? 117504???? 108032
> 
> munmap() -- ns/call
> -------------------
> size?? WITH med?? WITHOUT med?? delta??????????? WITH p99?? WITHOUT p99
> 4K???? 11008????? 8448????????? +2560? (+30%)???? 13568????? 11264
> 16K??? 10752????? 8448????????? +2304? (+27%)???? 12288????? 9472
> 64K??? 12032????? 9728????????? +2304? (+24%)???? 13824????? 10752
> 256K?? 15360????? 12800???????? +2560? (+20%)???? 17152????? 14592
> 1M???? 27648????? 24832???????? +2816? (+11%)???? 30976????? 26880
> 
> Since this is a clear security-vs-performance balance, I picked security
> over performance. Please let me know if you prefer otherwise.

So, I still believe that (almost) zero-overhead is possible in the
current framework. Maybe one could add a non-zero overhead option, but
then I would like to be able to disable it as there are plenty of
scenarios where security is of no concern.

Also, I am unsure what you measured there. When you write "mmap" here,
is that an "mmap" call made by the userspace process (which might not
actually change the page mappings), or is that an "mmap" call that was
queued for the stub to process?

A large amount of the overhead that UML tends to have happens when
handling both minor and major page faults. Simple tests for this is for
example a relatively tight fork/exec loop or just applications starting
up. In our test runs, we saw major speed improvements (>10%) just by
fixing bugs in the TLB handling code that resulted in fewer page faults
happening at application runtime.

> Meanwhile, after a second thought, there is a zero-overhead solution.
> pidfd_mmap(), which can be built on top of another patch of mine:
> https://lore.kernel.org/all/20260613001533.314739-2-xiyou.wangcong at gmail.com/

Sure, should some sort of method land that allows directly modifying
the?mappings of the remote process, that would be rather convenient. 

> Something like:
> 
> int map(struct mm_id *mm_idp, unsigned long virt, unsigned long len, int prot,
> ??????? int phys_fd, unsigned long long offset)
> {
> ??????? return pidfd_mmap(mm_idp->stub_pidfd, (void *)virt, len,
> ????????????????????????? prot_to_mmap(prot), MAP_FIXED | MAP_SHARED,
> ????????????????????????? phys_fd, offset);
> }
> int unmap(struct mm_id *mm_idp, unsigned long addr, unsigned long len)
> {
> ??????? return pidfd_munmap(mm_idp->stub_pidfd, (void *)addr, len);
> }
> 
> Obviously, this one requires a new syscall for the host kernel first.
> 
> > 
> > > ? 6:?? add a watchdog thread that detects a stub which stops reporting
> > > ?????? back (e.g. blocked SIGALRM) and SIGKILLs it, letting the monitor
> > > ?????? recover via the existing teardown.
> > 
> > That also seems like an odd solution to me. Architecturally, UML first
> > receives the SIGALRM and forwards it to the child. It would seem much
> > easier to set a flag and clear it again when the process reports back
> > that it received the SIGALRM. Then, when the kernel receives the next
> > SIGALRM, just kill the child immediately if the flag is still set.
> 
> The flag-and-recheck scheme matches the ptrace path (wait_stub_done), where
> the monitor is the tracer: it sees the stub's SIGALRM as a waitpid()
> signal-stop and PTRACE_CONTs it, so "monitor receives, then deals with the
> child" is accurate there. This patch is on the seccomp path
> (wait_stub_done_seccomp), which is architecturally different.
> 
> Or am I missing anything?

The fact that it is not actually different from an architectural
standpoint. In both cases SIGALRM is reported back to the kernel. It
obviously has to be reported back, otherwise scheduling would not work.

Benjamin

> 
> Thanks for your review.
> 
> Regards,
> Cong
> 


From xiyou.wangcong at gmail.com  Wed Jun 24 16:12:39 2026
From: xiyou.wangcong at gmail.com (Cong Wang)
Date: Wed, 24 Jun 2026 16:12:39 -0700
Subject: [PATCH 0/7] um: skas: harden the seccomp userspace stub
In-Reply-To: <03b2134eea62d664781069c8bd072705e0fc4aee.camel@sipsolutions.net>
References: <cover.1781925126.git.xiyou.wangcong@gmail.com>
 <1e1681eecfa5dd30181534674b1a17756df802f8.camel@sipsolutions.net>
 <CAM_iQpWj+daY6tsPXKq=B4ekjr-8-tpfX+2twLhW_oGP_YqOHw@mail.gmail.com> <03b2134eea62d664781069c8bd072705e0fc4aee.camel@sipsolutions.net>
Message-ID: <CAM_iQpUc4dQvYXbGpUgBXke+a8hVXnBCtjziXd1QX=QJXWiwjA@mail.gmail.com>

On Wed, Jun 24, 2026 at 12:54?AM Benjamin Berg
<benjamin at sipsolutions.net> wrote:
>
> On Tue, 2026-06-23 at 15:08 -0700, Cong Wang wrote:
> > On Mon, Jun 22, 2026 at 5:08?AM Benjamin Berg <benjamin at sipsolutions.net> wrote:
> > >
> > > On Fri, 2026-06-19 at 20:22 -0700, Cong Wang wrote:
> > > > From: Cong Wang <cwang at multikernel.io>
> > > >
> > > > In the seccomp ("SECCOMP") userspace mode, each guest userspace process
> > > > runs in a stub under a seccomp filter and traps to the monitor (the UML
> > > > kernel) on every syscall. Two items on the stub.c "Known security issues"
> > > > list could not be addressed by the filter alone:
> > > >
> > > >   - a hijacked stub could mmap() arbitrary physmem offsets, which is an
> > > >     intra-guest disclosure and, on this base (single physmem fd, no
> > > >     kernel/user split), a host escape; and
> > > >
> > > >   - a hijacked stub could block SIGALRM via a crafted rt_sigreturn to
> > > >     evade preemption and wedge the monitor indefinitely.
> > > >
> > > > This series closes both:
> > > >
> > > >   1-2: route the stub's mmap() through a SECCOMP_RET_USER_NOTIF listener
> > > >        owned by the monitor (no behavioural change yet).
> > > >   3-4: validate each mmap() against the mm's page table -- allowed iff the
> > > >        PTE already maps the requested frame with no more access than it
> > > >        grants -- including out-of-batch mmaps a hijacked stub issues on
> > > >        its own.
> > > >   5:   route and validate munmap() the same way (range-confined below
> > > >        STUB_START).
> > >
> > >
> > > That approach seems odd to me. Adding an explicit out-of-band check
> > > means you require two extra context switches per mmap syscall. I would
> > > expect that this makes the SECCOMP approach a lot slower than ptrace().
> > > My take is still that it is possible to carefully craft a SECCOMP
> > > filter as well as stub/kernel code that makes exploitation impossible
> > > for non-SMP.
> > >
> > > The true SMP case is more complicated, but we do not have that anyway,
> > > so I would not worry about it for now.
> > >
> > > Did you run any performance tests?
> >
> > Sorry, I thought I included them in patch 4/7, but I missed them.
> >
> > Here they are:
> >
> > mmap() -- ns/call
> > -----------------
> > size   WITH med   WITHOUT med   delta            WITH p99   WITHOUT p99
> > 4K     13056      10368         +2688  (+26%)     17408      15104
> > 16K    13824      11008         +2816  (+26%)     15616      12544
> > 64K    19840      15616         +4224  (+27%)     21760      17408
> > 256K   33152      28928         +4224  (+15%)     36352      31744
> > 1M     95616      84608         +11008 (+13%)     117504     108032
> >
> > munmap() -- ns/call
> > -------------------
> > size   WITH med   WITHOUT med   delta            WITH p99   WITHOUT p99
> > 4K     11008      8448          +2560  (+30%)     13568      11264
> > 16K    10752      8448          +2304  (+27%)     12288      9472
> > 64K    12032      9728          +2304  (+24%)     13824      10752
> > 256K   15360      12800         +2560  (+20%)     17152      14592
> > 1M     27648      24832         +2816  (+11%)     30976      26880
> >
> > Since this is a clear security-vs-performance balance, I picked security
> > over performance. Please let me know if you prefer otherwise.
>
> So, I still believe that (almost) zero-overhead is possible in the
> current framework. Maybe one could add a non-zero overhead option, but
> then I would like to be able to disable it as there are plenty of
> scenarios where security is of no concern.

This is fair, I can add an option to keep it disabled by default.

>
> Also, I am unsure what you measured there. When you write "mmap" here,
> is that an "mmap" call made by the userspace process (which might not
> actually change the page mappings), or is that an "mmap" call that was
> queued for the stub to process?
>
> A large amount of the overhead that UML tends to have happens when
> handling both minor and major page faults. Simple tests for this is for
> example a relatively tight fork/exec loop or just applications starting
> up. In our test runs, we saw major speed improvements (>10%) just by
> fixing bugs in the TLB handling code that resulted in fewer page faults
> happening at application runtime.

It is just a simple user-space mmap() loop just for benchmarking this
patchset.

>
> > Meanwhile, after a second thought, there is a zero-overhead solution.
> > pidfd_mmap(), which can be built on top of another patch of mine:
> > https://lore.kernel.org/all/20260613001533.314739-2-xiyou.wangcong at gmail.com/
>
> Sure, should some sort of method land that allows directly modifying
> the mappings of the remote process, that would be rather convenient.
>

Great. I will work on it. One question: should I drop this one or we can use
this one as the short term solution before the pidfd_mmap() solution gets in?
Even if we had pidfd_mmap(), UML might still want to keep compatibility with
older host kernels?


> > Something like:
> >
> > int map(struct mm_id *mm_idp, unsigned long virt, unsigned long len, int prot,
> >         int phys_fd, unsigned long long offset)
> > {
> >         return pidfd_mmap(mm_idp->stub_pidfd, (void *)virt, len,
> >                           prot_to_mmap(prot), MAP_FIXED | MAP_SHARED,
> >                           phys_fd, offset);
> > }
> > int unmap(struct mm_id *mm_idp, unsigned long addr, unsigned long len)
> > {
> >         return pidfd_munmap(mm_idp->stub_pidfd, (void *)addr, len);
> > }
> >
> > Obviously, this one requires a new syscall for the host kernel first.
> >
> > >
> > > >   6:   add a watchdog thread that detects a stub which stops reporting
> > > >        back (e.g. blocked SIGALRM) and SIGKILLs it, letting the monitor
> > > >        recover via the existing teardown.
> > >
> > > That also seems like an odd solution to me. Architecturally, UML first
> > > receives the SIGALRM and forwards it to the child. It would seem much
> > > easier to set a flag and clear it again when the process reports back
> > > that it received the SIGALRM. Then, when the kernel receives the next
> > > SIGALRM, just kill the child immediately if the flag is still set.
> >
> > The flag-and-recheck scheme matches the ptrace path (wait_stub_done), where
> > the monitor is the tracer: it sees the stub's SIGALRM as a waitpid()
> > signal-stop and PTRACE_CONTs it, so "monitor receives, then deals with the
> > child" is accurate there. This patch is on the seccomp path
> > (wait_stub_done_seccomp), which is architecturally different.
> >
> > Or am I missing anything?
>
> The fact that it is not actually different from an architectural
> standpoint. In both cases SIGALRM is reported back to the kernel. It
> obviously has to be reported back, otherwise scheduling would not work.

You're right.

In the seccomp path the monitor also receives the SIGALRM and
forwards it to the stub: the per-vCPU POSIX timer targets the monitor thread
and um_timer() -> os_alarm_process() -> kill(stub, SIGALRM) forwards
the tick. ptrace vs. seccomp differ only in how the monitor
learns the stub stopped (waitpid vs. futex), not in the point you raised.

So the watchdog thread is redundant. The inner FUTEX_WAIT loop in
wait_stub_done_seccomp already wakes on every forwarded tick via EINTR;
responsive stub acks by flipping data->futex out of FUTEX_IN_CHILD, a
SIGALRM-blocking one doesn't, exactly your flag scheme. I'll drop the helper
thread and timerfd and detect the stall inline: count consecutive ticks where
the stub didn't report and goto out_kill past a small threshold.

Thanks!


From ebiggers at kernel.org  Thu Jun 25 21:37:24 2026
From: ebiggers at kernel.org (Eric Biggers)
Date: Thu, 25 Jun 2026 21:37:24 -0700
Subject: [PATCH 1/8] x86/fpu: Check for missing AVX and AVX-512 xstate bits
In-Reply-To: <20260626043731.319287-1-ebiggers@kernel.org>
References: <20260626043731.319287-1-ebiggers@kernel.org>
Message-ID: <20260626043731.319287-2-ebiggers@kernel.org>

If the CPU declares AVX or AVX-512 support, verify that the
corresponding xstate bits are also set.  If not, warn and clear them.

This eliminates the perceived need for AVX and AVX-512 optimized code in
the kernel to call cpu_has_xfeatures().  That has never been universally
done, which strongly suggests that it has never really been needed in
practice, but this should remove any remaining doubt.

Signed-off-by: Eric Biggers <ebiggers at kernel.org>
---
 arch/x86/kernel/fpu/xstate.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c
index a7b6524a9dea..7f7e62e4ebc5 100644
--- a/arch/x86/kernel/fpu/xstate.c
+++ b/arch/x86/kernel/fpu/xstate.c
@@ -797,10 +797,27 @@ static u64 __init guest_default_mask(void)
 	 * for KVM guests.
 	 */
 	return ~(u64)XFEATURE_MASK_USER_DYNAMIC;
 }
 
+/* Clear any X86_FEATURE_* used by the kernel whose xfeatures are missing. */
+static void __init clear_cpu_caps_with_missing_xfeatures(u64 xfeatures)
+{
+	u64 mask;
+
+	mask = XFEATURE_MASK_FPSSE | XFEATURE_MASK_YMM;
+	if (boot_cpu_has(X86_FEATURE_AVX) && (xfeatures & mask) != mask) {
+		pr_err("x86/fpu: Disabling AVX support due to missing xstate features\n");
+		setup_clear_cpu_cap(X86_FEATURE_AVX);
+	}
+	mask = XFEATURE_MASK_FPSSE | XFEATURE_MASK_YMM | XFEATURE_MASK_AVX512;
+	if (boot_cpu_has(X86_FEATURE_AVX512F) && (xfeatures & mask) != mask) {
+		pr_err("x86/fpu: Disabling AVX-512 support due to missing xstate features\n");
+		setup_clear_cpu_cap(X86_FEATURE_AVX512F);
+	}
+}
+
 /*
  * Enable and initialize the xsave feature.
  * Called once per system bootup.
  */
 void __init fpu__init_system_xstate(unsigned int legacy_size)
@@ -853,10 +870,12 @@ void __init fpu__init_system_xstate(unsigned int legacy_size)
 		pr_err("x86/fpu: Both APX/MPX present in the CPU's xstate features: 0x%llx.\n",
 		       fpu_kernel_cfg.max_features);
 		goto out_disable;
 	}
 
+	clear_cpu_caps_with_missing_xfeatures(fpu_kernel_cfg.max_features);
+
 	fpu_kernel_cfg.independent_features = fpu_kernel_cfg.max_features &
 					      XFEATURE_MASK_INDEPENDENT;
 
 	/*
 	 * Clear XSAVE features that are disabled in the normal CPUID.
-- 
2.54.0



From ebiggers at kernel.org  Thu Jun 25 21:37:23 2026
From: ebiggers at kernel.org (Eric Biggers)
Date: Thu, 25 Jun 2026 21:37:23 -0700
Subject: [PATCH 0/8] x86: Remove cpu_has_xfeatures() and add AVX-512 xor_gen()
Message-ID: <20260626043731.319287-1-ebiggers@kernel.org>

My patch "lib/raid/xor: x86: Add AVX-512 optimized xor_gen()"
(https://lore.kernel.org/r/20260615190338.26581-1-ebiggers at kernel.org/)
still seems to be blocked on a Sashiko comment about cpu_has_xfeatures()
not being called.

However, the x86-optimized RAID library code supports UML, and currently
UML doesn't implement cpu_has_xfeatures().  That's perhaps why the
existing AVX-512 optimized RAID6 code doesn't check it either.

In fact, it seems to have been getting by fine without it, which
suggests that it's not truly needed.

But to eliminate any doubts, I've had a go at fully resolving the
situation by making both native x86 and UML explicitly clear any
X86_FEATURE_* flags at boot time whose xfeatures are missing.

Then, cpu_has_xfeatures() is entirely removed from the kernel.

The last patch adds the AVX-512 optimized xor_gen().  I do still think
it would be fine to proceed with it without the rest.  But if there are
any doubts, we can take this more comprehensive cleanup route.

Eric Biggers (8):
  x86/fpu: Check for missing AVX and AVX-512 xstate bits
  um: Check for missing AVX and AVX-512 xstate bits
  crypto: x86 - Stop using cpu_has_xfeatures()
  lib/crypto: x86: Stop using cpu_has_xfeatures()
  lib/crc: x86: Stop using cpu_has_xfeatures()
  x86/fpu: Remove cpu_has_xfeatures()
  lib/raid/xor: x86: Remove redundant X86_FEATURE_OSXSAVE check
  lib/raid/xor: x86: Add AVX-512 optimized xor_gen()

 arch/um/kernel/um_arch.c                   |  78 ++++++++++++-
 arch/x86/crypto/aegis128-aesni-glue.c      |   3 +-
 arch/x86/crypto/aesni-intel_glue.c         |   7 +-
 arch/x86/crypto/aria_aesni_avx2_glue.c     |  11 +-
 arch/x86/crypto/aria_aesni_avx_glue.c      |  11 +-
 arch/x86/crypto/aria_gfni_avx512_glue.c    |  11 +-
 arch/x86/crypto/camellia_aesni_avx2_glue.c |  11 +-
 arch/x86/crypto/camellia_aesni_avx_glue.c  |  11 +-
 arch/x86/crypto/cast5_avx_glue.c           |   7 +-
 arch/x86/crypto/cast6_avx_glue.c           |   7 +-
 arch/x86/crypto/serpent_avx2_glue.c        |   9 +-
 arch/x86/crypto/serpent_avx_glue.c         |   7 +-
 arch/x86/crypto/sm4_aesni_avx2_glue.c      |  11 +-
 arch/x86/crypto/sm4_aesni_avx_glue.c       |  11 +-
 arch/x86/crypto/twofish_avx_glue.c         |   6 +-
 arch/x86/include/asm/fpu/api.h             |   9 --
 arch/x86/kernel/fpu/xstate.c               |  63 ++++-------
 lib/crc/x86/crc-pclmul-template.h          |   6 +-
 lib/crypto/x86/blake2s.h                   |   4 +-
 lib/crypto/x86/chacha.h                    |   3 +-
 lib/crypto/x86/nh.h                        |   4 +-
 lib/crypto/x86/poly1305.h                  |   7 +-
 lib/crypto/x86/sha1.h                      |   4 +-
 lib/crypto/x86/sha256.h                    |   4 +-
 lib/crypto/x86/sha512.h                    |   3 +-
 lib/crypto/x86/sm3.h                       |   3 +-
 lib/raid/xor/Makefile                      |   2 +-
 lib/raid/xor/x86/xor-avx512.c              | 121 +++++++++++++++++++++
 lib/raid/xor/x86/xor_arch.h                |  24 ++--
 29 files changed, 264 insertions(+), 194 deletions(-)
 create mode 100644 lib/raid/xor/x86/xor-avx512.c


base-commit: 4edcdefd4083ae04b1a5656f4be6cd83ae919ef4
-- 
2.54.0



From ebiggers at kernel.org  Thu Jun 25 21:37:25 2026
From: ebiggers at kernel.org (Eric Biggers)
Date: Thu, 25 Jun 2026 21:37:25 -0700
Subject: [PATCH 2/8] um: Check for missing AVX and AVX-512 xstate bits
In-Reply-To: <20260626043731.319287-1-ebiggers@kernel.org>
References: <20260626043731.319287-1-ebiggers@kernel.org>
Message-ID: <20260626043731.319287-3-ebiggers@kernel.org>

If the CPU declares AVX or AVX-512 support, verify that all the
corresponding xstate bits are also set.  If any are missing, warn and
don't set the corresponding X86_FEATURE_* flags.

This eliminates the perceived need for UML-supporting AVX and AVX-512
optimized code in the kernel (that is, lib/raid/ currently) to start
checking the xstate bits in addition to X86_FEATURE_AVX*.

Signed-off-by: Eric Biggers <ebiggers at kernel.org>
---
 arch/um/kernel/um_arch.c | 78 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 77 insertions(+), 1 deletion(-)

diff --git a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c
index 2141f5f1f5a2..aafbaef2ae82 100644
--- a/arch/um/kernel/um_arch.c
+++ b/arch/um/kernel/um_arch.c
@@ -262,16 +262,92 @@ EXPORT_SYMBOL(task_size);
 
 unsigned long brk_start;
 
 #define MIN_VMALLOC (32 * 1024 * 1024)
 
+static u64 __init read_xcr0(void)
+{
+	u32 a, b, c, d;
+
+	asm volatile("cpuid"
+		     : "=a"(a), "=b"(b), "=c"(c), "=d"(d)
+		     : "a"(0), "c"(0));
+	if (a >= 1) { /* max_leaf >= 1 */
+		asm volatile("cpuid"
+			     : "=a"(a), "=b"(b), "=c"(c), "=d"(d)
+			     : "a"(1), "c"(0));
+		if (c & (1 << 27)) { /* XSAVE enabled by OS */
+			asm volatile("xgetbv" : "=d"(d), "=a"(a) : "c"(0));
+			return ((u64)d << 32) | a;
+		}
+	}
+	return 0;
+}
+
+static void __init validate_and_set_cpu_cap(int cap, u64 xcr0)
+{
+	/*
+	 * Check for missing xstate features right away, so that there's no
+	 * perceived need for all optimized code in the kernel to do so.
+	 */
+	switch (cap) {
+	case X86_FEATURE_AVX:
+	case X86_FEATURE_AVX2:
+	case X86_FEATURE_AVX_VNNI:
+	case X86_FEATURE_FMA:
+	case X86_FEATURE_VAES:
+	case X86_FEATURE_VPCLMULQDQ:
+		if ((xcr0 & 0x7) != 0x7) {
+			static bool warned;
+
+			if (!warned) {
+				os_warn("Disabling AVX support due to missing xstate features\n");
+				warned = true;
+			}
+			return;
+		}
+		break;
+	case X86_FEATURE_AVX512F:
+	case X86_FEATURE_AVX512BW:
+	case X86_FEATURE_AVX512CD:
+	case X86_FEATURE_AVX512DQ:
+	case X86_FEATURE_AVX512ER:
+	case X86_FEATURE_AVX512IFMA:
+	case X86_FEATURE_AVX512PF:
+	case X86_FEATURE_AVX512VBMI:
+	case X86_FEATURE_AVX512VL:
+	case X86_FEATURE_AVX512_4FMAPS:
+	case X86_FEATURE_AVX512_4VNNIW:
+	case X86_FEATURE_AVX512_BF16:
+	case X86_FEATURE_AVX512_BITALG:
+	case X86_FEATURE_AVX512_FP16:
+	case X86_FEATURE_AVX512_VBMI2:
+	case X86_FEATURE_AVX512_VNNI:
+	case X86_FEATURE_AVX512_VP2INTERSECT:
+	case X86_FEATURE_AVX512_VPOPCNTDQ:
+		if ((xcr0 & 0xe7) != 0xe7) {
+			static bool warned;
+
+			if (!warned) {
+				os_warn("Disabling AVX-512 support due to missing xstate features\n");
+				warned = true;
+			}
+			return;
+		}
+		break;
+	}
+	set_cpu_cap(&boot_cpu_data, cap);
+}
+
 static void __init parse_host_cpu_flags(char *line)
 {
+	u64 xcr0 = read_xcr0();
 	int i;
+
 	for (i = 0; i < 32*NCAPINTS; i++) {
 		if ((x86_cap_flags[i] != NULL) && strstr(line, x86_cap_flags[i]))
-			set_cpu_cap(&boot_cpu_data, i);
+			validate_and_set_cpu_cap(i, xcr0);
 	}
 }
 
 static void __init parse_cache_line(char *line)
 {
-- 
2.54.0



From ebiggers at kernel.org  Thu Jun 25 21:37:26 2026
From: ebiggers at kernel.org (Eric Biggers)
Date: Thu, 25 Jun 2026 21:37:26 -0700
Subject: [PATCH 3/8] crypto: x86 - Stop using cpu_has_xfeatures()
In-Reply-To: <20260626043731.319287-1-ebiggers@kernel.org>
References: <20260626043731.319287-1-ebiggers@kernel.org>
Message-ID: <20260626043731.319287-4-ebiggers@kernel.org>

Checking both boot_cpu_has(X86_FEATURE_AVX*) and cpu_has_xfeatures() has
never really been needed in practice, and it's never been universally
done (e.g., lib/raid/ omits cpu_has_xfeatures()).  Nevertheless, both
x86 and UML now explicitly clear the AVX and AVX-512 flags if their
xfeatures are missing, which should remove any remaining doubts.

Thus, remove all the calls to cpu_has_xfeatures(), as well as the
related checks of boot_cpu_has(X86_FEATURE_OSXSAVE).

In a few cases there was no corresponding boot_cpu_has(X86_FEATURE_AVX*)
check, so add the missing ones.

Signed-off-by: Eric Biggers <ebiggers at kernel.org>
---
 arch/x86/crypto/aegis128-aesni-glue.c      |  3 +--
 arch/x86/crypto/aesni-intel_glue.c         |  7 ++-----
 arch/x86/crypto/aria_aesni_avx2_glue.c     | 11 +----------
 arch/x86/crypto/aria_aesni_avx_glue.c      | 11 +----------
 arch/x86/crypto/aria_gfni_avx512_glue.c    | 11 +----------
 arch/x86/crypto/camellia_aesni_avx2_glue.c | 11 +----------
 arch/x86/crypto/camellia_aesni_avx_glue.c  | 11 +----------
 arch/x86/crypto/cast5_avx_glue.c           |  7 ++-----
 arch/x86/crypto/cast6_avx_glue.c           |  7 ++-----
 arch/x86/crypto/serpent_avx2_glue.c        |  9 +--------
 arch/x86/crypto/serpent_avx_glue.c         |  7 ++-----
 arch/x86/crypto/sm4_aesni_avx2_glue.c      | 11 +----------
 arch/x86/crypto/sm4_aesni_avx_glue.c       | 11 +----------
 arch/x86/crypto/twofish_avx_glue.c         |  6 ++----
 14 files changed, 19 insertions(+), 104 deletions(-)

diff --git a/arch/x86/crypto/aegis128-aesni-glue.c b/arch/x86/crypto/aegis128-aesni-glue.c
index f1adfba1a76e..09fc0b15b0e9 100644
--- a/arch/x86/crypto/aegis128-aesni-glue.c
+++ b/arch/x86/crypto/aegis128-aesni-glue.c
@@ -263,12 +263,11 @@ static struct aead_alg crypto_aegis128_aesni_alg = {
 };
 
 static int __init crypto_aegis128_aesni_module_init(void)
 {
 	if (!boot_cpu_has(X86_FEATURE_XMM4_1) ||
-	    !boot_cpu_has(X86_FEATURE_AES) ||
-	    !cpu_has_xfeatures(XFEATURE_MASK_SSE, NULL))
+	    !boot_cpu_has(X86_FEATURE_AES))
 		return -ENODEV;
 
 	return crypto_register_aead(&crypto_aegis128_aesni_alg);
 }
 
diff --git a/arch/x86/crypto/aesni-intel_glue.c b/arch/x86/crypto/aesni-intel_glue.c
index f522fff9231e..f6f899db7482 100644
--- a/arch/x86/crypto/aesni-intel_glue.c
+++ b/arch/x86/crypto/aesni-intel_glue.c
@@ -1546,12 +1546,11 @@ static int __init register_avx_algs(void)
 	 * For simplicity, just always check for VAES and VPCLMULQDQ together.
 	 */
 	if (!boot_cpu_has(X86_FEATURE_AVX2) ||
 	    !boot_cpu_has(X86_FEATURE_VAES) ||
 	    !boot_cpu_has(X86_FEATURE_VPCLMULQDQ) ||
-	    !boot_cpu_has(X86_FEATURE_PCLMULQDQ) ||
-	    !cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM, NULL))
+	    !boot_cpu_has(X86_FEATURE_PCLMULQDQ))
 		return 0;
 	err = crypto_register_skciphers(skcipher_algs_vaes_avx2,
 					ARRAY_SIZE(skcipher_algs_vaes_avx2));
 	if (err)
 		return err;
@@ -1560,13 +1559,11 @@ static int __init register_avx_algs(void)
 	if (err)
 		return err;
 
 	if (!boot_cpu_has(X86_FEATURE_AVX512BW) ||
 	    !boot_cpu_has(X86_FEATURE_AVX512VL) ||
-	    !boot_cpu_has(X86_FEATURE_BMI2) ||
-	    !cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM |
-			       XFEATURE_MASK_AVX512, NULL))
+	    !boot_cpu_has(X86_FEATURE_BMI2))
 		return 0;
 
 	if (boot_cpu_has(X86_FEATURE_PREFER_YMM)) {
 		int i;
 
diff --git a/arch/x86/crypto/aria_aesni_avx2_glue.c b/arch/x86/crypto/aria_aesni_avx2_glue.c
index 1487a49bfbac..371be2fb6469 100644
--- a/arch/x86/crypto/aria_aesni_avx2_glue.c
+++ b/arch/x86/crypto/aria_aesni_avx2_glue.c
@@ -193,26 +193,17 @@ static struct skcipher_alg aria_algs[] = {
 	}
 };
 
 static int __init aria_avx2_init(void)
 {
-	const char *feature_name;
-
 	if (!boot_cpu_has(X86_FEATURE_AVX) ||
 	    !boot_cpu_has(X86_FEATURE_AVX2) ||
-	    !boot_cpu_has(X86_FEATURE_AES) ||
-	    !boot_cpu_has(X86_FEATURE_OSXSAVE)) {
+	    !boot_cpu_has(X86_FEATURE_AES)) {
 		pr_info("AVX2 or AES-NI instructions are not detected.\n");
 		return -ENODEV;
 	}
 
-	if (!cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM,
-				&feature_name)) {
-		pr_info("CPU feature '%s' is not supported.\n", feature_name);
-		return -ENODEV;
-	}
-
 	if (boot_cpu_has(X86_FEATURE_GFNI)) {
 		aria_ops.aria_encrypt_16way = aria_aesni_avx_gfni_encrypt_16way;
 		aria_ops.aria_decrypt_16way = aria_aesni_avx_gfni_decrypt_16way;
 		aria_ops.aria_ctr_crypt_16way = aria_aesni_avx_gfni_ctr_crypt_16way;
 		aria_ops.aria_encrypt_32way = aria_aesni_avx2_gfni_encrypt_32way;
diff --git a/arch/x86/crypto/aria_aesni_avx_glue.c b/arch/x86/crypto/aria_aesni_avx_glue.c
index e4e3d78915a5..d23fc91c0ebd 100644
--- a/arch/x86/crypto/aria_aesni_avx_glue.c
+++ b/arch/x86/crypto/aria_aesni_avx_glue.c
@@ -180,25 +180,16 @@ static struct skcipher_alg aria_algs[] = {
 	}
 };
 
 static int __init aria_avx_init(void)
 {
-	const char *feature_name;
-
 	if (!boot_cpu_has(X86_FEATURE_AVX) ||
-	    !boot_cpu_has(X86_FEATURE_AES) ||
-	    !boot_cpu_has(X86_FEATURE_OSXSAVE)) {
+	    !boot_cpu_has(X86_FEATURE_AES)) {
 		pr_info("AVX or AES-NI instructions are not detected.\n");
 		return -ENODEV;
 	}
 
-	if (!cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM,
-				&feature_name)) {
-		pr_info("CPU feature '%s' is not supported.\n", feature_name);
-		return -ENODEV;
-	}
-
 	if (boot_cpu_has(X86_FEATURE_GFNI)) {
 		aria_ops.aria_encrypt_16way = aria_aesni_avx_gfni_encrypt_16way;
 		aria_ops.aria_decrypt_16way = aria_aesni_avx_gfni_decrypt_16way;
 		aria_ops.aria_ctr_crypt_16way = aria_aesni_avx_gfni_ctr_crypt_16way;
 	} else {
diff --git a/arch/x86/crypto/aria_gfni_avx512_glue.c b/arch/x86/crypto/aria_gfni_avx512_glue.c
index 363cbf4399cc..e05bbeb22d4a 100644
--- a/arch/x86/crypto/aria_gfni_avx512_glue.c
+++ b/arch/x86/crypto/aria_gfni_avx512_glue.c
@@ -194,28 +194,19 @@ static struct skcipher_alg aria_algs[] = {
 	}
 };
 
 static int __init aria_avx512_init(void)
 {
-	const char *feature_name;
-
 	if (!boot_cpu_has(X86_FEATURE_AVX) ||
 	    !boot_cpu_has(X86_FEATURE_AVX2) ||
 	    !boot_cpu_has(X86_FEATURE_AVX512F) ||
 	    !boot_cpu_has(X86_FEATURE_AVX512VL) ||
-	    !boot_cpu_has(X86_FEATURE_GFNI) ||
-	    !boot_cpu_has(X86_FEATURE_OSXSAVE)) {
+	    !boot_cpu_has(X86_FEATURE_GFNI)) {
 		pr_info("AVX512/GFNI instructions are not detected.\n");
 		return -ENODEV;
 	}
 
-	if (!cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM |
-			       XFEATURE_MASK_AVX512, &feature_name)) {
-		pr_info("CPU feature '%s' is not supported.\n", feature_name);
-		return -ENODEV;
-	}
-
 	aria_ops.aria_encrypt_16way = aria_aesni_avx_gfni_encrypt_16way;
 	aria_ops.aria_decrypt_16way = aria_aesni_avx_gfni_decrypt_16way;
 	aria_ops.aria_ctr_crypt_16way = aria_aesni_avx_gfni_ctr_crypt_16way;
 	aria_ops.aria_encrypt_32way = aria_aesni_avx2_gfni_encrypt_32way;
 	aria_ops.aria_decrypt_32way = aria_aesni_avx2_gfni_decrypt_32way;
diff --git a/arch/x86/crypto/camellia_aesni_avx2_glue.c b/arch/x86/crypto/camellia_aesni_avx2_glue.c
index 2d2f4e16537c..073fa3bb8388 100644
--- a/arch/x86/crypto/camellia_aesni_avx2_glue.c
+++ b/arch/x86/crypto/camellia_aesni_avx2_glue.c
@@ -95,26 +95,17 @@ static struct skcipher_alg camellia_algs[] = {
 	},
 };
 
 static int __init camellia_aesni_init(void)
 {
-	const char *feature_name;
-
 	if (!boot_cpu_has(X86_FEATURE_AVX) ||
 	    !boot_cpu_has(X86_FEATURE_AVX2) ||
-	    !boot_cpu_has(X86_FEATURE_AES) ||
-	    !boot_cpu_has(X86_FEATURE_OSXSAVE)) {
+	    !boot_cpu_has(X86_FEATURE_AES)) {
 		pr_info("AVX2 or AES-NI instructions are not detected.\n");
 		return -ENODEV;
 	}
 
-	if (!cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM,
-				&feature_name)) {
-		pr_info("CPU feature '%s' is not supported.\n", feature_name);
-		return -ENODEV;
-	}
-
 	return crypto_register_skciphers(camellia_algs,
 					 ARRAY_SIZE(camellia_algs));
 }
 
 static void __exit camellia_aesni_fini(void)
diff --git a/arch/x86/crypto/camellia_aesni_avx_glue.c b/arch/x86/crypto/camellia_aesni_avx_glue.c
index 5c321f255eb7..872e5e07220f 100644
--- a/arch/x86/crypto/camellia_aesni_avx_glue.c
+++ b/arch/x86/crypto/camellia_aesni_avx_glue.c
@@ -96,25 +96,16 @@ static struct skcipher_alg camellia_algs[] = {
 	}
 };
 
 static int __init camellia_aesni_init(void)
 {
-	const char *feature_name;
-
 	if (!boot_cpu_has(X86_FEATURE_AVX) ||
-	    !boot_cpu_has(X86_FEATURE_AES) ||
-	    !boot_cpu_has(X86_FEATURE_OSXSAVE)) {
+	    !boot_cpu_has(X86_FEATURE_AES)) {
 		pr_info("AVX or AES-NI instructions are not detected.\n");
 		return -ENODEV;
 	}
 
-	if (!cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM,
-				&feature_name)) {
-		pr_info("CPU feature '%s' is not supported.\n", feature_name);
-		return -ENODEV;
-	}
-
 	return crypto_register_skciphers(camellia_algs,
 					 ARRAY_SIZE(camellia_algs));
 }
 
 static void __exit camellia_aesni_fini(void)
diff --git a/arch/x86/crypto/cast5_avx_glue.c b/arch/x86/crypto/cast5_avx_glue.c
index 3aca04d43b34..5de35e863370 100644
--- a/arch/x86/crypto/cast5_avx_glue.c
+++ b/arch/x86/crypto/cast5_avx_glue.c
@@ -90,15 +90,12 @@ static struct skcipher_alg cast5_algs[] = {
 	}
 };
 
 static int __init cast5_init(void)
 {
-	const char *feature_name;
-
-	if (!cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM,
-				&feature_name)) {
-		pr_info("CPU feature '%s' is not supported.\n", feature_name);
+	if (!boot_cpu_has(X86_FEATURE_AVX)) {
+		pr_info("AVX instructions are not detected.\n");
 		return -ENODEV;
 	}
 
 	return crypto_register_skciphers(cast5_algs,
 					 ARRAY_SIZE(cast5_algs));
diff --git a/arch/x86/crypto/cast6_avx_glue.c b/arch/x86/crypto/cast6_avx_glue.c
index c4dd28c30303..3d7ea48007bc 100644
--- a/arch/x86/crypto/cast6_avx_glue.c
+++ b/arch/x86/crypto/cast6_avx_glue.c
@@ -90,15 +90,12 @@ static struct skcipher_alg cast6_algs[] = {
 	},
 };
 
 static int __init cast6_init(void)
 {
-	const char *feature_name;
-
-	if (!cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM,
-				&feature_name)) {
-		pr_info("CPU feature '%s' is not supported.\n", feature_name);
+	if (!boot_cpu_has(X86_FEATURE_AVX)) {
+		pr_info("AVX instructions are not detected.\n");
 		return -ENODEV;
 	}
 
 	return crypto_register_skciphers(cast6_algs, ARRAY_SIZE(cast6_algs));
 }
diff --git a/arch/x86/crypto/serpent_avx2_glue.c b/arch/x86/crypto/serpent_avx2_glue.c
index f5f2121b7956..72a9e2b306d6 100644
--- a/arch/x86/crypto/serpent_avx2_glue.c
+++ b/arch/x86/crypto/serpent_avx2_glue.c
@@ -91,21 +91,14 @@ static struct skcipher_alg serpent_algs[] = {
 	},
 };
 
 static int __init serpent_avx2_init(void)
 {
-	const char *feature_name;
-
-	if (!boot_cpu_has(X86_FEATURE_AVX2) || !boot_cpu_has(X86_FEATURE_OSXSAVE)) {
+	if (!boot_cpu_has(X86_FEATURE_AVX2)) {
 		pr_info("AVX2 instructions are not detected.\n");
 		return -ENODEV;
 	}
-	if (!cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM,
-				&feature_name)) {
-		pr_info("CPU feature '%s' is not supported.\n", feature_name);
-		return -ENODEV;
-	}
 
 	return crypto_register_skciphers(serpent_algs,
 					 ARRAY_SIZE(serpent_algs));
 }
 
diff --git a/arch/x86/crypto/serpent_avx_glue.c b/arch/x86/crypto/serpent_avx_glue.c
index 9c8b3a335d5c..42c4e1569674 100644
--- a/arch/x86/crypto/serpent_avx_glue.c
+++ b/arch/x86/crypto/serpent_avx_glue.c
@@ -98,15 +98,12 @@ static struct skcipher_alg serpent_algs[] = {
 	},
 };
 
 static int __init serpent_init(void)
 {
-	const char *feature_name;
-
-	if (!cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM,
-				&feature_name)) {
-		pr_info("CPU feature '%s' is not supported.\n", feature_name);
+	if (!boot_cpu_has(X86_FEATURE_AVX)) {
+		pr_info("AVX instructions are not detected.\n");
 		return -ENODEV;
 	}
 
 	return crypto_register_skciphers(serpent_algs,
 					 ARRAY_SIZE(serpent_algs));
diff --git a/arch/x86/crypto/sm4_aesni_avx2_glue.c b/arch/x86/crypto/sm4_aesni_avx2_glue.c
index fec0ab7a63dd..eef73894e777 100644
--- a/arch/x86/crypto/sm4_aesni_avx2_glue.c
+++ b/arch/x86/crypto/sm4_aesni_avx2_glue.c
@@ -96,26 +96,17 @@ static struct skcipher_alg sm4_aesni_avx2_skciphers[] = {
 	}
 };
 
 static int __init sm4_init(void)
 {
-	const char *feature_name;
-
 	if (!boot_cpu_has(X86_FEATURE_AVX) ||
 	    !boot_cpu_has(X86_FEATURE_AVX2) ||
-	    !boot_cpu_has(X86_FEATURE_AES) ||
-	    !boot_cpu_has(X86_FEATURE_OSXSAVE)) {
+	    !boot_cpu_has(X86_FEATURE_AES)) {
 		pr_info("AVX2 or AES-NI instructions are not detected.\n");
 		return -ENODEV;
 	}
 
-	if (!cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM,
-				&feature_name)) {
-		pr_info("CPU feature '%s' is not supported.\n", feature_name);
-		return -ENODEV;
-	}
-
 	return crypto_register_skciphers(sm4_aesni_avx2_skciphers,
 					 ARRAY_SIZE(sm4_aesni_avx2_skciphers));
 }
 
 static void __exit sm4_exit(void)
diff --git a/arch/x86/crypto/sm4_aesni_avx_glue.c b/arch/x86/crypto/sm4_aesni_avx_glue.c
index 88caf418a06f..ed383da5ff46 100644
--- a/arch/x86/crypto/sm4_aesni_avx_glue.c
+++ b/arch/x86/crypto/sm4_aesni_avx_glue.c
@@ -312,25 +312,16 @@ static struct skcipher_alg sm4_aesni_avx_skciphers[] = {
 	}
 };
 
 static int __init sm4_init(void)
 {
-	const char *feature_name;
-
 	if (!boot_cpu_has(X86_FEATURE_AVX) ||
-	    !boot_cpu_has(X86_FEATURE_AES) ||
-	    !boot_cpu_has(X86_FEATURE_OSXSAVE)) {
+	    !boot_cpu_has(X86_FEATURE_AES)) {
 		pr_info("AVX or AES-NI instructions are not detected.\n");
 		return -ENODEV;
 	}
 
-	if (!cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM,
-				&feature_name)) {
-		pr_info("CPU feature '%s' is not supported.\n", feature_name);
-		return -ENODEV;
-	}
-
 	return crypto_register_skciphers(sm4_aesni_avx_skciphers,
 					 ARRAY_SIZE(sm4_aesni_avx_skciphers));
 }
 
 static void __exit sm4_exit(void)
diff --git a/arch/x86/crypto/twofish_avx_glue.c b/arch/x86/crypto/twofish_avx_glue.c
index 9e20db013750..985bc54a2340 100644
--- a/arch/x86/crypto/twofish_avx_glue.c
+++ b/arch/x86/crypto/twofish_avx_glue.c
@@ -100,14 +100,12 @@ static struct skcipher_alg twofish_algs[] = {
 	},
 };
 
 static int __init twofish_init(void)
 {
-	const char *feature_name;
-
-	if (!cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM, &feature_name)) {
-		pr_info("CPU feature '%s' is not supported.\n", feature_name);
+	if (!boot_cpu_has(X86_FEATURE_AVX)) {
+		pr_info("AVX instructions are not detected.\n");
 		return -ENODEV;
 	}
 
 	return crypto_register_skciphers(twofish_algs,
 					 ARRAY_SIZE(twofish_algs));
-- 
2.54.0



From ebiggers at kernel.org  Thu Jun 25 21:37:29 2026
From: ebiggers at kernel.org (Eric Biggers)
Date: Thu, 25 Jun 2026 21:37:29 -0700
Subject: [PATCH 6/8] x86/fpu: Remove cpu_has_xfeatures()
In-Reply-To: <20260626043731.319287-1-ebiggers@kernel.org>
References: <20260626043731.319287-1-ebiggers@kernel.org>
Message-ID: <20260626043731.319287-7-ebiggers@kernel.org>

The only remaining caller of cpu_has_xfeatures() is
print_xstate_features(), which uses it only to check and get the name of
a single feature.

Remove it and just inline the needed code into print_xstate_features().

This also makes the "unknown xstate feature" entry at index XFEATURE_MAX
of xfeature_names[] unnecessary, so remove that too.

Signed-off-by: Eric Biggers <ebiggers at kernel.org>
---
 arch/x86/include/asm/fpu/api.h |  9 -------
 arch/x86/kernel/fpu/xstate.c   | 44 +++-------------------------------
 2 files changed, 3 insertions(+), 50 deletions(-)

diff --git a/arch/x86/include/asm/fpu/api.h b/arch/x86/include/asm/fpu/api.h
index 90c63fe19c0f..cfed8b24d64f 100644
--- a/arch/x86/include/asm/fpu/api.h
+++ b/arch/x86/include/asm/fpu/api.h
@@ -97,19 +97,10 @@ static inline void fpregs_assert_state_consistent(void) { }
 /*
  * Load the task FPU state before returning to userspace.
  */
 extern void switch_fpu_return(void);
 
-/*
- * Query the presence of one or more xfeatures. Works on any legacy CPU as well.
- *
- * If 'feature_name' is set then put a human-readable description of
- * the feature there as well - this can be used to print error (or success)
- * messages.
- */
-extern int cpu_has_xfeatures(u64 xfeatures_mask, const char **feature_name);
-
 /* Trap handling */
 extern int  fpu__exception_code(struct fpu *fpu, int trap_nr);
 extern void fpu_sync_fpstate(struct fpu *fpu);
 extern void fpu_reset_from_exception_fixup(void);
 
diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c
index 7f7e62e4ebc5..c6f0264f957c 100644
--- a/arch/x86/kernel/fpu/xstate.c
+++ b/arch/x86/kernel/fpu/xstate.c
@@ -64,12 +64,12 @@ static const char *xfeature_names[] =
 	"unknown xstate feature",
 	"unknown xstate feature",
 	"AMX Tile config",
 	"AMX Tile data",
 	"APX registers",
-	"unknown xstate feature",
 };
+static_assert(ARRAY_SIZE(xfeature_names) == XFEATURE_MAX);
 
 static unsigned short xsave_cpuid_features[] __initdata = {
 	[XFEATURE_FP]				= X86_FEATURE_FPU,
 	[XFEATURE_SSE]				= X86_FEATURE_XMM,
 	[XFEATURE_YMM]				= X86_FEATURE_AVX,
@@ -120,48 +120,10 @@ static inline unsigned int next_xfeature_order(unsigned int i, u64 mask)
 	     i++)
 
 #define XSTATE_FLAG_SUPERVISOR	BIT(0)
 #define XSTATE_FLAG_ALIGNED64	BIT(1)
 
-/*
- * Return whether the system supports a given xfeature.
- *
- * Also return the name of the (most advanced) feature that the caller requested:
- */
-int cpu_has_xfeatures(u64 xfeatures_needed, const char **feature_name)
-{
-	u64 xfeatures_missing = xfeatures_needed & ~fpu_kernel_cfg.max_features;
-
-	if (unlikely(feature_name)) {
-		long xfeature_idx, max_idx;
-		u64 xfeatures_print;
-		/*
-		 * So we use FLS here to be able to print the most advanced
-		 * feature that was requested but is missing. So if a driver
-		 * asks about "XFEATURE_MASK_SSE | XFEATURE_MASK_YMM" we'll print the
-		 * missing AVX feature - this is the most informative message
-		 * to users:
-		 */
-		if (xfeatures_missing)
-			xfeatures_print = xfeatures_missing;
-		else
-			xfeatures_print = xfeatures_needed;
-
-		xfeature_idx = fls64(xfeatures_print)-1;
-		max_idx = ARRAY_SIZE(xfeature_names)-1;
-		xfeature_idx = min(xfeature_idx, max_idx);
-
-		*feature_name = xfeature_names[xfeature_idx];
-	}
-
-	if (xfeatures_missing)
-		return 0;
-
-	return 1;
-}
-EXPORT_SYMBOL_GPL(cpu_has_xfeatures);
-
 static bool xfeature_is_aligned64(int xfeature_nr)
 {
 	return xstate_flags[xfeature_nr] & XSTATE_FLAG_ALIGNED64;
 }
 
@@ -300,13 +262,13 @@ static void __init print_xstate_features(void)
 {
 	int i;
 
 	for (i = 0; i < XFEATURE_MAX; i++) {
 		u64 mask = BIT_ULL(i);
-		const char *name;
+		const char *name = xfeature_names[i];
 
-		if (cpu_has_xfeatures(mask, &name))
+		if (fpu_kernel_cfg.max_features & mask)
 			pr_info("x86/fpu: Supporting XSAVE feature 0x%03Lx: '%s'\n", mask, name);
 	}
 }
 
 /*
-- 
2.54.0



From ebiggers at kernel.org  Thu Jun 25 21:37:30 2026
From: ebiggers at kernel.org (Eric Biggers)
Date: Thu, 25 Jun 2026 21:37:30 -0700
Subject: [PATCH 7/8] lib/raid/xor: x86: Remove redundant X86_FEATURE_OSXSAVE check
In-Reply-To: <20260626043731.319287-1-ebiggers@kernel.org>
References: <20260626043731.319287-1-ebiggers@kernel.org>
Message-ID: <20260626043731.319287-8-ebiggers@kernel.org>

X86_FEATURE_AVX implies X86_FEATURE_OSXSAVE already.

Signed-off-by: Eric Biggers <ebiggers at kernel.org>
---
 lib/raid/xor/x86/xor_arch.h | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/lib/raid/xor/x86/xor_arch.h b/lib/raid/xor/x86/xor_arch.h
index 99fe85a213c6..991abe3f4bbd 100644
--- a/lib/raid/xor/x86/xor_arch.h
+++ b/lib/raid/xor/x86/xor_arch.h
@@ -16,12 +16,11 @@ extern struct xor_block_template xor_block_avx;
  *
  * 32-bit without MMX can fall back to the generic routines.
  */
 static __always_inline void __init arch_xor_init(void)
 {
-	if (boot_cpu_has(X86_FEATURE_AVX) &&
-	    boot_cpu_has(X86_FEATURE_OSXSAVE)) {
+	if (boot_cpu_has(X86_FEATURE_AVX)) {
 		xor_force(&xor_block_avx);
 	} else if (IS_ENABLED(CONFIG_X86_64) || boot_cpu_has(X86_FEATURE_XMM)) {
 		xor_register(&xor_block_sse);
 		xor_register(&xor_block_sse_pf64);
 	} else if (boot_cpu_has(X86_FEATURE_MMX)) {
-- 
2.54.0



From ebiggers at kernel.org  Thu Jun 25 21:37:28 2026
From: ebiggers at kernel.org (Eric Biggers)
Date: Thu, 25 Jun 2026 21:37:28 -0700
Subject: [PATCH 5/8] lib/crc: x86: Stop using cpu_has_xfeatures()
In-Reply-To: <20260626043731.319287-1-ebiggers@kernel.org>
References: <20260626043731.319287-1-ebiggers@kernel.org>
Message-ID: <20260626043731.319287-6-ebiggers@kernel.org>

Checking both boot_cpu_has() and cpu_has_xfeatures() has never really
been needed in practice, and it's never been universally done (e.g.,
lib/raid/ omits cpu_has_xfeatures()).  Nevertheless, both x86 and UML
now explicitly clear the AVX and AVX-512 flags if their xfeatures are
missing, which should remove any remaining doubts.

Thus, remove all the calls to cpu_has_xfeatures().

Signed-off-by: Eric Biggers <ebiggers at kernel.org>
---
 lib/crc/x86/crc-pclmul-template.h | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/lib/crc/x86/crc-pclmul-template.h b/lib/crc/x86/crc-pclmul-template.h
index 02744831c6fa..893119bb7c07 100644
--- a/lib/crc/x86/crc-pclmul-template.h
+++ b/lib/crc/x86/crc-pclmul-template.h
@@ -25,20 +25,18 @@ crc_t prefix##_vpclmul_avx512(crc_t crc, const u8 *p, size_t len,	\
 DEFINE_STATIC_CALL(prefix##_pclmul, prefix##_pclmul_sse)
 
 static inline bool have_vpclmul(void)
 {
 	return boot_cpu_has(X86_FEATURE_VPCLMULQDQ) &&
-	       boot_cpu_has(X86_FEATURE_AVX2) &&
-	       cpu_has_xfeatures(XFEATURE_MASK_YMM, NULL);
+	       boot_cpu_has(X86_FEATURE_AVX2);
 }
 
 static inline bool have_avx512(void)
 {
 	return boot_cpu_has(X86_FEATURE_AVX512BW) &&
 	       boot_cpu_has(X86_FEATURE_AVX512VL) &&
-	       !boot_cpu_has(X86_FEATURE_PREFER_YMM) &&
-	       cpu_has_xfeatures(XFEATURE_MASK_AVX512, NULL);
+	       !boot_cpu_has(X86_FEATURE_PREFER_YMM);
 }
 
 /*
  * Call a [V]PCLMULQDQ optimized CRC function if the data length is at least 16
  * bytes, the CPU has PCLMULQDQ support, and the current context may use SIMD.
-- 
2.54.0



From ebiggers at kernel.org  Thu Jun 25 21:37:27 2026
From: ebiggers at kernel.org (Eric Biggers)
Date: Thu, 25 Jun 2026 21:37:27 -0700
Subject: [PATCH 4/8] lib/crypto: x86: Stop using cpu_has_xfeatures()
In-Reply-To: <20260626043731.319287-1-ebiggers@kernel.org>
References: <20260626043731.319287-1-ebiggers@kernel.org>
Message-ID: <20260626043731.319287-5-ebiggers@kernel.org>

Checking both boot_cpu_has() and cpu_has_xfeatures() has never really
been needed in practice, and it's never been universally done (e.g.,
lib/raid/ omits cpu_has_xfeatures()).  Nevertheless, both x86 and UML
now explicitly clear the AVX and AVX-512 flags if their xfeatures are
missing, which should remove any remaining doubts.

Thus, remove all the calls to cpu_has_xfeatures().

Signed-off-by: Eric Biggers <ebiggers at kernel.org>
---
 lib/crypto/x86/blake2s.h  | 4 +---
 lib/crypto/x86/chacha.h   | 3 +--
 lib/crypto/x86/nh.h       | 4 +---
 lib/crypto/x86/poly1305.h | 7 ++-----
 lib/crypto/x86/sha1.h     | 4 +---
 lib/crypto/x86/sha256.h   | 4 +---
 lib/crypto/x86/sha512.h   | 3 +--
 lib/crypto/x86/sm3.h      | 3 +--
 8 files changed, 9 insertions(+), 23 deletions(-)

diff --git a/lib/crypto/x86/blake2s.h b/lib/crypto/x86/blake2s.h
index f8eed6cb042e..0f7c51f055c8 100644
--- a/lib/crypto/x86/blake2s.h
+++ b/lib/crypto/x86/blake2s.h
@@ -53,10 +53,8 @@ static void blake2s_mod_init_arch(void)
 		static_branch_enable(&blake2s_use_ssse3);
 
 	if (boot_cpu_has(X86_FEATURE_AVX) &&
 	    boot_cpu_has(X86_FEATURE_AVX2) &&
 	    boot_cpu_has(X86_FEATURE_AVX512F) &&
-	    boot_cpu_has(X86_FEATURE_AVX512VL) &&
-	    cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM |
-			      XFEATURE_MASK_AVX512, NULL))
+	    boot_cpu_has(X86_FEATURE_AVX512VL))
 		static_branch_enable(&blake2s_use_avx512);
 }
diff --git a/lib/crypto/x86/chacha.h b/lib/crypto/x86/chacha.h
index 10cf8f1c569d..c79562aac56b 100644
--- a/lib/crypto/x86/chacha.h
+++ b/lib/crypto/x86/chacha.h
@@ -163,12 +163,11 @@ static void chacha_mod_init_arch(void)
 		return;
 
 	static_branch_enable(&chacha_use_simd);
 
 	if (boot_cpu_has(X86_FEATURE_AVX) &&
-	    boot_cpu_has(X86_FEATURE_AVX2) &&
-	    cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM, NULL)) {
+	    boot_cpu_has(X86_FEATURE_AVX2)) {
 		static_branch_enable(&chacha_use_avx2);
 
 		if (boot_cpu_has(X86_FEATURE_AVX512VL) &&
 		    boot_cpu_has(X86_FEATURE_AVX512BW)) /* kmovq */
 			static_branch_enable(&chacha_use_avx512vl);
diff --git a/lib/crypto/x86/nh.h b/lib/crypto/x86/nh.h
index 83361c2e9783..342636dcb750 100644
--- a/lib/crypto/x86/nh.h
+++ b/lib/crypto/x86/nh.h
@@ -35,11 +35,9 @@ static bool nh_arch(const u32 *key, const u8 *message, size_t message_len,
 #define nh_mod_init_arch nh_mod_init_arch
 static void nh_mod_init_arch(void)
 {
 	if (boot_cpu_has(X86_FEATURE_XMM2)) {
 		static_branch_enable(&have_sse2);
-		if (boot_cpu_has(X86_FEATURE_AVX2) &&
-		    cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM,
-				      NULL))
+		if (boot_cpu_has(X86_FEATURE_AVX2))
 			static_branch_enable(&have_avx2);
 	}
 }
diff --git a/lib/crypto/x86/poly1305.h b/lib/crypto/x86/poly1305.h
index ee92e3740a78..b061b9926fa5 100644
--- a/lib/crypto/x86/poly1305.h
+++ b/lib/crypto/x86/poly1305.h
@@ -141,18 +141,15 @@ static void poly1305_emit(const struct poly1305_state *ctx,
 }
 
 #define poly1305_mod_init_arch poly1305_mod_init_arch
 static void poly1305_mod_init_arch(void)
 {
-	if (boot_cpu_has(X86_FEATURE_AVX) &&
-	    cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM, NULL))
+	if (boot_cpu_has(X86_FEATURE_AVX))
 		static_branch_enable(&poly1305_use_avx);
-	if (boot_cpu_has(X86_FEATURE_AVX) && boot_cpu_has(X86_FEATURE_AVX2) &&
-	    cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM, NULL))
+	if (boot_cpu_has(X86_FEATURE_AVX) && boot_cpu_has(X86_FEATURE_AVX2))
 		static_branch_enable(&poly1305_use_avx2);
 	if (boot_cpu_has(X86_FEATURE_AVX) && boot_cpu_has(X86_FEATURE_AVX2) &&
 	    boot_cpu_has(X86_FEATURE_AVX512F) &&
-	    cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM | XFEATURE_MASK_AVX512, NULL) &&
 	    /* Skylake downclocks unacceptably much when using zmm, but later generations are fast. */
 	    boot_cpu_data.x86_vfm != INTEL_SKYLAKE_X)
 		static_branch_enable(&poly1305_use_avx512);
 }
diff --git a/lib/crypto/x86/sha1.h b/lib/crypto/x86/sha1.h
index c48a0131fd12..6aff433466e7 100644
--- a/lib/crypto/x86/sha1.h
+++ b/lib/crypto/x86/sha1.h
@@ -57,13 +57,11 @@ static void sha1_blocks(struct sha1_block_state *state,
 #define sha1_mod_init_arch sha1_mod_init_arch
 static void sha1_mod_init_arch(void)
 {
 	if (boot_cpu_has(X86_FEATURE_SHA_NI)) {
 		static_call_update(sha1_blocks_x86, sha1_blocks_ni);
-	} else if (cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM,
-				     NULL) &&
-		   boot_cpu_has(X86_FEATURE_AVX)) {
+	} else if (boot_cpu_has(X86_FEATURE_AVX)) {
 		if (boot_cpu_has(X86_FEATURE_AVX2) &&
 		    boot_cpu_has(X86_FEATURE_BMI1) &&
 		    boot_cpu_has(X86_FEATURE_BMI2))
 			static_call_update(sha1_blocks_x86, sha1_blocks_avx2);
 		else
diff --git a/lib/crypto/x86/sha256.h b/lib/crypto/x86/sha256.h
index 0ee69d8e39fe..e98ffdaf4b14 100644
--- a/lib/crypto/x86/sha256.h
+++ b/lib/crypto/x86/sha256.h
@@ -102,13 +102,11 @@ static void sha256_mod_init_arch(void)
 		static_branch_enable(&have_sha_ni);
 	} else if (IS_ENABLED(CONFIG_CPU_SUP_ZHAOXIN) &&
 		   boot_cpu_has(X86_FEATURE_PHE_EN) &&
 		   boot_cpu_data.x86 >= 0x07) {
 		static_call_update(sha256_blocks_x86, sha256_blocks_phe);
-	} else if (cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM,
-				     NULL) &&
-		   boot_cpu_has(X86_FEATURE_AVX)) {
+	} else if (boot_cpu_has(X86_FEATURE_AVX)) {
 		if (boot_cpu_has(X86_FEATURE_AVX2) &&
 		    boot_cpu_has(X86_FEATURE_BMI2))
 			static_call_update(sha256_blocks_x86,
 					   sha256_blocks_avx2);
 		else
diff --git a/lib/crypto/x86/sha512.h b/lib/crypto/x86/sha512.h
index 0213c70cedd0..4e177b4606bd 100644
--- a/lib/crypto/x86/sha512.h
+++ b/lib/crypto/x86/sha512.h
@@ -35,12 +35,11 @@ static void sha512_blocks(struct sha512_block_state *state,
 }
 
 #define sha512_mod_init_arch sha512_mod_init_arch
 static void sha512_mod_init_arch(void)
 {
-	if (cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM, NULL) &&
-	    boot_cpu_has(X86_FEATURE_AVX)) {
+	if (boot_cpu_has(X86_FEATURE_AVX)) {
 		if (boot_cpu_has(X86_FEATURE_AVX2) &&
 		    boot_cpu_has(X86_FEATURE_BMI2))
 			static_call_update(sha512_blocks_x86,
 					   sha512_blocks_avx2);
 		else
diff --git a/lib/crypto/x86/sm3.h b/lib/crypto/x86/sm3.h
index 3834780f2f6a..e06d4a22e4fa 100644
--- a/lib/crypto/x86/sm3.h
+++ b/lib/crypto/x86/sm3.h
@@ -31,9 +31,8 @@ static void sm3_blocks(struct sm3_block_state *state,
 }
 
 #define sm3_mod_init_arch sm3_mod_init_arch
 static void sm3_mod_init_arch(void)
 {
-	if (boot_cpu_has(X86_FEATURE_AVX) && boot_cpu_has(X86_FEATURE_BMI2) &&
-	    cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM, NULL))
+	if (boot_cpu_has(X86_FEATURE_AVX) && boot_cpu_has(X86_FEATURE_BMI2))
 		static_call_update(sm3_blocks_x86, sm3_blocks_avx);
 }
-- 
2.54.0



From ebiggers at kernel.org  Thu Jun 25 21:37:31 2026
From: ebiggers at kernel.org (Eric Biggers)
Date: Thu, 25 Jun 2026 21:37:31 -0700
Subject: [PATCH 8/8] lib/raid/xor: x86: Add AVX-512 optimized xor_gen()
In-Reply-To: <20260626043731.319287-1-ebiggers@kernel.org>
References: <20260626043731.319287-1-ebiggers@kernel.org>
Message-ID: <20260626043731.319287-9-ebiggers@kernel.org>

Add an implementation of xor_gen() using AVX-512.

It uses 512-bit vectors, i.e. ZMM registers.  It also uses the
vpternlogq instruction to do three-input XORs when applicable.

It's enabled on x86_64 CPUs that have AVX512F && !PREFER_YMM.  In
practice that means:

    - AMD Zen 4 and later (client and server)
    - Intel Sapphire Rapids and later (server)
    - Intel Rocket Lake (client)
    - Intel Nova Lake and later (client)

The !PREFER_YMM condition excludes the older AVX-512 implementations in
Intel Skylake Server and Intel Ice Lake.  They could run this code, but
they're known to have overly-eager downclocking when ZMM registers are
used.  This is the same policy that the crypto and CRC code uses.

Benchmark on AMD Ryzen 9 9950X (Zen 5):

    src_cnt    avx          avx512       Improvement
    =======    ==========   ==========   ===========
    1          56353 MB/s   75388 MB/s   33%
    2          54274 MB/s   68409 MB/s   26%
    3          44649 MB/s   64042 MB/s   43%
    4          41315 MB/s   55002 MB/s   33%

Reviewed-by: David Laight <david.laight.linux at gmail.com>
Signed-off-by: Eric Biggers <ebiggers at kernel.org>
---
 lib/raid/xor/Makefile         |   2 +-
 lib/raid/xor/x86/xor-avx512.c | 121 ++++++++++++++++++++++++++++++++++
 lib/raid/xor/x86/xor_arch.h   |  23 ++++---
 3 files changed, 135 insertions(+), 11 deletions(-)
 create mode 100644 lib/raid/xor/x86/xor-avx512.c

diff --git a/lib/raid/xor/Makefile b/lib/raid/xor/Makefile
index e8ecec3c09f9..4a0e5c6d8298 100644
--- a/lib/raid/xor/Makefile
+++ b/lib/raid/xor/Makefile
@@ -27,11 +27,11 @@ xor-$(CONFIG_ALTIVEC)		+= powerpc/xor_vmx.o powerpc/xor_vmx_glue.o
 xor-$(CONFIG_RISCV_ISA_V)	+= riscv/xor.o riscv/xor-glue.o
 xor-$(CONFIG_SPARC32)		+= sparc/xor-sparc32.o
 xor-$(CONFIG_SPARC64)		+= sparc/xor-sparc64.o sparc/xor-sparc64-glue.o
 xor-$(CONFIG_S390)		+= s390/xor.o
 xor-$(CONFIG_X86_32)		+= x86/xor-avx.o x86/xor-sse.o x86/xor-mmx.o
-xor-$(CONFIG_X86_64)		+= x86/xor-avx.o x86/xor-sse.o
+xor-$(CONFIG_X86_64)		+= x86/xor-avx.o x86/xor-sse.o x86/xor-avx512.o
 obj-y				+= tests/
 
 CFLAGS_xor-neon.o		+= $(CC_FLAGS_FPU) -I$(src)/$(SRCARCH)
 CFLAGS_REMOVE_xor-neon.o	+= $(CC_FLAGS_NO_FPU)
 
diff --git a/lib/raid/xor/x86/xor-avx512.c b/lib/raid/xor/x86/xor-avx512.c
new file mode 100644
index 000000000000..17f57900d827
--- /dev/null
+++ b/lib/raid/xor/x86/xor-avx512.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * AVX-512 optimized implementation of xor_gen()
+ *
+ * Copyright 2026 Google LLC
+ */
+
+#include <linux/types.h>
+#include <asm/fpu/api.h>
+#include "xor_impl.h"
+#include "xor_arch.h"
+
+/*
+ * Implementation notes:
+ *
+ * Unrolling by the number of buffers (2-5) is very important.
+ *
+ * Unrolling by length is less important, especially when using register-indexed
+ * addressing with negative indices from the end of the buffers.  That approach
+ * results in just two loop control instructions being needed per iteration,
+ * regardless of the number of buffers.
+ *
+ * In fact, benchmarks showed that the 2 and 3 buffer cases require only 2x
+ * unrolling by length, while the 4 and 5 buffer cases don't require any
+ * unrolling by length.  Benchmarks also showed that the register-indexed
+ * addressing isn't a bottleneck either; i.e., we can't do any better by
+ * incrementing the pointers as we go along, even with more unrolling.
+ */
+
+static void xor_avx512_2(long bytes, u8 *p1, const u8 *p2)
+{
+	long i = -bytes;
+
+	asm volatile("1: vmovdqa64 (%1,%0), %%zmm0\n"
+		     "vmovdqa64 64(%1,%0), %%zmm1\n"
+		     "vpxorq (%2,%0), %%zmm0, %%zmm0\n"
+		     "vpxorq 64(%2,%0), %%zmm1, %%zmm1\n"
+		     "vmovdqa64 %%zmm0, (%1,%0)\n"
+		     "vmovdqa64 %%zmm1, 64(%1,%0)\n"
+		     "add $128, %0\n"
+		     "jnz 1b\n"
+		     : "+&r"(i)
+		     : "r"(p1 + bytes), "r"(p2 + bytes)
+		     : "memory", "cc");
+}
+
+static void xor_avx512_3(long bytes, u8 *p1, const u8 *p2, const u8 *p3)
+{
+	long i = -bytes;
+
+	asm volatile("1: vmovdqa64 (%1,%0), %%zmm0\n"
+		     "vmovdqa64 64(%1,%0), %%zmm1\n"
+		     "vmovdqa64 (%2,%0), %%zmm2\n"
+		     "vmovdqa64 64(%2,%0), %%zmm3\n"
+		     "vpternlogq $0x96, (%3,%0), %%zmm2, %%zmm0\n"
+		     "vpternlogq $0x96, 64(%3,%0), %%zmm3, %%zmm1\n"
+		     "vmovdqa64 %%zmm0, (%1,%0)\n"
+		     "vmovdqa64 %%zmm1, 64(%1,%0)\n"
+		     "add $128, %0\n"
+		     "jnz 1b\n"
+		     : "+&r"(i)
+		     : "r"(p1 + bytes), "r"(p2 + bytes), "r"(p3 + bytes)
+		     : "memory", "cc");
+}
+
+static void xor_avx512_4(long bytes, u8 *p1, const u8 *p2, const u8 *p3,
+			 const u8 *p4)
+{
+	long i = -bytes;
+
+	asm volatile("1: vmovdqa64 (%1,%0), %%zmm0\n"
+		     "vmovdqa64 (%2,%0), %%zmm1\n"
+		     "vpxorq (%3,%0), %%zmm0, %%zmm0\n"
+		     "vpternlogq $0x96, (%4,%0), %%zmm1, %%zmm0\n"
+		     "vmovdqa64 %%zmm0, (%1,%0)\n"
+		     "add $64, %0\n"
+		     "jnz 1b\n"
+		     : "+&r"(i)
+		     : "r"(p1 + bytes), "r"(p2 + bytes), "r"(p3 + bytes),
+		       "r"(p4 + bytes)
+		     : "memory", "cc");
+}
+
+static void xor_avx512_5(long bytes, u8 *p1, const u8 *p2, const u8 *p3,
+			 const u8 *p4, const u8 *p5)
+{
+	long i = -bytes;
+
+	asm volatile("1: vmovdqa64 (%1,%0), %%zmm0\n"
+		     "vmovdqa64 (%2,%0), %%zmm1\n"
+		     "vpternlogq $0x96, (%3,%0), %%zmm1, %%zmm0\n"
+		     "vmovdqa64 (%4,%0), %%zmm1\n"
+		     "vpternlogq $0x96, (%5,%0), %%zmm1, %%zmm0\n"
+		     "vmovdqa64 %%zmm0, (%1,%0)\n"
+		     "add $64, %0\n"
+		     "jnz 1b\n"
+		     : "+&r"(i)
+		     : "r"(p1 + bytes), "r"(p2 + bytes), "r"(p3 + bytes),
+		       "r"(p4 + bytes), "r"(p5 + bytes)
+		     : "memory", "cc");
+}
+
+DO_XOR_BLOCKS(avx512_inner, xor_avx512_2, xor_avx512_3, xor_avx512_4,
+	      xor_avx512_5);
+
+/*
+ * Preconditions: bytes is a nonzero multiple of 512, and all buffers are
+ * 64-byte aligned.
+ */
+static void xor_gen_avx512(void *dest, void **srcs, unsigned int src_cnt,
+			   unsigned int bytes)
+{
+	kernel_fpu_begin();
+	xor_gen_avx512_inner(dest, srcs, src_cnt, bytes);
+	kernel_fpu_end();
+}
+
+struct xor_block_template xor_block_avx512 = {
+	.name = "avx512",
+	.xor_gen = xor_gen_avx512,
+};
diff --git a/lib/raid/xor/x86/xor_arch.h b/lib/raid/xor/x86/xor_arch.h
index 991abe3f4bbd..d5e192b8793f 100644
--- a/lib/raid/xor/x86/xor_arch.h
+++ b/lib/raid/xor/x86/xor_arch.h
@@ -4,25 +4,28 @@
 extern struct xor_block_template xor_block_pII_mmx;
 extern struct xor_block_template xor_block_p5_mmx;
 extern struct xor_block_template xor_block_sse;
 extern struct xor_block_template xor_block_sse_pf64;
 extern struct xor_block_template xor_block_avx;
+extern struct xor_block_template xor_block_avx512;
 
-/*
- * When SSE is available, use it as it can write around L2.  We may also be able
- * to load into the L1 only depending on how the cpu deals with a load to a line
- * that is being prefetched.
- *
- * When AVX2 is available, force using it as it is better by all measures.
- *
- * 32-bit without MMX can fall back to the generic routines.
- */
 static __always_inline void __init arch_xor_init(void)
 {
-	if (boot_cpu_has(X86_FEATURE_AVX)) {
+	if (IS_ENABLED(CONFIG_X86_64) && boot_cpu_has(X86_FEATURE_AVX512F) &&
+	    !boot_cpu_has(X86_FEATURE_PREFER_YMM)) {
+		/* AVX-512 will be the best; no need to try others. */
+		/* !PREFER_YMM excludes CPUs with overly-eager downclocking. */
+		xor_force(&xor_block_avx512);
+	} else if (boot_cpu_has(X86_FEATURE_AVX)) {
+		/* AVX will be the best; no need to try others. */
 		xor_force(&xor_block_avx);
 	} else if (IS_ENABLED(CONFIG_X86_64) || boot_cpu_has(X86_FEATURE_XMM)) {
+		/*
+		 * When SSE is available, use it as it can write around L2.  We
+		 * may also be able to load into the L1 only depending on how
+		 * the cpu deals with a load to a line that is being prefetched.
+		 */
 		xor_register(&xor_block_sse);
 		xor_register(&xor_block_sse_pf64);
 	} else if (boot_cpu_has(X86_FEATURE_MMX)) {
 		xor_register(&xor_block_pII_mmx);
 		xor_register(&xor_block_p5_mmx);
-- 
2.54.0



From hch at lst.de  Thu Jun 25 22:39:55 2026
From: hch at lst.de (Christoph Hellwig)
Date: Fri, 26 Jun 2026 07:39:55 +0200
Subject: [PATCH 1/8] x86/fpu: Check for missing AVX and AVX-512 xstate
 bits
In-Reply-To: <20260626043731.319287-2-ebiggers@kernel.org>
References: <20260626043731.319287-1-ebiggers@kernel.org> <20260626043731.319287-2-ebiggers@kernel.org>
Message-ID: <20260626053955.GA9629@lst.de>

On Thu, Jun 25, 2026 at 09:37:24PM -0700, Eric Biggers wrote:
> If the CPU declares AVX or AVX-512 support, verify that the
> corresponding xstate bits are also set.  If not, warn and clear them.
> 
> This eliminates the perceived need for AVX and AVX-512 optimized code in
> the kernel to call cpu_has_xfeatures().  That has never been universally
> done, which strongly suggests that it has never really been needed in
> practice, but this should remove any remaining doubt.

I'll leave it to the x86-experts if the low-level details are right,
but the model behind this makes life so much easier, thanks a lot!



From hch at lst.de  Thu Jun 25 22:40:23 2026
From: hch at lst.de (Christoph Hellwig)
Date: Fri, 26 Jun 2026 07:40:23 +0200
Subject: [PATCH 7/8] lib/raid/xor: x86: Remove redundant
 X86_FEATURE_OSXSAVE check
In-Reply-To: <20260626043731.319287-8-ebiggers@kernel.org>
References: <20260626043731.319287-1-ebiggers@kernel.org> <20260626043731.319287-8-ebiggers@kernel.org>
Message-ID: <20260626054023.GB9629@lst.de>

Looks good:

Reviewed-by: Christoph Hellwig <hch at lst.de>



From hch at lst.de  Thu Jun 25 22:47:31 2026
From: hch at lst.de (Christoph Hellwig)
Date: Fri, 26 Jun 2026 07:47:31 +0200
Subject: [PATCH 8/8] lib/raid/xor: x86: Add AVX-512 optimized xor_gen()
In-Reply-To: <20260626043731.319287-9-ebiggers@kernel.org>
References: <20260626043731.319287-1-ebiggers@kernel.org> <20260626043731.319287-9-ebiggers@kernel.org>
Message-ID: <20260626054731.GC9629@lst.de>

On Thu, Jun 25, 2026 at 09:37:31PM -0700, Eric Biggers wrote:
> +	if (IS_ENABLED(CONFIG_X86_64) && boot_cpu_has(X86_FEATURE_AVX512F) &&
> +	    !boot_cpu_has(X86_FEATURE_PREFER_YMM)) {
> +		/* AVX-512 will be the best; no need to try others. */
> +		/* !PREFER_YMM excludes CPUs with overly-eager downclocking. */

Can you turn this into a single block comment using full sentences?
Right now the two separate comments almost feel contradictory even
if I get what you mean.  While you're at it also through in a blurb
why we dont bother with AVX-512 (number of register, no one in the right
mind would bother running high performance code on modern cpus in 32-bit
mode).



From hch at lst.de  Thu Jun 25 22:47:49 2026
From: hch at lst.de (Christoph Hellwig)
Date: Fri, 26 Jun 2026 07:47:49 +0200
Subject: [PATCH 8/8] lib/raid/xor: x86: Add AVX-512 optimized xor_gen()
In-Reply-To: <20260626054731.GC9629@lst.de>
References: <20260626043731.319287-1-ebiggers@kernel.org> <20260626043731.319287-9-ebiggers@kernel.org> <20260626054731.GC9629@lst.de>
Message-ID: <20260626054749.GD9629@lst.de>

On Fri, Jun 26, 2026 at 07:47:31AM +0200, Christoph Hellwig wrote:
> On Thu, Jun 25, 2026 at 09:37:31PM -0700, Eric Biggers wrote:
> > +	if (IS_ENABLED(CONFIG_X86_64) && boot_cpu_has(X86_FEATURE_AVX512F) &&
> > +	    !boot_cpu_has(X86_FEATURE_PREFER_YMM)) {
> > +		/* AVX-512 will be the best; no need to try others. */
> > +		/* !PREFER_YMM excludes CPUs with overly-eager downclocking. */
> 
> Can you turn this into a single block comment using full sentences?
> Right now the two separate comments almost feel contradictory even
> if I get what you mean.  While you're at it also through in a blurb
> why we dont bother with AVX-512 (number of register, no one in the right
> mind would bother running high performance code on modern cpus in 32-bit
> mode).

Otherwise looks good, btw:

Reviewed-by: Christoph Hellwig <hch at lst.de>


From david.laight.linux at gmail.com  Fri Jun 26 00:41:13 2026
From: david.laight.linux at gmail.com (David Laight)
Date: Fri, 26 Jun 2026 08:41:13 +0100
Subject: [PATCH 2/8] um: Check for missing AVX and AVX-512 xstate bits
In-Reply-To: <20260626043731.319287-3-ebiggers@kernel.org>
References: <20260626043731.319287-1-ebiggers@kernel.org>
	<20260626043731.319287-3-ebiggers@kernel.org>
Message-ID: <20260626084113.42eae31c@pumpkin>

On Thu, 25 Jun 2026 21:37:25 -0700
Eric Biggers <ebiggers at kernel.org> wrote:

> If the CPU declares AVX or AVX-512 support, verify that all the
> corresponding xstate bits are also set.  If any are missing, warn and
> don't set the corresponding X86_FEATURE_* flags.
> 
> This eliminates the perceived need for UML-supporting AVX and AVX-512
> optimized code in the kernel (that is, lib/raid/ currently) to start
> checking the xstate bits in addition to X86_FEATURE_AVX*.
> 
...
>  static void __init parse_host_cpu_flags(char *line)
>  {
> +	u64 xcr0 = read_xcr0();
>  	int i;
> +
>  	for (i = 0; i < 32*NCAPINTS; i++) {
>  		if ((x86_cap_flags[i] != NULL) && strstr(line, x86_cap_flags[i]))

'line' comes from /proc/cpuinfo
Surely something would be terribly wrong if that included something the kernel
had disabled (or didn't support).

	David


> -			set_cpu_cap(&boot_cpu_data, i);
> +			validate_and_set_cpu_cap(i, xcr0);
>  	}
>  }
>  
>  static void __init parse_cache_line(char *line)
>  {



From anton.ivanov at cambridgegreys.com  Fri Jun 26 01:21:49 2026
From: anton.ivanov at cambridgegreys.com (Anton Ivanov)
Date: Fri, 26 Jun 2026 09:21:49 +0100
Subject: [PATCH 2/8] um: Check for missing AVX and AVX-512 xstate bits
In-Reply-To: <20260626084113.42eae31c@pumpkin>
References: <20260626043731.319287-1-ebiggers@kernel.org>
 <20260626043731.319287-3-ebiggers@kernel.org>
 <20260626084113.42eae31c@pumpkin>
Message-ID: <6a20b442-b97f-4cae-9168-30201d5ef82c@cambridgegreys.com>



On 26/06/2026 08:41, David Laight wrote:
> On Thu, 25 Jun 2026 21:37:25 -0700
> Eric Biggers <ebiggers at kernel.org> wrote:
> 
>> If the CPU declares AVX or AVX-512 support, verify that all the
>> corresponding xstate bits are also set.  If any are missing, warn and
>> don't set the corresponding X86_FEATURE_* flags.
>>
>> This eliminates the perceived need for UML-supporting AVX and AVX-512
>> optimized code in the kernel (that is, lib/raid/ currently) to start
>> checking the xstate bits in addition to X86_FEATURE_AVX*.
>>
> ...
>>   static void __init parse_host_cpu_flags(char *line)
>>   {
>> +	u64 xcr0 = read_xcr0();
>>   	int i;
>> +
>>   	for (i = 0; i < 32*NCAPINTS; i++) {
>>   		if ((x86_cap_flags[i] != NULL) && strstr(line, x86_cap_flags[i]))
> 
> 'line' comes from /proc/cpuinfo
> Surely something would be terribly wrong if that included something the kernel
> had disabled (or didn't support).
> 
> 	David
> 
> 
>> -			set_cpu_cap(&boot_cpu_data, i);
>> +			validate_and_set_cpu_cap(i, xcr0);
>>   	}
>>   }
>>   
>>   static void __init parse_cache_line(char *line)
>>   {
> 
> 
> 
 >
Lots of other stuff will go wrong before that. Glibc, things compiled with LLVM, python, perl, etc.

Half of the userland will go belly up, because AVX is used in string operations and hashing if it is available.

UML is just another userland application from this perspective, so there is no reason for it to behave any different from the rest of the userland.
-- 
Anton R. Ivanov
Cambridgegreys Limited. Registered in England. Company Number 10273661




From david.laight.linux at gmail.com  Fri Jun 26 03:49:57 2026
From: david.laight.linux at gmail.com (David Laight)
Date: Fri, 26 Jun 2026 11:49:57 +0100
Subject: [PATCH 2/8] um: Check for missing AVX and AVX-512 xstate bits
In-Reply-To: <6a20b442-b97f-4cae-9168-30201d5ef82c@cambridgegreys.com>
References: <20260626043731.319287-1-ebiggers@kernel.org>
	<20260626043731.319287-3-ebiggers@kernel.org>
	<20260626084113.42eae31c@pumpkin>
	<6a20b442-b97f-4cae-9168-30201d5ef82c@cambridgegreys.com>
Message-ID: <20260626114957.1a2b7e5b@pumpkin>

On Fri, 26 Jun 2026 09:21:49 +0100
Anton Ivanov <anton.ivanov at cambridgegreys.com> wrote:

> On 26/06/2026 08:41, David Laight wrote:
> > On Thu, 25 Jun 2026 21:37:25 -0700
> > Eric Biggers <ebiggers at kernel.org> wrote:
> >   
> >> If the CPU declares AVX or AVX-512 support, verify that all the
> >> corresponding xstate bits are also set.  If any are missing, warn and
> >> don't set the corresponding X86_FEATURE_* flags.
> >>
> >> This eliminates the perceived need for UML-supporting AVX and AVX-512
> >> optimized code in the kernel (that is, lib/raid/ currently) to start
> >> checking the xstate bits in addition to X86_FEATURE_AVX*.
> >>  
> > ...  
> >>   static void __init parse_host_cpu_flags(char *line)
> >>   {
> >> +	u64 xcr0 = read_xcr0();
> >>   	int i;
> >> +
> >>   	for (i = 0; i < 32*NCAPINTS; i++) {
> >>   		if ((x86_cap_flags[i] != NULL) && strstr(line, x86_cap_flags[i]))  
> > 
> > 'line' comes from /proc/cpuinfo
> > Surely something would be terribly wrong if that included something the kernel
> > had disabled (or didn't support).
> > 
> > 	David
> > 
> >   
> >> -			set_cpu_cap(&boot_cpu_data, i);
> >> +			validate_and_set_cpu_cap(i, xcr0);
> >>   	}
> >>   }
> >>   
> >>   static void __init parse_cache_line(char *line)
> >>   {  
> > 
> > 
> > 
>  >  
> Lots of other stuff will go wrong before that. Glibc, things compiled with LLVM, python, perl, etc.
> 
> Half of the userland will go belly up, because AVX is used in string operations and hashing if it is available.

And glibc will check xcr0.

> 
> UML is just another userland application from this perspective, so there is no reason for it to behave any different from the rest of the userland.



From ebiggers at kernel.org  Fri Jun 26 13:55:44 2026
From: ebiggers at kernel.org (Eric Biggers)
Date: Fri, 26 Jun 2026 20:55:44 +0000
Subject: [PATCH 2/8] um: Check for missing AVX and AVX-512 xstate bits
In-Reply-To: <20260626114957.1a2b7e5b@pumpkin>
References: <20260626043731.319287-1-ebiggers@kernel.org>
 <20260626043731.319287-3-ebiggers@kernel.org>
 <20260626084113.42eae31c@pumpkin>
 <6a20b442-b97f-4cae-9168-30201d5ef82c@cambridgegreys.com>
 <20260626114957.1a2b7e5b@pumpkin>
Message-ID: <20260626205544.GB2368695@google.com>

On Fri, Jun 26, 2026 at 11:49:57AM +0100, David Laight wrote:
> > UML is just another userland application from this perspective, so
> > there is no reason for it to behave any different from the rest of
> > the userland.

Which is why it should do the XCR0 check that the vast majority of
userspace programs do, right?  It has always been part of the documented
way to detect AVX and AVX-512 support.

(I think this helps explain why LLMs notice this too.  They've been
trained on lots of code that does it correctly.)

That being said, it does seem likely that it's basically obsolete now.
So maybe we could take a shortcut and omit it.

The important thing is really that we make a definitive decision *once*
for each of UML and native x86.  The status quo is that the decision is
instead punted to every individual AVX optimized function in the kernel,
which isn't working well.

- Eric


From david.laight.linux at gmail.com  Fri Jun 26 14:33:31 2026
From: david.laight.linux at gmail.com (David Laight)
Date: Fri, 26 Jun 2026 22:33:31 +0100
Subject: [PATCH 2/8] um: Check for missing AVX and AVX-512 xstate bits
In-Reply-To: <20260626205544.GB2368695@google.com>
References: <20260626043731.319287-1-ebiggers@kernel.org>
	<20260626043731.319287-3-ebiggers@kernel.org>
	<20260626084113.42eae31c@pumpkin>
	<6a20b442-b97f-4cae-9168-30201d5ef82c@cambridgegreys.com>
	<20260626114957.1a2b7e5b@pumpkin>
	<20260626205544.GB2368695@google.com>
Message-ID: <20260626223331.3451c11b@pumpkin>

On Fri, 26 Jun 2026 20:55:44 +0000
Eric Biggers <ebiggers at kernel.org> wrote:

> On Fri, Jun 26, 2026 at 11:49:57AM +0100, David Laight wrote:
> > > UML is just another userland application from this perspective, so
> > > there is no reason for it to behave any different from the rest of
> > > the userland.  
> 
> Which is why it should do the XCR0 check that the vast majority of
> userspace programs do, right?  It has always been part of the documented
> way to detect AVX and AVX-512 support.

That and looking at the other 'cpuid' registers rather than reading
/proc/cpuinfo.
If you want to run 'um' on (say) NetBSD you'd need to do it differently.

> (I think this helps explain why LLMs notice this too.  They've been
> trained on lots of code that does it correctly.)
> 
> That being said, it does seem likely that it's basically obsolete now.
> So maybe we could take a shortcut and omit it.

It would need the linux kernel to report a cpu feature that needed
kernel support, but that the kernel didn't support.

But for things like popcnt um should probably check the relevant cpuid bit
rather than scanning /proc/cpuinfo.

> 
> The important thing is really that we make a definitive decision *once*
> for each of UML and native x86.  The status quo is that the decision is
> instead punted to every individual AVX optimized function in the kernel,
> which isn't working well.

Indeed.

	David

> 
> - Eric



From benjamin at sipsolutions.net  Sat Jun 27 09:21:41 2026
From: benjamin at sipsolutions.net (Benjamin Berg)
Date: Sat, 27 Jun 2026 18:21:41 +0200
Subject: [PATCH 0/7] um: skas: harden the seccomp userspace stub
In-Reply-To: <CAM_iQpUc4dQvYXbGpUgBXke+a8hVXnBCtjziXd1QX=QJXWiwjA@mail.gmail.com>
References: <cover.1781925126.git.xiyou.wangcong@gmail.com>
	 <1e1681eecfa5dd30181534674b1a17756df802f8.camel@sipsolutions.net>
	 <CAM_iQpWj+daY6tsPXKq=B4ekjr-8-tpfX+2twLhW_oGP_YqOHw@mail.gmail.com>
	 <03b2134eea62d664781069c8bd072705e0fc4aee.camel@sipsolutions.net>
	 <CAM_iQpUc4dQvYXbGpUgBXke+a8hVXnBCtjziXd1QX=QJXWiwjA@mail.gmail.com>
Message-ID: <c4bc735d4413410c6ff5c5011ebdf0fffe05866f.camel@sipsolutions.net>

Hi,

On Wed, 2026-06-24 at 16:12 -0700, Cong Wang wrote:
> On Wed, Jun 24, 2026 at 12:54?AM Benjamin Berg
> <benjamin at sipsolutions.net> wrote:
> > 
> > On Tue, 2026-06-23 at 15:08 -0700, Cong Wang wrote:
> > > On Mon, Jun 22, 2026 at 5:08?AM Benjamin Berg <benjamin at sipsolutions.net> wrote:
> > > > 
> > > > On Fri, 2026-06-19 at 20:22 -0700, Cong Wang wrote:
> > > > > From: Cong Wang <cwang at multikernel.io>
> > > > > 
> > > > > In the seccomp ("SECCOMP") userspace mode, each guest userspace process
> > > > > runs in a stub under a seccomp filter and traps to the monitor (the UML
> > > > > kernel) on every syscall. Two items on the stub.c "Known security issues"
> > > > > list could not be addressed by the filter alone:
> > > > > 
> > > > > ? - a hijacked stub could mmap() arbitrary physmem offsets, which is an
> > > > > ??? intra-guest disclosure and, on this base (single physmem fd, no
> > > > > ??? kernel/user split), a host escape; and
> > > > > 
> > > > > ? - a hijacked stub could block SIGALRM via a crafted rt_sigreturn to
> > > > > ??? evade preemption and wedge the monitor indefinitely.
> > > > > 
> > > > > This series closes both:
> > > > > 
> > > > > ? 1-2: route the stub's mmap() through a SECCOMP_RET_USER_NOTIF listener
> > > > > ?????? owned by the monitor (no behavioural change yet).
> > > > > ? 3-4: validate each mmap() against the mm's page table -- allowed iff the
> > > > > ?????? PTE already maps the requested frame with no more access than it
> > > > > ?????? grants -- including out-of-batch mmaps a hijacked stub issues on
> > > > > ?????? its own.
> > > > > ? 5:?? route and validate munmap() the same way (range-confined below
> > > > > ?????? STUB_START).
> > > > 
> > > > 
> > > > That approach seems odd to me. Adding an explicit out-of-band check
> > > > means you require two extra context switches per mmap syscall. I would
> > > > expect that this makes the SECCOMP approach a lot slower than ptrace().
> > > > My take is still that it is possible to carefully craft a SECCOMP
> > > > filter as well as stub/kernel code that makes exploitation impossible
> > > > for non-SMP.
> > > > 
> > > > The true SMP case is more complicated, but we do not have that anyway,
> > > > so I would not worry about it for now.
> > > > 
> > > > Did you run any performance tests?
> > > 
> > > Sorry, I thought I included them in patch 4/7, but I missed them.
> > > 
> > > Here they are:
> > > 
> > > mmap() -- ns/call
> > > -----------------
> > > size?? WITH med?? WITHOUT med?? delta??????????? WITH p99?? WITHOUT p99
> > > 4K???? 13056????? 10368???????? +2688? (+26%)???? 17408????? 15104
> > > 16K??? 13824????? 11008???????? +2816? (+26%)???? 15616????? 12544
> > > 64K??? 19840????? 15616???????? +4224? (+27%)???? 21760????? 17408
> > > 256K?? 33152????? 28928???????? +4224? (+15%)???? 36352????? 31744
> > > 1M???? 95616????? 84608???????? +11008 (+13%)???? 117504???? 108032
> > > 
> > > munmap() -- ns/call
> > > -------------------
> > > size?? WITH med?? WITHOUT med?? delta??????????? WITH p99?? WITHOUT p99
> > > 4K???? 11008????? 8448????????? +2560? (+30%)???? 13568????? 11264
> > > 16K??? 10752????? 8448????????? +2304? (+27%)???? 12288????? 9472
> > > 64K??? 12032????? 9728????????? +2304? (+24%)???? 13824????? 10752
> > > 256K?? 15360????? 12800???????? +2560? (+20%)???? 17152????? 14592
> > > 1M???? 27648????? 24832???????? +2816? (+11%)???? 30976????? 26880
> > > 
> > > Since this is a clear security-vs-performance balance, I picked security
> > > over performance. Please let me know if you prefer otherwise.
> > 
> > So, I still believe that (almost) zero-overhead is possible in the
> > current framework. Maybe one could add a non-zero overhead option, but
> > then I would like to be able to disable it as there are plenty of
> > scenarios where security is of no concern.
> 
> This is fair, I can add an option to keep it disabled by default.

Sure. But I still think that a discussion is needed on whether another
option is viable. I would really prefer not adding your proposed
solution if another low/zero overhead option is viable.

And I still absolutely believe that it is viable to modify the current
code and make it secure. I just never finished that work because it
requires a lot of care and it is not that important to us.

> > Also, I am unsure what you measured there. When you write "mmap" here,
> > is that an "mmap" call made by the userspace process (which might not
> > actually change the page mappings), or is that an "mmap" call that was
> > queued for the stub to process?
> > 
> > A large amount of the overhead that UML tends to have happens when
> > handling both minor and major page faults. Simple tests for this is for
> > example a relatively tight fork/exec loop or just applications starting
> > up. In our test runs, we saw major speed improvements (>10%) just by
> > fixing bugs in the TLB handling code that resulted in fewer page faults
> > happening at application runtime.
> 
> It is just a simple user-space mmap() loop just for benchmarking this
> patchset.

If you just say "mmap() loop" I think that you are purely doing an
mmap/unmap syscall of anonymous memory in a loop. However, the kernel
may not map any of those pages until they are actually accessed. I
would not have been surprised if you did not see any performance
difference at all in such a test.

Are you actually touching (writing) each of the mapped pages?

> > > Meanwhile, after a second thought, there is a zero-overhead solution.
> > > pidfd_mmap(), which can be built on top of another patch of mine:
> > > https://lore.kernel.org/all/20260613001533.314739-2-xiyou.wangcong at gmail.com/
> > 
> > Sure, should some sort of method land that allows directly modifying
> > the mappings of the remote process, that would be rather convenient.
> > 
> 
> Great. I will work on it. One question: should I drop this one or we can use
> this one as the short term solution before the pidfd_mmap() solution gets in?
> Even if we had pidfd_mmap(), UML might still want to keep compatibility with
> older host kernels?

Of course, UML should not regress on older kernels. Also, any new API
like that will probably still take a while. That said, I am not sure
there is a pressing need to fix the security problem as long as we have
ptrace mode available.

> 
> > > Something like:
> > > 
> > > int map(struct mm_id *mm_idp, unsigned long virt, unsigned long len, int prot,
> > > ??????? int phys_fd, unsigned long long offset)
> > > {
> > > ??????? return pidfd_mmap(mm_idp->stub_pidfd, (void *)virt, len,
> > > ????????????????????????? prot_to_mmap(prot), MAP_FIXED | MAP_SHARED,
> > > ????????????????????????? phys_fd, offset);
> > > }
> > > int unmap(struct mm_id *mm_idp, unsigned long addr, unsigned long len)
> > > {
> > > ??????? return pidfd_munmap(mm_idp->stub_pidfd, (void *)addr, len);
> > > }
> > > 
> > > Obviously, this one requires a new syscall for the host kernel first.
> > > 
> > > > 
> > > > > ? 6:?? add a watchdog thread that detects a stub which stops reporting
> > > > > ?????? back (e.g. blocked SIGALRM) and SIGKILLs it, letting the monitor
> > > > > ?????? recover via the existing teardown.
> > > > 
> > > > That also seems like an odd solution to me. Architecturally, UML first
> > > > receives the SIGALRM and forwards it to the child. It would seem much
> > > > easier to set a flag and clear it again when the process reports back
> > > > that it received the SIGALRM. Then, when the kernel receives the next
> > > > SIGALRM, just kill the child immediately if the flag is still set.
> > > 
> > > The flag-and-recheck scheme matches the ptrace path (wait_stub_done), where
> > > the monitor is the tracer: it sees the stub's SIGALRM as a waitpid()
> > > signal-stop and PTRACE_CONTs it, so "monitor receives, then deals with the
> > > child" is accurate there. This patch is on the seccomp path
> > > (wait_stub_done_seccomp), which is architecturally different.
> > > 
> > > Or am I missing anything?
> > 
> > The fact that it is not actually different from an architectural
> > standpoint. In both cases SIGALRM is reported back to the kernel. It
> > obviously has to be reported back, otherwise scheduling would not work.
> 
> You're right.
> 
> In the seccomp path the monitor also receives the SIGALRM and
> forwards it to the stub: the per-vCPU POSIX timer targets the monitor thread
> and um_timer() -> os_alarm_process() -> kill(stub, SIGALRM) forwards
> the tick. ptrace vs. seccomp differ only in how the monitor
> learns the stub stopped (waitpid vs. futex), not in the point you raised.
> 
> So the watchdog thread is redundant. The inner FUTEX_WAIT loop in
> wait_stub_done_seccomp already wakes on every forwarded tick via EINTR;
> responsive stub acks by flipping data->futex out of FUTEX_IN_CHILD, a
> SIGALRM-blocking one doesn't, exactly your flag scheme. I'll drop the helper
> thread and timerfd and detect the stall inline: count consecutive ticks where
> the stub didn't report and goto out_kill past a small threshold.

Not sure I follow and the description makes me wonder if it was written
by an LLM. To me it seems overly complicated and incorrect in subtle
ways.

Benjamin


From ruanjinjie at huawei.com  Mon Jun 29 06:05:58 2026
From: ruanjinjie at huawei.com (Jinjie Ruan)
Date: Mon, 29 Jun 2026 21:05:58 +0800
Subject: [PATCH v16 00/18] arm64: entry: Convert to Generic Entry
Message-ID: <20260629130616.642022-1-ruanjinjie@huawei.com>

This is the 16th iteration of the patch series. I would like to express
my sincere gratitude to everyone who has contributed to the reviews
and testing across the previous versions, which helped significantly
refine this work.

Currently, architectures such as x86, Riscv, Loongarch use the Generic
Entry, which significantly eases maintenance burden and improves
code elegance. Given that arm64 has already successfully adopted
the Generic IRQ Entry in commit b3cf07851b6c ("arm64: entry: Switch to
generic IRQ entry"), it is now the right time to completely convert arm64
to the Generic Entry infrastructure.

The objective of this series is to bring arm64 in line with other major
architectures, reducing duplicated boilerplate code and simplifying the
sharing of future improvements in entry/exit paths, such as "Syscall User
Dispatch" and "rseq time slice extension" optimizations.

This patch set is rebased on v7.2-rc1. It contains the foundational
updates required for arm64. Following the suggestion from Linus Walleij,
these patches are being submitted separately for inclusion via
the arm64 tree.

Performance benchmarks were conducted on a "Kunpeng HIP09" platform with
kernel auditing enabled. The results are detailed below:

1. perf bench syscall usec/op (-ve is improvement)

        | Syscall | Base        | Generic Entry | change % |
        | ------- | ----------- | ------------- | -------- |
        | basic   | 0.223       | 0.218         | -2.25    |
        | execve  | 799.564     | 787.849       | -1.47    |
        | fork    | 1088.503    | 1096.920      | +0.77    |
        | getpgid | 0.218       |  0.122        | -0.47    |

2. perf bench syscall ops/sec (+ve is improvement)

        | Syscall | Base     | Generic Entry| change % |
        | ------- | -------- | ------------ | -------- |
        | basic   | 4475386  | 4578820      | +2.31    |
        | execve  | 1250     | 1269         | +1.54    |
        | fork    | 918      | 911          | -0.74    |
        | getpgid | 4587781  | 4607488      | +0.43    |

Overall, the syscall performance remains largely neutral, with minor
variations ranging from a 0.8% regression to a 2.3% improvement, which
is within normal measurement tolerance.

Tested successfully on "Kunpeng HIP09" using the following test cases:
 - stress-ng: CPU, Virtual Memory, Syscall, and Interrupt stress tests.
 - hackbench: Evaluated in both thread and pipe modes.
 - kselftests (ptrace): get_syscall_info, set_syscall_info, peeksiginfo.
 - kselftests (breakpoints): breakpoint_test_arm64.
 - kselftests (arm64/abi): syscall-abi and ptrace.
 - kselftests (arm64/fp): fp-ptrace, sve-ptrace, and za-ptrace.
 - kselftests (vDSO): vdso_test_getrandom.
 - Ptrace stress test: tracing stress-ng system calls under load.
 - Pseudo-NMI load test using perf.

Changes in v16:
- Rebased on v7.2-rc1.
- Convert __secure_computing() to return boolean as Thomas suggested.
- Introduce a weak vesion for arch_syscall_is_vdso_sigreturn()
  to simplify the code.
- Split out the implementation of arch_syscall_is_vdso_sigreturn()
  into a separate patch to facilitate reviewing.
- Break down the syscall_exit_to_user_mode_work() refactoring into
  5 smaller, bite-sized patches for easier review and justification.
- Update the comment and commit message as Ada suggeted.
- Clarify patch titles and align title prefixes for consistency.
- Collect Reviewed-by and Tesed-by.
- Link to v15: https://lore.kernel.org/all/20260511092103.1974980-1-ruanjinjie at huawei.com/

Changes in v15:
- Rebased on v7.1-rc1 and Mark's fix patch in [1].
- Solve issues Sashiko AI pointed out, "Fix potential syscall truncation
  in syscall_trace_enter()".
- Make syscall_exit_to_user_mode_work() __always_inline to keep
  the fast-path performance as Sashiko pointed out.

Changes in v14:
- Initialize ret = 0 in syscall_trace_enter().
- Split into two patch sets as Linus Walleij suggested, so this patch set
  can be applied separately to the arm64 tree.
- Rebased on arm64 for-next/core branch.
- Collect Reviewed-by and Acked-by.
- Link to v13 resend: https://lore.kernel.org/all/20260317082020.737779-15-ruanjinjie at huawei.com/

Changes in v13 resend:
- Fix exit_to_user_mode_prepare_legacy() issues.
- Also move TIF_SINGLESTEP to generic TIF infrastructure for loongarch.
- Use generic TIF bits for arm64 and moving TIF_SINGLESTEP to
  generic TIF for related architectures separately.
- Refactor syscall_trace_enter/exit() to accept flags and Use
  syscall_get_nr() helper separately.
- Tested with slice_test for rseq optimizations.
- Add acked-by.
- Link to v13: https://lore.kernel.org/all/20260313094738.3985794-1-ruanjinjie at huawei.com/

Changes in v13:
- Rebased on v7.0-rc3, so drop the firt applied arm64 patch.
- Use generic TIF bits to enables RSEQ optimization.
- Update most of the commit message to make it more clear.
- Link to v12: https://lore.kernel.org/all/20260203133728.848283-1-ruanjinjie at huawei.com/

Changes in v12:
- Rebased on "sched/core", so remove the four generic entry patches.
- Move "Expand secure_computing() in place" and
  "Use syscall_get_arguments() helper" patch forward, which will group all
  non-functional cleanups at the front.
- Adjust the explanation for moving rseq_syscall() before
  audit_syscall_exit().
- Link to v11: https://lore.kernel.org/all/20260128031934.3906955-1-ruanjinjie at huawei.com/

Changes in v11:
- Remove unused syscall in syscall_trace_enter().
- Update and provide a detailed explanation of the differences after
  moving rseq_syscall() before audit_syscall_exit().
- Rebased on arm64 (for-next/entry), and remove the first applied 3 patchs.
- syscall_exit_to_user_mode_work() for arch reuse instead of adding
  new syscall_exit_to_user_mode_work_prepare() helper.
- Link to v10: https://lore.kernel.org/all/20251222114737.1334364-1-ruanjinjie at huawei.com/

Changes in v10:
- Rebased on v6.19-rc1, rename syscall_exit_to_user_mode_prepare() to
  syscall_exit_to_user_mode_work_prepare() to avoid conflict.
- Also inline syscall_trace_enter().
- Support aarch64 for sud_benchmark.
- Update and correct the commit message.
- Add Reviewed-by.
- Link to v9: https://lore.kernel.org/all/20251204082123.2792067-1-ruanjinjie at huawei.com/

Changes in v9:
- Move "Return early for ptrace_report_syscall_entry() error" patch ahead
  to make it not introduce a regression.
- Not check _TIF_SECCOMP/SYSCALL_EMU for syscall_exit_work() in
  a separate patch.
- Do not report_syscall_exit() for PTRACE_SYSEMU_SINGLESTEP in a separate
  patch.
- Add two performance patch to improve the arm64 performance.
- Add Reviewed-by.
- Link to v8: https://lore.kernel.org/all/20251126071446.3234218-1-ruanjinjie at huawei.com/

Changes in v8:
- Rename "report_syscall_enter()" to "report_syscall_entry()".
- Add ptrace_save_reg() to avoid duplication.
- Remove unused _TIF_WORK_MASK in a standalone patch.
- Align syscall_trace_enter() return value with the generic version.
- Use "scno" instead of regs->syscallno in el0_svc_common().
- Move rseq_syscall() ahead in a standalone patch to clarify it clearly.
- Rename "syscall_trace_exit()" to "syscall_exit_work()".
- Keep the goto in el0_svc_common().
- No argument was passed to __secure_computing() and check -1 not -1L.
- Remove "Add has_syscall_work() helper" patch.
- Move "Add syscall_exit_to_user_mode_prepare() helper" patch later.
- Add miss header for asm/entry-common.h.
- Update the implementation of arch_syscall_is_vdso_sigreturn().
- Add "ARCH_SYSCALL_WORK_EXIT" to be defined as "SECCOMP | SYSCALL_EMU"
  to keep the behaviour unchanged.
- Add more testcases test.
- Add Reviewed-by.
- Update the commit message.
- Link to v7: https://lore.kernel.org/all/20251117133048.53182-1-ruanjinjie at huawei.com/

Jinjie Ruan (18):
  seccomp: Convert __secure_computing() to return boolean
  syscall_user_dispatch: Introduce a weak fallback for
    arch_syscall_is_vdso_sigreturn()
  arm64: ptrace: Pass thread flags to syscall_trace_enter/exit()
  arm64: ptrace: Use syscall_get_nr() helper for syscall_trace_enter()
  arm64: ptrace: Expand secure_computing() in place
  arm64: ptrace: Use syscall_get_arguments() helper for audit
  arm64: ptrace: Protect rseq_syscall() from tracer PC modifications
  arm64: ptrace: Rename syscall_trace_exit() to syscall_exit_work()
  arm64: syscall: Rework the syscall exit path in el0_svc_common()
  arm64: ptrace: Extract syscall_exit_to_user_mode_work() helper
  arm64: ptrace: Align syscall exit work semantics with generic entry
  arm64: syscall: Use exit-specific flags check in el0_svc_common()
  arm64: syscall: Simplify el0_svc_common() syscall exit path
  arm64: syscall: Simplify syscall exit path in el0_svc_common()
  arm64: ptrace: Skip syscall exit reporting for
    PTRACE_SYSEMU_SINGLESTEP
  arm64: entry: Convert to generic entry
  arm64: Inline el0_svc_common()
  arm64: vdso: Expose sigreturn address on vdso to the kernel

 arch/alpha/kernel/ptrace.c            |   2 +-
 arch/arm/kernel/ptrace.c              |   2 +-
 arch/arm64/Kconfig                    |   2 +-
 arch/arm64/include/asm/elf.h          |   1 +
 arch/arm64/include/asm/entry-common.h |  76 +++++++++++++++++
 arch/arm64/include/asm/syscall.h      |   4 +-
 arch/arm64/include/asm/thread_info.h  |  16 +---
 arch/arm64/kernel/debug-monitors.c    |   8 ++
 arch/arm64/kernel/ptrace.c            | 115 --------------------------
 arch/arm64/kernel/signal.c            |   2 +-
 arch/arm64/kernel/syscall.c           |  30 ++-----
 arch/arm64/kernel/vdso.c              |  16 ++++
 arch/csky/kernel/ptrace.c             |   2 +-
 arch/loongarch/include/asm/syscall.h  |   5 --
 arch/m68k/kernel/ptrace.c             |   2 +-
 arch/mips/kernel/ptrace.c             |   2 +-
 arch/parisc/kernel/ptrace.c           |   2 +-
 arch/powerpc/include/asm/syscall.h    |   5 --
 arch/riscv/include/asm/syscall.h      |   5 --
 arch/s390/include/asm/syscall.h       |   5 --
 arch/sh/kernel/ptrace_32.c            |   2 +-
 arch/um/kernel/skas/syscall.c         |   2 +-
 arch/x86/entry/vsyscall/vsyscall_64.c |   2 +-
 arch/xtensa/kernel/ptrace.c           |   3 +-
 include/linux/entry-common.h          |   7 +-
 include/linux/seccomp.h               |  10 +--
 include/linux/syscall_user_dispatch.h |   1 +
 kernel/entry/syscall_user_dispatch.c  |   5 ++
 kernel/seccomp.c                      |  34 ++++----
 29 files changed, 154 insertions(+), 214 deletions(-)

-- 
2.34.1



From ruanjinjie at huawei.com  Mon Jun 29 06:05:59 2026
From: ruanjinjie at huawei.com (Jinjie Ruan)
Date: Mon, 29 Jun 2026 21:05:59 +0800
Subject: [PATCH v16 01/18] seccomp: Convert __secure_computing() to return boolean
In-Reply-To: <20260629130616.642022-1-ruanjinjie@huawei.com>
References: <20260629130616.642022-1-ruanjinjie@huawei.com>
Message-ID: <20260629130616.642022-2-ruanjinjie@huawei.com>

The return value of __secure_computing() currently uses 0 to indicate
that a system call should be allowed, and -1 to indicate that it should
be blocked/killed. This 0/-1 pattern is non-intuitive for a security
check function and makes the control flow at the call sites less readable.

Furthermore, any potential future changes to these return values would
require a high-risk, error-prone audit of all its users across different
architectures.

Sanitize this logic by converting the return type of __secure_computing()
to a proper boolean, where 'true' explicitly means 'allow' and 'false'
means 'fail/deny'.

Update all the two dozen or so call sites across the tree to align with
this new boolean semantic. No functional changes are intended, as the
callers still return -1 to the lower-level assembly entry code upon
seccomp denial.

Suggested-by: Thomas Gleixner <tglx at kernel.org>
Signed-off-by: Jinjie Ruan <ruanjinjie at huawei.com>
---
 arch/alpha/kernel/ptrace.c            |  2 +-
 arch/arm/kernel/ptrace.c              |  2 +-
 arch/arm64/kernel/ptrace.c            |  2 +-
 arch/csky/kernel/ptrace.c             |  2 +-
 arch/m68k/kernel/ptrace.c             |  2 +-
 arch/mips/kernel/ptrace.c             |  2 +-
 arch/parisc/kernel/ptrace.c           |  2 +-
 arch/sh/kernel/ptrace_32.c            |  2 +-
 arch/um/kernel/skas/syscall.c         |  2 +-
 arch/x86/entry/vsyscall/vsyscall_64.c |  2 +-
 arch/xtensa/kernel/ptrace.c           |  3 +--
 include/linux/entry-common.h          |  7 +++---
 include/linux/seccomp.h               | 10 ++++----
 kernel/seccomp.c                      | 34 +++++++++++++--------------
 14 files changed, 36 insertions(+), 38 deletions(-)

diff --git a/arch/alpha/kernel/ptrace.c b/arch/alpha/kernel/ptrace.c
index 0687760ea466..27d9847b1082 100644
--- a/arch/alpha/kernel/ptrace.c
+++ b/arch/alpha/kernel/ptrace.c
@@ -387,7 +387,7 @@ asmlinkage unsigned long syscall_trace_enter(void)
 	 * If this fails, seccomp may already have set up the return value
 	 * (e.g. SECCOMP_RET_ERRNO / TRACE).
 	 */
-	if (secure_computing() == -1) {
+	if (!secure_computing()) {
 		if (regs->r19 == 0 && regs->r0 == (unsigned long)-1)
 			syscall_set_return_value(current, regs, -ENOSYS, 0);
 		syscall_set_nr(current, regs, -1);
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
index 7951b2c06fec..5210745725ca 100644
--- a/arch/arm/kernel/ptrace.c
+++ b/arch/arm/kernel/ptrace.c
@@ -855,7 +855,7 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs)
 
 	/* Do seccomp after ptrace; syscall may have changed. */
 #ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER
-	if (secure_computing() == -1)
+	if (!secure_computing())
 		return -1;
 #else
 	/* XXX: remove this once OABI gets fixed */
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index 4d08598e2891..2ca6fab39a37 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -2420,7 +2420,7 @@ int syscall_trace_enter(struct pt_regs *regs)
 	}
 
 	/* Do the secure computing after ptrace; failures should be fast. */
-	if (secure_computing() == -1)
+	if (!secure_computing())
 		return NO_SYSCALL;
 
 	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
diff --git a/arch/csky/kernel/ptrace.c b/arch/csky/kernel/ptrace.c
index 6bb685a2646b..11c5eff41e9d 100644
--- a/arch/csky/kernel/ptrace.c
+++ b/arch/csky/kernel/ptrace.c
@@ -323,7 +323,7 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs)
 		if (ptrace_report_syscall_entry(regs))
 			return -1;
 
-	if (secure_computing() == -1)
+	if (!secure_computing())
 		return -1;
 
 	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
diff --git a/arch/m68k/kernel/ptrace.c b/arch/m68k/kernel/ptrace.c
index cfa2df24eced..d2411404b9df 100644
--- a/arch/m68k/kernel/ptrace.c
+++ b/arch/m68k/kernel/ptrace.c
@@ -281,7 +281,7 @@ asmlinkage int syscall_trace_enter(void)
 	if (test_thread_flag(TIF_SYSCALL_TRACE))
 		ret = ptrace_report_syscall_entry(task_pt_regs(current));
 
-	if (secure_computing() == -1)
+	if (!secure_computing())
 		return -1;
 
 	return ret;
diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c
index 3f4c94c88124..0d809cda7542 100644
--- a/arch/mips/kernel/ptrace.c
+++ b/arch/mips/kernel/ptrace.c
@@ -1328,7 +1328,7 @@ asmlinkage long syscall_trace_enter(struct pt_regs *regs)
 			return -1;
 	}
 
-	if (secure_computing())
+	if (!secure_computing())
 		return -1;
 
 	if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
diff --git a/arch/parisc/kernel/ptrace.c b/arch/parisc/kernel/ptrace.c
index 8a17ab7e6e0b..565b51a48c8a 100644
--- a/arch/parisc/kernel/ptrace.c
+++ b/arch/parisc/kernel/ptrace.c
@@ -351,7 +351,7 @@ long do_syscall_trace_enter(struct pt_regs *regs)
 	}
 
 	/* Do the secure computing check after ptrace. */
-	if (secure_computing() == -1)
+	if (!secure_computing())
 		return -1;
 
 #ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
diff --git a/arch/sh/kernel/ptrace_32.c b/arch/sh/kernel/ptrace_32.c
index 06f765d71a29..8687f17cbe5a 100644
--- a/arch/sh/kernel/ptrace_32.c
+++ b/arch/sh/kernel/ptrace_32.c
@@ -460,7 +460,7 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
 		return -1;
 	}
 
-	if (secure_computing() == -1)
+	if (!secure_computing())
 		return -1;
 
 	if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
diff --git a/arch/um/kernel/skas/syscall.c b/arch/um/kernel/skas/syscall.c
index ba7494f9bfe4..916cd7acceaf 100644
--- a/arch/um/kernel/skas/syscall.c
+++ b/arch/um/kernel/skas/syscall.c
@@ -27,7 +27,7 @@ void handle_syscall(struct uml_pt_regs *r)
 		goto out;
 
 	/* Do the seccomp check after ptrace; failures should be fast. */
-	if (secure_computing() == -1)
+	if (!secure_computing())
 		goto out;
 
 	syscall = UPT_SYSCALL_NR(r);
diff --git a/arch/x86/entry/vsyscall/vsyscall_64.c b/arch/x86/entry/vsyscall/vsyscall_64.c
index ea36de9fa864..6aed3987b9f9 100644
--- a/arch/x86/entry/vsyscall/vsyscall_64.c
+++ b/arch/x86/entry/vsyscall/vsyscall_64.c
@@ -198,7 +198,7 @@ static bool __emulate_vsyscall(struct pt_regs *regs, unsigned long address)
 	regs->orig_ax = syscall_nr;
 	regs->ax = -ENOSYS;
 	tmp = secure_computing();
-	if ((!tmp && regs->orig_ax != syscall_nr) || regs->ip != address) {
+	if ((tmp && regs->orig_ax != syscall_nr) || regs->ip != address) {
 		warn_bad_vsyscall(KERN_DEBUG, regs,
 				  "seccomp tried to change syscall nr or ip");
 		force_exit_sig(SIGSYS);
diff --git a/arch/xtensa/kernel/ptrace.c b/arch/xtensa/kernel/ptrace.c
index b80d54b2ea34..ef78fcd318ff 100644
--- a/arch/xtensa/kernel/ptrace.c
+++ b/arch/xtensa/kernel/ptrace.c
@@ -553,8 +553,7 @@ int do_syscall_trace_enter(struct pt_regs *regs)
 		return 0;
 	}
 
-	if (regs->syscall == NO_SYSCALL ||
-	    secure_computing() == -1) {
+	if (regs->syscall == NO_SYSCALL || !secure_computing()) {
 		do_syscall_trace_leave(regs);
 		return 0;
 	}
diff --git a/include/linux/entry-common.h b/include/linux/entry-common.h
index 416a3352261f..3f66320e46d3 100644
--- a/include/linux/entry-common.h
+++ b/include/linux/entry-common.h
@@ -100,9 +100,8 @@ static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned l
 
 	/* Do seccomp after ptrace, to catch any tracer changes. */
 	if (work & SYSCALL_WORK_SECCOMP) {
-		ret = __secure_computing();
-		if (ret == -1L)
-			return ret;
+		if (!__secure_computing())
+			return -1L;
 	}
 
 	/* Either of the above might have changed the syscall number */
@@ -113,7 +112,7 @@ static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned l
 
 	syscall_enter_audit(regs, syscall);
 
-	return ret ? : syscall;
+	return syscall;
 }
 
 /**
diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h
index 9b959972bf4a..7af3173f40e9 100644
--- a/include/linux/seccomp.h
+++ b/include/linux/seccomp.h
@@ -22,14 +22,14 @@
 #include <linux/atomic.h>
 #include <asm/seccomp.h>
 
-extern int __secure_computing(void);
+extern bool __secure_computing(void);
 
 #ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER
-static inline int secure_computing(void)
+static inline bool secure_computing(void)
 {
 	if (unlikely(test_syscall_work(SECCOMP)))
 		return  __secure_computing();
-	return 0;
+	return true;
 }
 #else
 extern void secure_computing_strict(int this_syscall);
@@ -50,11 +50,11 @@ static inline int seccomp_mode(struct seccomp *s)
 struct seccomp_data;
 
 #ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER
-static inline int secure_computing(void) { return 0; }
+static inline bool secure_computing(void) { return true; }
 #else
 static inline void secure_computing_strict(int this_syscall) { return; }
 #endif
-static inline int __secure_computing(void) { return 0; }
+static inline bool __secure_computing(void) { return true; }
 
 static inline long prctl_get_seccomp(void)
 {
diff --git a/kernel/seccomp.c b/kernel/seccomp.c
index 066909393c38..1fec6efedab6 100644
--- a/kernel/seccomp.c
+++ b/kernel/seccomp.c
@@ -1100,12 +1100,12 @@ void secure_computing_strict(int this_syscall)
 	else
 		BUG();
 }
-int __secure_computing(void)
+bool __secure_computing(void)
 {
 	int this_syscall = syscall_get_nr(current, current_pt_regs());
 
 	secure_computing_strict(this_syscall);
-	return 0;
+	return true;
 }
 #else
 
@@ -1256,7 +1256,7 @@ static int seccomp_do_user_notification(int this_syscall,
 	return -1;
 }
 
-static int __seccomp_filter(int this_syscall, const bool recheck_after_trace)
+static bool __seccomp_filter(int this_syscall, const bool recheck_after_trace)
 {
 	u32 filter_ret, action;
 	struct seccomp_data sd;
@@ -1294,7 +1294,7 @@ static int __seccomp_filter(int this_syscall, const bool recheck_after_trace)
 	case SECCOMP_RET_TRACE:
 		/* We've been put in this state by the ptracer already. */
 		if (recheck_after_trace)
-			return 0;
+			return true;
 
 		/* ENOSYS these calls if there is no tracer attached. */
 		if (!ptrace_event_enabled(current, PTRACE_EVENT_SECCOMP)) {
@@ -1330,19 +1330,19 @@ static int __seccomp_filter(int this_syscall, const bool recheck_after_trace)
 		 * a skip would have already been reported.
 		 */
 		if (__seccomp_filter(this_syscall, true))
-			return -1;
+			return false;
 
-		return 0;
+		return true;
 
 	case SECCOMP_RET_USER_NOTIF:
 		if (seccomp_do_user_notification(this_syscall, match, &sd))
 			goto skip;
 
-		return 0;
+		return true;
 
 	case SECCOMP_RET_LOG:
 		seccomp_log(this_syscall, 0, action, true);
-		return 0;
+		return true;
 
 	case SECCOMP_RET_ALLOW:
 		/*
@@ -1350,7 +1350,7 @@ static int __seccomp_filter(int this_syscall, const bool recheck_after_trace)
 		 * this action since SECCOMP_RET_ALLOW is the starting
 		 * state in seccomp_run_filters().
 		 */
-		return 0;
+		return true;
 
 	case SECCOMP_RET_KILL_THREAD:
 	case SECCOMP_RET_KILL_PROCESS:
@@ -1367,46 +1367,46 @@ static int __seccomp_filter(int this_syscall, const bool recheck_after_trace)
 		} else {
 			do_exit(SIGSYS);
 		}
-		return -1; /* skip the syscall go directly to signal handling */
+		return false; /* skip the syscall go directly to signal handling */
 	}
 
 	unreachable();
 
 skip:
 	seccomp_log(this_syscall, 0, action, match ? match->log : false);
-	return -1;
+	return false;
 }
 #else
-static int __seccomp_filter(int this_syscall, const bool recheck_after_trace)
+static bool __seccomp_filter(int this_syscall, const bool recheck_after_trace)
 {
 	BUG();
 
-	return -1;
+	return false;
 }
 #endif
 
-int __secure_computing(void)
+bool __secure_computing(void)
 {
 	int mode = current->seccomp.mode;
 	int this_syscall;
 
 	if (IS_ENABLED(CONFIG_CHECKPOINT_RESTORE) &&
 	    unlikely(current->ptrace & PT_SUSPEND_SECCOMP))
-		return 0;
+		return true;
 
 	this_syscall = syscall_get_nr(current, current_pt_regs());
 
 	switch (mode) {
 	case SECCOMP_MODE_STRICT:
 		__secure_computing_strict(this_syscall);  /* may call do_exit */
-		return 0;
+		return true;
 	case SECCOMP_MODE_FILTER:
 		return __seccomp_filter(this_syscall, false);
 	/* Surviving SECCOMP_RET_KILL_* must be proactively impossible. */
 	case SECCOMP_MODE_DEAD:
 		WARN_ON_ONCE(1);
 		do_exit(SIGKILL);
-		return -1;
+		return false;
 	default:
 		BUG();
 	}
-- 
2.34.1



From ruanjinjie at huawei.com  Mon Jun 29 06:06:00 2026
From: ruanjinjie at huawei.com (Jinjie Ruan)
Date: Mon, 29 Jun 2026 21:06:00 +0800
Subject: [PATCH v16 02/18] syscall_user_dispatch: Introduce a weak fallback for arch_syscall_is_vdso_sigreturn()
In-Reply-To: <20260629130616.642022-1-ruanjinjie@huawei.com>
References: <20260629130616.642022-1-ruanjinjie@huawei.com>
Message-ID: <20260629130616.642022-3-ruanjinjie@huawei.com>

Currently, multiple architectures (LoongArch, RISC-V, S390, Powerpc)
provide identical stubs for arch_syscall_is_vdso_sigreturn() that simply
return false. This results in redundant boilerplate code across the tree.

Introduce a default __weak implementation of
arch_syscall_is_vdso_sigreturn() directly in syscall_user_dispatch.c that
returns false. This allows architectures that do not utilize a vDSO
sigreturn to entirely drop their redundant inline definitions.

Architectures requiring a specialized check (such as x86) will continue to
override this fallback with their strong symbol definitions.

Clean up the redundant implementations in loongarch, riscv, s390
and powerpc.

Cc: Thomas Gleixner <tglx at kernel.org>
Signed-off-by: Jinjie Ruan <ruanjinjie at huawei.com>
---
 arch/loongarch/include/asm/syscall.h  | 5 -----
 arch/powerpc/include/asm/syscall.h    | 5 -----
 arch/riscv/include/asm/syscall.h      | 5 -----
 arch/s390/include/asm/syscall.h       | 5 -----
 include/linux/syscall_user_dispatch.h | 1 +
 kernel/entry/syscall_user_dispatch.c  | 5 +++++
 6 files changed, 6 insertions(+), 20 deletions(-)

diff --git a/arch/loongarch/include/asm/syscall.h b/arch/loongarch/include/asm/syscall.h
index df8ea223c77b..946886794ced 100644
--- a/arch/loongarch/include/asm/syscall.h
+++ b/arch/loongarch/include/asm/syscall.h
@@ -85,9 +85,4 @@ static inline int syscall_get_arch(struct task_struct *task)
 #endif
 }
 
-static inline bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs)
-{
-	return false;
-}
-
 #endif	/* __ASM_LOONGARCH_SYSCALL_H */
diff --git a/arch/powerpc/include/asm/syscall.h b/arch/powerpc/include/asm/syscall.h
index 834fcc4f7b54..4b3c52ed6e9d 100644
--- a/arch/powerpc/include/asm/syscall.h
+++ b/arch/powerpc/include/asm/syscall.h
@@ -139,9 +139,4 @@ static inline int syscall_get_arch(struct task_struct *task)
 	else
 		return AUDIT_ARCH_PPC64;
 }
-
-static inline bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs)
-{
-	return false;
-}
 #endif	/* _ASM_SYSCALL_H */
diff --git a/arch/riscv/include/asm/syscall.h b/arch/riscv/include/asm/syscall.h
index 8067e666a4ca..987c9a78806f 100644
--- a/arch/riscv/include/asm/syscall.h
+++ b/arch/riscv/include/asm/syscall.h
@@ -112,11 +112,6 @@ static inline void syscall_handler(struct pt_regs *regs, ulong syscall)
 	regs->a0 = fn(regs);
 }
 
-static inline bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs)
-{
-	return false;
-}
-
 asmlinkage long sys_riscv_flush_icache(uintptr_t, uintptr_t, uintptr_t);
 
 asmlinkage long sys_riscv_hwprobe(struct riscv_hwprobe *, size_t, size_t,
diff --git a/arch/s390/include/asm/syscall.h b/arch/s390/include/asm/syscall.h
index 4271e4169f45..5f310caad1fc 100644
--- a/arch/s390/include/asm/syscall.h
+++ b/arch/s390/include/asm/syscall.h
@@ -89,11 +89,6 @@ static inline int syscall_get_arch(struct task_struct *task)
 	return AUDIT_ARCH_S390X;
 }
 
-static inline bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs)
-{
-	return false;
-}
-
 #define SYSCALL_FMT_0
 #define SYSCALL_FMT_1 , "0" (r2)
 #define SYSCALL_FMT_2 , "d" (r3) SYSCALL_FMT_1
diff --git a/include/linux/syscall_user_dispatch.h b/include/linux/syscall_user_dispatch.h
index 3858a6ffdd5c..73d69e02807d 100644
--- a/include/linux/syscall_user_dispatch.h
+++ b/include/linux/syscall_user_dispatch.h
@@ -10,6 +10,7 @@
 
 #ifdef CONFIG_GENERIC_ENTRY
 
+bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs);
 int set_syscall_user_dispatch(unsigned long mode, unsigned long offset,
 			      unsigned long len, char __user *selector);
 
diff --git a/kernel/entry/syscall_user_dispatch.c b/kernel/entry/syscall_user_dispatch.c
index d89dffcc2d64..acf545774d37 100644
--- a/kernel/entry/syscall_user_dispatch.c
+++ b/kernel/entry/syscall_user_dispatch.c
@@ -32,6 +32,11 @@ static void trigger_sigsys(struct pt_regs *regs)
 	force_sig_info(&info);
 }
 
+bool __weak arch_syscall_is_vdso_sigreturn(struct pt_regs *regs)
+{
+	return false;
+}
+
 bool syscall_user_dispatch(struct pt_regs *regs)
 {
 	struct syscall_user_dispatch *sd = &current->syscall_dispatch;
-- 
2.34.1



From ruanjinjie at huawei.com  Mon Jun 29 06:06:01 2026
From: ruanjinjie at huawei.com (Jinjie Ruan)
Date: Mon, 29 Jun 2026 21:06:01 +0800
Subject: [PATCH v16 03/18] arm64: ptrace: Pass thread flags to syscall_trace_enter/exit()
In-Reply-To: <20260629130616.642022-1-ruanjinjie@huawei.com>
References: <20260629130616.642022-1-ruanjinjie@huawei.com>
Message-ID: <20260629130616.642022-4-ruanjinjie@huawei.com>

Refactor syscall_trace_enter() and syscall_trace_exit() to move thread
flag reading to the caller. This aligns arm64's syscall trace enter/exit
function signature with generic entry framework.

[Changes]
1. Function signature changes:
   - syscall_trace_enter(regs) ? syscall_trace_enter(regs, flags)
   - syscall_trace_exit(regs) ? syscall_trace_exit(regs, flags)

2. Move flags reading to caller:
   - Previously: read_thread_flags() called inside each function.
   - Now: caller (like el0_svc_common) passes flags as parameter.

3. Update syscall.c:
   - el0_svc_common() now passes flags to tracing functions and
     re-fetches flags before entry/exit to handle potential TIF
     updates.

[Why this matters]
- Aligns arm64 with the generic entry interface.
- Makes future migration to generic entry framework smoother.

No functional changes intended.

Cc: Mark Rutland <mark.rutland at arm.com>
Cc: Will Deacon <will at kernel.org>
Cc: Catalin Marinas <catalin.marinas at arm.com>
Reviewed-by: Ada Couprie Diaz <ada.coupriediaz at arm.com>
Reviewed-by: Linus Walleij <linusw at kernel.org>
Reviewed-by: Yeoreum Yun <yeoreum.yun at arm.com>
Reviewed-by: Kevin Brodsky <kevin.brodsky at arm.com>
Signed-off-by: Jinjie Ruan <ruanjinjie at huawei.com>
---
 arch/arm64/include/asm/syscall.h | 4 ++--
 arch/arm64/kernel/ptrace.c       | 7 ++-----
 arch/arm64/kernel/syscall.c      | 5 +++--
 3 files changed, 7 insertions(+), 9 deletions(-)

diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h
index 5e4c7fc44f73..30b203ef156b 100644
--- a/arch/arm64/include/asm/syscall.h
+++ b/arch/arm64/include/asm/syscall.h
@@ -120,7 +120,7 @@ static inline int syscall_get_arch(struct task_struct *task)
 	return AUDIT_ARCH_AARCH64;
 }
 
-int syscall_trace_enter(struct pt_regs *regs);
-void syscall_trace_exit(struct pt_regs *regs);
+int syscall_trace_enter(struct pt_regs *regs, unsigned long flags);
+void syscall_trace_exit(struct pt_regs *regs, unsigned long flags);
 
 #endif	/* __ASM_SYSCALL_H */
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index 2ca6fab39a37..ac15b81a71b5 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -2408,9 +2408,8 @@ static void report_syscall_exit(struct pt_regs *regs)
 	}
 }
 
-int syscall_trace_enter(struct pt_regs *regs)
+int syscall_trace_enter(struct pt_regs *regs, unsigned long flags)
 {
-	unsigned long flags = read_thread_flags();
 	int ret;
 
 	if (flags & (_TIF_SYSCALL_EMU | _TIF_SYSCALL_TRACE)) {
@@ -2432,10 +2431,8 @@ int syscall_trace_enter(struct pt_regs *regs)
 	return regs->syscallno;
 }
 
-void syscall_trace_exit(struct pt_regs *regs)
+void syscall_trace_exit(struct pt_regs *regs, unsigned long flags)
 {
-	unsigned long flags = read_thread_flags();
-
 	audit_syscall_exit(regs);
 
 	if (flags & _TIF_SYSCALL_TRACEPOINT)
diff --git a/arch/arm64/kernel/syscall.c b/arch/arm64/kernel/syscall.c
index 358ddfbf1401..c8e116397cfd 100644
--- a/arch/arm64/kernel/syscall.c
+++ b/arch/arm64/kernel/syscall.c
@@ -113,7 +113,7 @@ static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
 		 */
 		if (scno == NO_SYSCALL)
 			syscall_set_return_value(current, regs, -ENOSYS, 0);
-		scno = syscall_trace_enter(regs);
+		scno = syscall_trace_enter(regs, read_thread_flags());
 		if (scno == NO_SYSCALL)
 			goto trace_exit;
 	}
@@ -132,7 +132,8 @@ static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
 	}
 
 trace_exit:
-	syscall_trace_exit(regs);
+	flags = read_thread_flags();
+	syscall_trace_exit(regs, flags);
 }
 
 void do_el0_svc(struct pt_regs *regs)
-- 
2.34.1



From ruanjinjie at huawei.com  Mon Jun 29 06:06:03 2026
From: ruanjinjie at huawei.com (Jinjie Ruan)
Date: Mon, 29 Jun 2026 21:06:03 +0800
Subject: [PATCH v16 05/18] arm64: ptrace: Expand secure_computing() in place
In-Reply-To: <20260629130616.642022-1-ruanjinjie@huawei.com>
References: <20260629130616.642022-1-ruanjinjie@huawei.com>
Message-ID: <20260629130616.642022-6-ruanjinjie@huawei.com>

Refactor syscall_trace_enter() by open-coding the seccomp check
to align with the generic entry framework.

[Background]
The generic entry implementation expands the seccomp check in-place
instead of using the secure_computing() wrapper. It directly tests
SYSCALL_WORK_SECCOMP and calls the underlying __secure_computing()
function to handle syscall filtering.

[Changes]
1. Open-code seccomp check:
   - Instead of calling the secure_computing() wrapper, explicitly check
     the 'flags' parameter for _TIF_SECCOMP.
   - Call __secure_computing() directly if the flag is set.

[Why this matters]
- Aligns the arm64 syscall path with the generic entry implementation,
  simplifying future migration to the generic entry framework.
- No functional changes are intended; seccomp behavior remains identical.

Cc: Mark Rutland <mark.rutland at arm.com>
Cc: Will Deacon <will at kernel.org>
Cc: Catalin Marinas <catalin.marinas at arm.com>
Reviewed-by: Ada Couprie Diaz <ada.coupriediaz at arm.com>
Reviewed-by: Linus Walleij <linusw at kernel.org>
Reviewed-by: Yeoreum Yun <yeoreum.yun at arm.com>
Reviewed-by: Kevin Brodsky <kevin.brodsky at arm.com>
Signed-off-by: Jinjie Ruan <ruanjinjie at huawei.com>
---
 arch/arm64/kernel/ptrace.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index 863083de37c3..30df515528e8 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -2420,8 +2420,10 @@ int syscall_trace_enter(struct pt_regs *regs, unsigned long flags)
 	}
 
 	/* Do the secure computing after ptrace; failures should be fast. */
-	if (!secure_computing())
-		return NO_SYSCALL;
+	if (flags & _TIF_SECCOMP) {
+		if (!__secure_computing())
+			return NO_SYSCALL;
+	}
 
 	/* Either of the above might have changed the syscall number */
 	syscall = syscall_get_nr(current, regs);
-- 
2.34.1



From ruanjinjie at huawei.com  Mon Jun 29 06:06:04 2026
From: ruanjinjie at huawei.com (Jinjie Ruan)
Date: Mon, 29 Jun 2026 21:06:04 +0800
Subject: [PATCH v16 06/18] arm64: ptrace: Use syscall_get_arguments() helper for audit
In-Reply-To: <20260629130616.642022-1-ruanjinjie@huawei.com>
References: <20260629130616.642022-1-ruanjinjie@huawei.com>
Message-ID: <20260629130616.642022-7-ruanjinjie@huawei.com>

Extract syscall_enter_audit() helper and use syscall_get_arguments()
to get syscall arguments, matching the generic entry implementation.

The new code:
- Checks audit_context() first to avoid unnecessary memcpy when audit
  is not active.
- Uses syscall_get_arguments() helper instead of directly accessing
  regs fields.
- Is now exactly equivalent to generic entry's syscall_enter_audit().

No functional changes.

Cc: Mark Rutland <mark.rutland at arm.com>
Cc: Will Deacon <will at kernel.org>
Cc: Catalin Marinas <catalin.marinas at arm.com>
Reviewed-by: Ada Couprie Diaz <ada.coupriediaz at arm.com>
Reviewed-by: Linus Walleij <linusw at kernel.org>
Reviewed-by: Yeoreum Yun <yeoreum.yun at arm.com>
Reviewed-by: Kevin Brodsky <kevin.brodsky at arm.com>
Signed-off-by: Jinjie Ruan <ruanjinjie at huawei.com>
---
 arch/arm64/kernel/ptrace.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index 30df515528e8..ae71cadbd4df 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -2408,6 +2408,16 @@ static void report_syscall_exit(struct pt_regs *regs)
 	}
 }
 
+static inline void syscall_enter_audit(struct pt_regs *regs, long syscall)
+{
+	if (unlikely(audit_context())) {
+		unsigned long args[6];
+
+		syscall_get_arguments(current, regs, args);
+		audit_syscall_entry(syscall, args[0], args[1], args[2], args[3]);
+	}
+}
+
 int syscall_trace_enter(struct pt_regs *regs, unsigned long flags)
 {
 	long syscall;
@@ -2438,8 +2448,7 @@ int syscall_trace_enter(struct pt_regs *regs, unsigned long flags)
 		 syscall = syscall_get_nr(current, regs);
 	}
 
-	audit_syscall_entry(syscall, regs->orig_x0, regs->regs[1],
-			    regs->regs[2], regs->regs[3]);
+	syscall_enter_audit(regs, syscall);
 
 	return syscall;
 }
-- 
2.34.1



From ruanjinjie at huawei.com  Mon Jun 29 06:06:05 2026
From: ruanjinjie at huawei.com (Jinjie Ruan)
Date: Mon, 29 Jun 2026 21:06:05 +0800
Subject: [PATCH v16 07/18] arm64: ptrace: Protect rseq_syscall() from tracer PC modifications
In-Reply-To: <20260629130616.642022-1-ruanjinjie@huawei.com>
References: <20260629130616.642022-1-ruanjinjie@huawei.com>
Message-ID: <20260629130616.642022-8-ruanjinjie@huawei.com>

Move the rseq_syscall() check earlier in the syscall exit path to ensure
it operates on the original instruction pointer (regs->pc) before any
potential modification by a tracer.

[Background]
When CONFIG_DEBUG_RSEQ is enabled, rseq_syscall() verifies that a system
call was not executed within an rseq critical section by examining
regs->pc. If a violation is detected, it triggers a SIGSEGV.

[Problem]
Currently, arm64 invokes rseq_syscall() after report_syscall_exit().
However, during report_syscall_exit(), a ptrace tracer can modify the
task's instruction pointer via PTRACE_SETREGSET (with NT_PRSTATUS). This
leads to an inconsistency where rseq may analyze a post-trace PC instead
of the actual PC at the time of syscall exit.

[Why this matters]
The rseq check is intended to validate the execution context of the
syscall itself. Analyzing a tracer-modified PC can lead to incorrect
detection or missed violations. Moving the check earlier ensures rseq
sees the authentic state of the task.

[Alignment]
This change aligns arm64 with:
- Generic entry, which calls rseq_syscall() first.
- arm32 implementation, which also performs the check before audit.

[Impact]
There is no functional change to signal delivery; SIGSEGV will still be
processed in arm64_exit_to_user_mode() at the end of the exit path.

Cc: Mark Rutland <mark.rutland at arm.com>
Cc: Thomas Gleixner <tglx at kernel.org>
Cc: Will Deacon <will at kernel.org>
Cc: Catalin Marinas <catalin.marinas at arm.com>
Reviewed-by: Ada Couprie Diaz <ada.coupriediaz at arm.com>
Reviewed-by: Linus Walleij <linusw at kernel.org>
Reviewed-by: Yeoreum Yun <yeoreum.yun at arm.com>
Reviewed-by: Kevin Brodsky <kevin.brodsky at arm.com>
Signed-off-by: Jinjie Ruan <ruanjinjie at huawei.com>
---
 arch/arm64/kernel/ptrace.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index ae71cadbd4df..bc08c93c69c5 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -2455,6 +2455,8 @@ int syscall_trace_enter(struct pt_regs *regs, unsigned long flags)
 
 void syscall_trace_exit(struct pt_regs *regs, unsigned long flags)
 {
+	rseq_syscall(regs);
+
 	audit_syscall_exit(regs);
 
 	if (flags & _TIF_SYSCALL_TRACEPOINT)
@@ -2462,8 +2464,6 @@ void syscall_trace_exit(struct pt_regs *regs, unsigned long flags)
 
 	if (flags & (_TIF_SYSCALL_TRACE | _TIF_SINGLESTEP))
 		report_syscall_exit(regs);
-
-	rseq_syscall(regs);
 }
 
 /*
-- 
2.34.1



From ruanjinjie at huawei.com  Mon Jun 29 06:06:02 2026
From: ruanjinjie at huawei.com (Jinjie Ruan)
Date: Mon, 29 Jun 2026 21:06:02 +0800
Subject: [PATCH v16 04/18] arm64: ptrace: Use syscall_get_nr() helper for syscall_trace_enter()
In-Reply-To: <20260629130616.642022-1-ruanjinjie@huawei.com>
References: <20260629130616.642022-1-ruanjinjie@huawei.com>
Message-ID: <20260629130616.642022-5-ruanjinjie@huawei.com>

Use syscall_get_nr() to get syscall number for syscall_trace_enter().
This aligns arm64's internal tracing logic with the generic
entry framework.

[Changes]
1. Use syscall_get_nr() helper:
   - Replace direct regs->syscallno access with
     syscall_get_nr(current, regs).
   - This helper is functionally equivalent to direct access on arm64.

2. Re-read syscall number after tracepoint:
  - Re-fetch the syscall number after trace_sys_enter() as it may have
    been modified by BPF or ftrace probes, matching generic entry behavior.

[Why this matters]
- Aligns arm64 with the generic entry interface.
- Makes future migration to generic entry framework smoother.
- Properly handles syscall number modifications by tracers.
- Uses standard architecture-independent helpers.

No functional changes intended.

Cc: Mark Rutland <mark.rutland at arm.com>
Cc: Will Deacon <will at kernel.org>
Cc: Catalin Marinas <catalin.marinas at arm.com>
Reviewed-by: Ada Couprie Diaz <ada.coupriediaz at arm.com>
Reviewed-by: Linus Walleij <linusw at kernel.org>
Reviewed-by: Yeoreum Yun <yeoreum.yun at arm.com>
Reviewed-by: Kevin Brodsky <kevin.brodsky at arm.com>
Signed-off-by: Jinjie Ruan <ruanjinjie at huawei.com>
---
 arch/arm64/kernel/ptrace.c | 19 +++++++++++++++----
 1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index ac15b81a71b5..863083de37c3 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -2410,6 +2410,7 @@ static void report_syscall_exit(struct pt_regs *regs)
 
 int syscall_trace_enter(struct pt_regs *regs, unsigned long flags)
 {
+	long syscall;
 	int ret;
 
 	if (flags & (_TIF_SYSCALL_EMU | _TIF_SYSCALL_TRACE)) {
@@ -2422,13 +2423,23 @@ int syscall_trace_enter(struct pt_regs *regs, unsigned long flags)
 	if (!secure_computing())
 		return NO_SYSCALL;
 
-	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
-		trace_sys_enter(regs, regs->syscallno);
+	/* Either of the above might have changed the syscall number */
+	syscall = syscall_get_nr(current, regs);
 
-	audit_syscall_entry(regs->syscallno, regs->orig_x0, regs->regs[1],
+	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) {
+		trace_sys_enter(regs, syscall);
+
+		/*
+		 * Probes or BPF hooks in the tracepoint may have changed the
+		 * system call number as well.
+		 */
+		 syscall = syscall_get_nr(current, regs);
+	}
+
+	audit_syscall_entry(syscall, regs->orig_x0, regs->regs[1],
 			    regs->regs[2], regs->regs[3]);
 
-	return regs->syscallno;
+	return syscall;
 }
 
 void syscall_trace_exit(struct pt_regs *regs, unsigned long flags)
-- 
2.34.1



From ruanjinjie at huawei.com  Mon Jun 29 06:06:06 2026
From: ruanjinjie at huawei.com (Jinjie Ruan)
Date: Mon, 29 Jun 2026 21:06:06 +0800
Subject: [PATCH v16 08/18] arm64: ptrace: Rename syscall_trace_exit() to syscall_exit_work()
In-Reply-To: <20260629130616.642022-1-ruanjinjie@huawei.com>
References: <20260629130616.642022-1-ruanjinjie@huawei.com>
Message-ID: <20260629130616.642022-9-ruanjinjie@huawei.com>

In preparation for moving arm64 over to the generic entry code, rename
syscall_trace_exit() to syscall_exit_work(). The renamed function checks
and prevents illegal system calls inside user-space rseq critical
sections, while also handling tracing, auditing, and ptrace reporting.

No functional changes.

Cc: Mark Rutland <mark.rutland at arm.com>
Cc: Thomas Gleixner <tglx at kernel.org>
Cc: Will Deacon <will at kernel.org>
Cc: Catalin Marinas <catalin.marinas at arm.com>
Cc: Ada Couprie Diaz <ada.coupriediaz at arm.com>
Signed-off-by: Jinjie Ruan <ruanjinjie at huawei.com>
---
 arch/arm64/include/asm/syscall.h | 2 +-
 arch/arm64/kernel/ptrace.c       | 2 +-
 arch/arm64/kernel/syscall.c      | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h
index 30b203ef156b..8205af0be612 100644
--- a/arch/arm64/include/asm/syscall.h
+++ b/arch/arm64/include/asm/syscall.h
@@ -121,6 +121,6 @@ static inline int syscall_get_arch(struct task_struct *task)
 }
 
 int syscall_trace_enter(struct pt_regs *regs, unsigned long flags);
-void syscall_trace_exit(struct pt_regs *regs, unsigned long flags);
+void syscall_exit_work(struct pt_regs *regs, unsigned long flags);
 
 #endif	/* __ASM_SYSCALL_H */
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index bc08c93c69c5..9ebe3389451c 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -2453,7 +2453,7 @@ int syscall_trace_enter(struct pt_regs *regs, unsigned long flags)
 	return syscall;
 }
 
-void syscall_trace_exit(struct pt_regs *regs, unsigned long flags)
+void syscall_exit_work(struct pt_regs *regs, unsigned long flags)
 {
 	rseq_syscall(regs);
 
diff --git a/arch/arm64/kernel/syscall.c b/arch/arm64/kernel/syscall.c
index c8e116397cfd..43ffb74162c5 100644
--- a/arch/arm64/kernel/syscall.c
+++ b/arch/arm64/kernel/syscall.c
@@ -133,7 +133,7 @@ static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
 
 trace_exit:
 	flags = read_thread_flags();
-	syscall_trace_exit(regs, flags);
+	syscall_exit_work(regs, flags);
 }
 
 void do_el0_svc(struct pt_regs *regs)
-- 
2.34.1



From ruanjinjie at huawei.com  Mon Jun 29 06:06:07 2026
From: ruanjinjie at huawei.com (Jinjie Ruan)
Date: Mon, 29 Jun 2026 21:06:07 +0800
Subject: [PATCH v16 09/18] arm64: syscall: Rework the syscall exit path in el0_svc_common()
In-Reply-To: <20260629130616.642022-1-ruanjinjie@huawei.com>
References: <20260629130616.642022-1-ruanjinjie@huawei.com>
Message-ID: <20260629130616.642022-10-ruanjinjie@huawei.com>

In preparation for moving arm64 over to the generic entry code,
restructure the fast-path syscall exit sequence within el0_svc_common().

Invert the nested conditional flags check so that when syscall work is
pending or single-stepping is active, the corresponding exit work can be
dispatched directly from within the fast-path bailout block. This clarifies
the separation between the fast and slow exit paths, facilitating a cleaner
transition to the generic entry state machine in subsequent patches.

No functional changes.

Cc: Mark Rutland <mark.rutland at arm.com>
Cc: Will Deacon <will at kernel.org>
Cc: Catalin Marinas <catalin.marinas at arm.com>
Cc: Ada Couprie Diaz <ada.coupriediaz at arm.com>
Signed-off-by: Jinjie Ruan <ruanjinjie at huawei.com>
---
 arch/arm64/kernel/syscall.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/kernel/syscall.c b/arch/arm64/kernel/syscall.c
index 43ffb74162c5..f6e9aa132b09 100644
--- a/arch/arm64/kernel/syscall.c
+++ b/arch/arm64/kernel/syscall.c
@@ -127,8 +127,11 @@ static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
 	 */
 	if (!has_syscall_work(flags) && !IS_ENABLED(CONFIG_DEBUG_RSEQ)) {
 		flags = read_thread_flags();
-		if (!has_syscall_work(flags) && !(flags & _TIF_SINGLESTEP))
-			return;
+		if (has_syscall_work(flags) || flags & _TIF_SINGLESTEP) {
+			flags = read_thread_flags();
+			syscall_exit_work(regs, flags);
+		}
+		return;
 	}
 
 trace_exit:
-- 
2.34.1



From ruanjinjie at huawei.com  Mon Jun 29 06:06:08 2026
From: ruanjinjie at huawei.com (Jinjie Ruan)
Date: Mon, 29 Jun 2026 21:06:08 +0800
Subject: [PATCH v16 10/18] arm64: ptrace: Extract syscall_exit_to_user_mode_work() helper
In-Reply-To: <20260629130616.642022-1-ruanjinjie@huawei.com>
References: <20260629130616.642022-1-ruanjinjie@huawei.com>
Message-ID: <20260629130616.642022-11-ruanjinjie@huawei.com>

In preparation for moving arm64 over to the generic entry code, extract
the core syscall exit tracing logic into a new inline helper,
syscall_exit_to_user_mode_work().

This new helper encapsulates the thread flags retrieval and syscall exit
processing, unifying the exit invocation paths across both the fast-path
bailout block and the slow-path trace_exit fallback in el0_svc_common().
This restructuring significantly streamlines the architecture for
the upcoming transition to the generic entry framework.

No functional changes.

Cc: Mark Rutland <mark.rutland at arm.com>
Cc: Will Deacon <will at kernel.org>
Cc: Catalin Marinas <catalin.marinas at arm.com>
Cc: Ada Couprie Diaz <ada.coupriediaz at arm.com>
Signed-off-by: Jinjie Ruan <ruanjinjie at huawei.com>
---
 arch/arm64/include/asm/syscall.h | 7 +++++++
 arch/arm64/kernel/syscall.c      | 9 +++------
 2 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h
index 8205af0be612..72461c22bb5e 100644
--- a/arch/arm64/include/asm/syscall.h
+++ b/arch/arm64/include/asm/syscall.h
@@ -123,4 +123,11 @@ static inline int syscall_get_arch(struct task_struct *task)
 int syscall_trace_enter(struct pt_regs *regs, unsigned long flags);
 void syscall_exit_work(struct pt_regs *regs, unsigned long flags);
 
+static __always_inline void syscall_exit_to_user_mode_work(struct pt_regs *regs)
+{
+	unsigned long flags = read_thread_flags();
+
+	syscall_exit_work(regs, flags);
+}
+
 #endif	/* __ASM_SYSCALL_H */
diff --git a/arch/arm64/kernel/syscall.c b/arch/arm64/kernel/syscall.c
index f6e9aa132b09..6de1fe281d61 100644
--- a/arch/arm64/kernel/syscall.c
+++ b/arch/arm64/kernel/syscall.c
@@ -127,16 +127,13 @@ static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
 	 */
 	if (!has_syscall_work(flags) && !IS_ENABLED(CONFIG_DEBUG_RSEQ)) {
 		flags = read_thread_flags();
-		if (has_syscall_work(flags) || flags & _TIF_SINGLESTEP) {
-			flags = read_thread_flags();
-			syscall_exit_work(regs, flags);
-		}
+		if (has_syscall_work(flags) || flags & _TIF_SINGLESTEP)
+			syscall_exit_to_user_mode_work(regs);
 		return;
 	}
 
 trace_exit:
-	flags = read_thread_flags();
-	syscall_exit_work(regs, flags);
+	syscall_exit_to_user_mode_work(regs);
 }
 
 void do_el0_svc(struct pt_regs *regs)
-- 
2.34.1



From ruanjinjie at huawei.com  Mon Jun 29 06:06:12 2026
From: ruanjinjie at huawei.com (Jinjie Ruan)
Date: Mon, 29 Jun 2026 21:06:12 +0800
Subject: [PATCH v16 14/18] arm64: syscall: Simplify syscall exit path in el0_svc_common()
In-Reply-To: <20260629130616.642022-1-ruanjinjie@huawei.com>
References: <20260629130616.642022-1-ruanjinjie@huawei.com>
Message-ID: <20260629130616.642022-15-ruanjinjie@huawei.com>

Remove the redundant conditional fast-path check within el0_svc_common()
as both branches now execute the exact the same
syscall_exit_to_user_mode_work().

Since both the conditional fast-path and the fallback slow-path now
uniformly invoke syscall_exit_to_user_mode_work(), this explicit
conditional branch is entirely redundant regardless of whether
the evaluation is true or false. Removing it collapses the duplicated
logic into a single, unconditional path.

No functional changes.

Cc: Mark Rutland <mark.rutland at arm.com>
Cc: Will Deacon <will at kernel.org>
Cc: Catalin Marinas <catalin.marinas at arm.com>
Cc: Ada Couprie Diaz <ada.coupriediaz at arm.com>
Signed-off-by: Jinjie Ruan <ruanjinjie at huawei.com>
---
 arch/arm64/kernel/syscall.c | 11 -----------
 1 file changed, 11 deletions(-)

diff --git a/arch/arm64/kernel/syscall.c b/arch/arm64/kernel/syscall.c
index 74308b6df43b..275cde8ab6f4 100644
--- a/arch/arm64/kernel/syscall.c
+++ b/arch/arm64/kernel/syscall.c
@@ -114,17 +114,6 @@ static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
 	}
 
 	invoke_syscall(regs, scno, sc_nr, syscall_table);
-
-	/*
-	 * The tracing status may have changed under our feet, so we have to
-	 * check again. However, if we were tracing entry, then we always trace
-	 * exit regardless, as the old entry assembly did.
-	 */
-	if (!(unlikely(flags & _TIF_SYSCALL_WORK)) && !IS_ENABLED(CONFIG_DEBUG_RSEQ)) {
-		syscall_exit_to_user_mode_work(regs);
-		return;
-	}
-
 trace_exit:
 	syscall_exit_to_user_mode_work(regs);
 }
-- 
2.34.1



From ruanjinjie at huawei.com  Mon Jun 29 06:06:10 2026
From: ruanjinjie at huawei.com (Jinjie Ruan)
Date: Mon, 29 Jun 2026 21:06:10 +0800
Subject: [PATCH v16 12/18] arm64: syscall: Use exit-specific flags check in el0_svc_common()
In-Reply-To: <20260629130616.642022-1-ruanjinjie@huawei.com>
References: <20260629130616.642022-1-ruanjinjie@huawei.com>
Message-ID: <20260629130616.642022-13-ruanjinjie@huawei.com>

Use exit-specific _TIF_SYSCALL_EXIT_WORK mask to filter out entry-only
flags during the system call exit path checks. This aligns arm64 with
the generic entry framework's SYSCALL_WORK_EXIT semantics.

[Rationale]
The current syscall exit path re-evaluates the thread flags using
the global _TIF_SYSCALL_WORK mask. However, _TIF_SYSCALL_WORK includes
flags that are strictly relevant to system call entry processing:

1. _TIF_SECCOMP: Seccomp filtering (__secure_computing()) only runs
  on entry. There is no seccomp callback for syscall exit.

2. _TIF_SYSCALL_EMU: In PTRACE_SYSEMU mode, the syscall is
  intercepted and skipped on entry. Since the syscall is never fully
  executed, reporting a separate syscall exit stop is unnecessary.

[Changes]
- _TIF_SYSCALL_EXIT_WORK: A new mask containing only flags
  requiring exit-time processing: _TIF_SYSCALL_TRACE, _TIF_SYSCALL_AUDIT,
  and _TIF_SYSCALL_TRACEPOINT.

- Optimize re-evaluation check: Use _TIF_SYSCALL_EXIT_WORK inside the
  el0_svc_common() fast-path block to prevent redundant exit work execution
  when entry-only flags fluctuate or are synchronously modified under
  the hood. The outermost gating maintains _TIF_SYSCALL_WORK to preserve
  architectural entry-exit symmetry for tracers.

- Cleanup: Remove the has_syscall_work() helper as it is no longer
  needed, supporting direct flag comparison to clearly distinguish between
  entry and exit mandates.

[Impact]
Unnecessary exit tracing and auditing processing are safely bypassed when
entry-specific flags fluctuate during the fast-path re-check block. This
safely streamlines the syscall exit sequence to mirror the generic entry
loop behaviors without breaking debugger expectations.

No functional changes intended

Cc: Mark Rutland <mark.rutland at arm.com>
Cc: Will Deacon <will at kernel.org>
Cc: Catalin Marinas <catalin.marinas at arm.com>
Cc: Ada Couprie Diaz <ada.coupriediaz at arm.com>
Reviewed-by: Ada Couprie Diaz <ada.coupriediaz at arm.com>
Reviewed-by: Linus Walleij <linusw at kernel.org>
Reviewed-by: Yeoreum Yun <yeoreum.yun at arm.com>
Signed-off-by: Jinjie Ruan <ruanjinjie at huawei.com>
---
 arch/arm64/kernel/syscall.c | 11 +++--------
 1 file changed, 3 insertions(+), 8 deletions(-)

diff --git a/arch/arm64/kernel/syscall.c b/arch/arm64/kernel/syscall.c
index 6de1fe281d61..5dd94bece929 100644
--- a/arch/arm64/kernel/syscall.c
+++ b/arch/arm64/kernel/syscall.c
@@ -54,11 +54,6 @@ static void invoke_syscall(struct pt_regs *regs, unsigned int scno,
 	syscall_set_return_value(current, regs, 0, ret);
 }
 
-static inline bool has_syscall_work(unsigned long flags)
-{
-	return unlikely(flags & _TIF_SYSCALL_WORK);
-}
-
 static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
 			   const syscall_fn_t syscall_table[])
 {
@@ -95,7 +90,7 @@ static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
 		return;
 	}
 
-	if (has_syscall_work(flags)) {
+	if (unlikely(flags & _TIF_SYSCALL_WORK)) {
 		/*
 		 * The de-facto standard way to skip a system call using ptrace
 		 * is to set the system call to -1 (NO_SYSCALL) and set x0 to a
@@ -125,9 +120,9 @@ static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
 	 * check again. However, if we were tracing entry, then we always trace
 	 * exit regardless, as the old entry assembly did.
 	 */
-	if (!has_syscall_work(flags) && !IS_ENABLED(CONFIG_DEBUG_RSEQ)) {
+	if (!(unlikely(flags & _TIF_SYSCALL_WORK)) && !IS_ENABLED(CONFIG_DEBUG_RSEQ)) {
 		flags = read_thread_flags();
-		if (has_syscall_work(flags) || flags & _TIF_SINGLESTEP)
+		if (unlikely(flags & _TIF_SYSCALL_EXIT_WORK) || flags & _TIF_SINGLESTEP)
 			syscall_exit_to_user_mode_work(regs);
 		return;
 	}
-- 
2.34.1



From ruanjinjie at huawei.com  Mon Jun 29 06:06:13 2026
From: ruanjinjie at huawei.com (Jinjie Ruan)
Date: Mon, 29 Jun 2026 21:06:13 +0800
Subject: [PATCH v16 15/18] arm64: ptrace: Skip syscall exit reporting for PTRACE_SYSEMU_SINGLESTEP
In-Reply-To: <20260629130616.642022-1-ruanjinjie@huawei.com>
References: <20260629130616.642022-1-ruanjinjie@huawei.com>
Message-ID: <20260629130616.642022-16-ruanjinjie@huawei.com>

Align the syscall exit reporting logic with the generic entry
framework by skipping the exit stop when PTRACE_SYSEMU_SINGLESTEP is
in effect.

[Rationale]
When a tracer uses PTRACE_SYSEMU_SINGLESTEP, both _TIF_SYSCALL_EMU
and _TIF_SINGLESTEP flags are set. Currently, arm64 reports a syscall
exit stop whenever _TIF_SINGLESTEP is set, regardless of the
emulation state.

However, as per the generic entry implementation (see
include/linux/entry-common.h):
"If SYSCALL_EMU is set, then the only reason to report is when SINGLESTEP
is set (i.e. PTRACE_SYSEMU_SINGLESTEP). This syscall instruction has been
already reported in syscall_trace_enter()."

Since PTRACE_SYSEMU intercepts and skips the actual syscall
execution, reporting a subsequent exit stop is redundant and
inconsistent with the expected behavior of emulated system calls.

[Changes]
- Introduce report_single_step(): Add a helper to encapsulate the
  logic for deciding whether to report a single-step stop at syscall
  exit. It returns false if _TIF_SYSCALL_EMU is set, ensuring the
  emulated syscall does not trigger a duplicate report.

- Update syscall_exit_work(): Use the new helper to determine
  the stepping state instead of directly checking _TIF_SINGLESTEP.

[Impact]
- PTRACE_SINGLESTEP: Continues to report exit stops for actual
  instructions.

- PTRACE_SYSEMU: Continues to skip exit stops.

- PTRACE_SYSEMU_SINGLESTEP: Now correctly skips the redundant exit
  stop, aligning arm64 with the generic entry infrastructure.

Cc: Mark Rutland <mark.rutland at arm.com>
Cc: Will Deacon <will at kernel.org>
Cc: Catalin Marinas <catalin.marinas at arm.com>
Reviewed-by: Ada Couprie Diaz <ada.coupriediaz at arm.com>
Reviewed-by: Linus Walleij <linusw at kernel.org>
Reviewed-by: Yeoreum Yun <yeoreum.yun at arm.com>
Signed-off-by: Jinjie Ruan <ruanjinjie at huawei.com>
---
 arch/arm64/kernel/ptrace.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index 05ceb9f2d038..650db9b47373 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -2452,14 +2452,25 @@ int syscall_trace_enter(struct pt_regs *regs, unsigned long flags)
 	return syscall;
 }
 
+static inline bool report_single_step(unsigned long flags)
+{
+	if (flags & _TIF_SYSCALL_EMU)
+		return false;
+
+	return flags & _TIF_SINGLESTEP;
+}
+
 void syscall_exit_work(struct pt_regs *regs, unsigned long flags)
 {
+	bool step;
+
 	audit_syscall_exit(regs);
 
 	if (flags & _TIF_SYSCALL_TRACEPOINT)
 		trace_sys_exit(regs, syscall_get_return_value(current, regs));
 
-	if (flags & (_TIF_SYSCALL_TRACE | _TIF_SINGLESTEP))
+	step = report_single_step(flags);
+	if (step || flags & _TIF_SYSCALL_TRACE)
 		report_syscall_exit(regs);
 }
 
-- 
2.34.1



From ruanjinjie at huawei.com  Mon Jun 29 06:06:11 2026
From: ruanjinjie at huawei.com (Jinjie Ruan)
Date: Mon, 29 Jun 2026 21:06:11 +0800
Subject: [PATCH v16 13/18] arm64: syscall: Simplify el0_svc_common() syscall exit path
In-Reply-To: <20260629130616.642022-1-ruanjinjie@huawei.com>
References: <20260629130616.642022-1-ruanjinjie@huawei.com>
Message-ID: <20260629130616.642022-14-ruanjinjie@huawei.com>

Remove the redundant nested conditional check within the system call
exit path of el0_svc_common() to streamline the exit sequence.

When entering this fast-path block, CONFIG_DEBUG_RSEQ is guaranteed to be
disabled. Under this constraint, the code logic inside the block becomes
completely identical to the evaluation performed within
syscall_exit_to_user_mode_work(). Therefore, invoking the inline helper
directly achieves full logical equivalence while eliminating duplicate
code nesting.

No functional changes.

Cc: Mark Rutland <mark.rutland at arm.com>
Cc: Will Deacon <will at kernel.org>
Cc: Catalin Marinas <catalin.marinas at arm.com>
Cc: Ada Couprie Diaz <ada.coupriediaz at arm.com>
Signed-off-by: Jinjie Ruan <ruanjinjie at huawei.com>
---
 arch/arm64/kernel/syscall.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/arch/arm64/kernel/syscall.c b/arch/arm64/kernel/syscall.c
index 5dd94bece929..74308b6df43b 100644
--- a/arch/arm64/kernel/syscall.c
+++ b/arch/arm64/kernel/syscall.c
@@ -121,9 +121,7 @@ static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
 	 * exit regardless, as the old entry assembly did.
 	 */
 	if (!(unlikely(flags & _TIF_SYSCALL_WORK)) && !IS_ENABLED(CONFIG_DEBUG_RSEQ)) {
-		flags = read_thread_flags();
-		if (unlikely(flags & _TIF_SYSCALL_EXIT_WORK) || flags & _TIF_SINGLESTEP)
-			syscall_exit_to_user_mode_work(regs);
+		syscall_exit_to_user_mode_work(regs);
 		return;
 	}
 
-- 
2.34.1



From ruanjinjie at huawei.com  Mon Jun 29 06:06:14 2026
From: ruanjinjie at huawei.com (Jinjie Ruan)
Date: Mon, 29 Jun 2026 21:06:14 +0800
Subject: [PATCH v16 16/18] arm64: entry: Convert to generic entry
In-Reply-To: <20260629130616.642022-1-ruanjinjie@huawei.com>
References: <20260629130616.642022-1-ruanjinjie@huawei.com>
Message-ID: <20260629130616.642022-17-ruanjinjie@huawei.com>

Implement the generic entry framework for arm64 to handle system call
entry and exit. This follows the migration of x86, RISC-V, and LoongArch,
consolidating architecture-specific syscall tracing and auditing into
the common kernel entry infrastructure, making it easier to enable
advanced features on arm64, such as "Syscall User Dispatch" and "rseq time
slice extension" optimization.

[Background]
Arm64 has already adopted generic IRQ entry. Completing the conversion
to the generic syscall entry framework reduces architectural divergence,
simplifies maintenance, and allows arm64 to automatically benefit from
improvements in the common entry code.

[Changes]

1. Kconfig and Infrastructure:
- Select GENERIC_ENTRY and remove GENERIC_IRQ_ENTRY (now implied).

- Migrate struct thread_info to use the syscall_work field instead
  of TIF flags for syscall-related tasks.

2. Thread Info and Flags:
- Remove definitions for TIF_SYSCALL_TRACE, TIF_SYSCALL_AUDIT,
  TIF_SYSCALL_TRACEPOINT, TIF_SECCOMP, and TIF_SYSCALL_EMU.

- Replace _TIF_SYSCALL_WORK and _TIF_SYSCALL_EXIT_WORK with the
  generic SYSCALL_WORK bitmask.

- Map single-step state to SYSCALL_EXIT_TRAP in debug-monitors.c.

3. Architecture-Specific Hooks (asm/entry-common.h):
- Implement arch_ptrace_report_syscall_entry() and _exit() by
  porting the existing arm64 logic to the generic interface.

4. Cleanup and Refactoring:
- Remove redundant arm64-specific syscall tracing functions from
  ptrace.c, including syscall_trace_enter(), syscall_exit_work(),
  and related audit/step helpers.

- Update el0_svc_common() in syscall.c to use the generic
  syscall_work checks and entry/exit call sites.

[Why this matters]
- Unified Interface: Aligns arm64 with the modern kernel entry standard.

- Improved Maintainability: Bug fixes in kernel/entry/common.c now
  apply to arm64 automatically.

- Feature Readiness: Simplifies the implementation of future
  cross-architecture syscall features.

[Compatibility]
This conversion maintains full ABI compatibility with existing
userspace. The ptrace register-saving behavior, seccomp filtering, and
syscall tracing semantics remain identical to the previous implementation.

Additionally, since arm64 now does not select HAVE_GENERIC_TIF_BITS, there
is no functional change regarding rseq management, as those generic entry
pathways remain a no-op for this architecture."

Cc: Mark Rutland <mark.rutland at arm.com>
Cc: Will Deacon <will at kernel.org>
Cc: Catalin Marinas <catalin.marinas at arm.com>
Cc: Thomas Gleixner <tglx at kernel.org>
Cc: Peter Zijlstra <peterz at infradead.org>
Tested-by: Ada Couprie Diaz <ada.coupriediaz at arm.com>
Reviewed-by: Ada Couprie Diaz <ada.coupriediaz at arm.com>
Reviewed-by: Linus Walleij <linusw at kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz at infradead.org>
Reviewed-by: Yeoreum Yun <yeoreum.yun at arm.com>
Reviewed-by: Kevin Brodsky <kevin.brodsky at arm.com>
Suggested-by: Kevin Brodsky <kevin.brodsky at arm.com>
Suggested-by: Mark Rutland <mark.rutland at arm.com>
Signed-off-by: Jinjie Ruan <ruanjinjie at huawei.com>
---
 arch/arm64/Kconfig                    |   2 +-
 arch/arm64/include/asm/entry-common.h |  76 ++++++++++++++
 arch/arm64/include/asm/syscall.h      |  13 ---
 arch/arm64/include/asm/thread_info.h  |  19 +---
 arch/arm64/kernel/debug-monitors.c    |   8 ++
 arch/arm64/kernel/ptrace.c            | 142 --------------------------
 arch/arm64/kernel/signal.c            |   2 +-
 arch/arm64/kernel/syscall.c           |   7 +-
 8 files changed, 92 insertions(+), 177 deletions(-)

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index b3afe0688919..aea686dce3d5 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -128,9 +128,9 @@ config ARM64
 	select GENERIC_CPU_DEVICES
 	select GENERIC_CPU_VULNERABILITIES
 	select GENERIC_EARLY_IOREMAP
+	select GENERIC_ENTRY
 	select GENERIC_IDLE_POLL_SETUP
 	select GENERIC_IOREMAP
-	select GENERIC_IRQ_ENTRY
 	select GENERIC_IRQ_IPI
 	select GENERIC_IRQ_KEXEC_CLEAR_VM_FORWARD
 	select GENERIC_IRQ_PROBE
diff --git a/arch/arm64/include/asm/entry-common.h b/arch/arm64/include/asm/entry-common.h
index cab8cd78f693..d8bf4bf342e8 100644
--- a/arch/arm64/include/asm/entry-common.h
+++ b/arch/arm64/include/asm/entry-common.h
@@ -3,14 +3,21 @@
 #ifndef _ASM_ARM64_ENTRY_COMMON_H
 #define _ASM_ARM64_ENTRY_COMMON_H
 
+#include <linux/ptrace.h>
 #include <linux/thread_info.h>
 
+#include <asm/compat.h>
 #include <asm/cpufeature.h>
 #include <asm/daifflags.h>
 #include <asm/fpsimd.h>
 #include <asm/mte.h>
 #include <asm/stacktrace.h>
 
+enum ptrace_syscall_dir {
+	PTRACE_SYSCALL_ENTER = 0,
+	PTRACE_SYSCALL_EXIT,
+};
+
 #define ARCH_EXIT_TO_USER_MODE_WORK (_TIF_MTE_ASYNC_FAULT | _TIF_FOREIGN_FPSTATE)
 
 static __always_inline void arch_exit_to_user_mode_work(struct pt_regs *regs,
@@ -54,4 +61,73 @@ static inline bool arch_irqentry_exit_need_resched(void)
 
 #define arch_irqentry_exit_need_resched arch_irqentry_exit_need_resched
 
+static __always_inline unsigned long ptrace_save_reg(struct pt_regs *regs,
+						     enum ptrace_syscall_dir dir,
+						     int *regno)
+{
+	unsigned long saved_reg;
+
+	/*
+	 * We have some ABI weirdness here in the way that we handle syscall
+	 * exit stops because we indicate whether or not the stop has been
+	 * signalled from syscall entry or syscall exit by clobbering a general
+	 * purpose register (ip/r12 for AArch32, x7 for AArch64) in the tracee
+	 * and restoring its old value after the stop. This means that:
+	 *
+	 * - Any writes by the tracer to this register during the stop are
+	 *   ignored/discarded.
+	 *
+	 * - The actual value of the register is not available during the stop,
+	 *   so the tracer cannot save it and restore it later.
+	 *
+	 * - Syscall stops behave differently to seccomp and pseudo-step traps
+	 *   (the latter do not nobble any registers).
+	 */
+	*regno = (is_compat_task() ? 12 : 7);
+	saved_reg = regs->regs[*regno];
+	regs->regs[*regno] = dir;
+
+	return saved_reg;
+}
+
+static __always_inline int arch_ptrace_report_syscall_entry(struct pt_regs *regs)
+{
+	unsigned long saved_reg;
+	int regno, ret;
+
+	saved_reg = ptrace_save_reg(regs, PTRACE_SYSCALL_ENTER, &regno);
+	ret = ptrace_report_syscall_entry(regs);
+	if (ret)
+		forget_syscall(regs);
+	regs->regs[regno] = saved_reg;
+
+	return ret;
+}
+
+#define arch_ptrace_report_syscall_entry arch_ptrace_report_syscall_entry
+
+static __always_inline void arch_ptrace_report_syscall_exit(struct pt_regs *regs,
+							    int step)
+{
+	unsigned long saved_reg;
+	int regno;
+
+	saved_reg = ptrace_save_reg(regs, PTRACE_SYSCALL_EXIT, &regno);
+	if (!step) {
+		ptrace_report_syscall_exit(regs, 0);
+		regs->regs[regno] = saved_reg;
+	} else {
+		regs->regs[regno] = saved_reg;
+
+		/*
+		 * Signal a pseudo-step exception since we are stepping but
+		 * tracer modifications to the registers may have rewound the
+		 * state machine.
+		 */
+		ptrace_report_syscall_exit(regs, 1);
+	}
+}
+
+#define arch_ptrace_report_syscall_exit arch_ptrace_report_syscall_exit
+
 #endif /* _ASM_ARM64_ENTRY_COMMON_H */
diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h
index b982398f8765..84b1c36233d4 100644
--- a/arch/arm64/include/asm/syscall.h
+++ b/arch/arm64/include/asm/syscall.h
@@ -121,17 +121,4 @@ static inline int syscall_get_arch(struct task_struct *task)
 	return AUDIT_ARCH_AARCH64;
 }
 
-int syscall_trace_enter(struct pt_regs *regs, unsigned long flags);
-void syscall_exit_work(struct pt_regs *regs, unsigned long flags);
-
-static __always_inline void syscall_exit_to_user_mode_work(struct pt_regs *regs)
-{
-	unsigned long flags = read_thread_flags();
-
-	rseq_syscall(regs);
-
-	if (unlikely(flags & _TIF_SYSCALL_EXIT_WORK) || flags & _TIF_SINGLESTEP)
-		syscall_exit_work(regs, flags);
-}
-
 #endif	/* __ASM_SYSCALL_H */
diff --git a/arch/arm64/include/asm/thread_info.h b/arch/arm64/include/asm/thread_info.h
index 56a2c9426a32..3f621ba0f961 100644
--- a/arch/arm64/include/asm/thread_info.h
+++ b/arch/arm64/include/asm/thread_info.h
@@ -46,6 +46,7 @@ struct thread_info {
 	u64			mpam_partid_pmg;
 #endif
 	u32			cpu;
+	unsigned long		syscall_work;   /* SYSCALL_WORK_ flags */
 };
 
 #define thread_saved_pc(tsk)	\
@@ -68,11 +69,6 @@ void arch_setup_new_exec(void);
 #define TIF_UPROBE		5	/* uprobe breakpoint or singlestep */
 #define TIF_MTE_ASYNC_FAULT	6	/* MTE Asynchronous Tag Check Fault */
 #define TIF_NOTIFY_SIGNAL	7	/* signal notifications exist */
-#define TIF_SYSCALL_TRACE	8	/* syscall trace active */
-#define TIF_SYSCALL_AUDIT	9	/* syscall auditing */
-#define TIF_SYSCALL_TRACEPOINT	10	/* syscall tracepoint for ftrace */
-#define TIF_SECCOMP		11	/* syscall secure computing */
-#define TIF_SYSCALL_EMU		12	/* syscall emulation active */
 #define TIF_PATCH_PENDING	13	/* pending live patching update */
 #define TIF_MEMDIE		18	/* is terminating due to OOM killer */
 #define TIF_FREEZE		19
@@ -94,27 +90,14 @@ void arch_setup_new_exec(void);
 #define _TIF_NEED_RESCHED_LAZY	(1 << TIF_NEED_RESCHED_LAZY)
 #define _TIF_NOTIFY_RESUME	(1 << TIF_NOTIFY_RESUME)
 #define _TIF_FOREIGN_FPSTATE	(1 << TIF_FOREIGN_FPSTATE)
-#define _TIF_SYSCALL_TRACE	(1 << TIF_SYSCALL_TRACE)
-#define _TIF_SYSCALL_AUDIT	(1 << TIF_SYSCALL_AUDIT)
-#define _TIF_SYSCALL_TRACEPOINT	(1 << TIF_SYSCALL_TRACEPOINT)
-#define _TIF_SECCOMP		(1 << TIF_SECCOMP)
-#define _TIF_SYSCALL_EMU	(1 << TIF_SYSCALL_EMU)
 #define _TIF_PATCH_PENDING	(1 << TIF_PATCH_PENDING)
 #define _TIF_UPROBE		(1 << TIF_UPROBE)
-#define _TIF_SINGLESTEP		(1 << TIF_SINGLESTEP)
 #define _TIF_32BIT		(1 << TIF_32BIT)
 #define _TIF_SVE		(1 << TIF_SVE)
 #define _TIF_MTE_ASYNC_FAULT	(1 << TIF_MTE_ASYNC_FAULT)
 #define _TIF_NOTIFY_SIGNAL	(1 << TIF_NOTIFY_SIGNAL)
 #define _TIF_TSC_SIGSEGV	(1 << TIF_TSC_SIGSEGV)
 
-#define _TIF_SYSCALL_WORK	(_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \
-				 _TIF_SYSCALL_TRACEPOINT | _TIF_SECCOMP | \
-				 _TIF_SYSCALL_EMU)
-
-#define _TIF_SYSCALL_EXIT_WORK	(_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \
-				 _TIF_SYSCALL_TRACEPOINT)
-
 #ifdef CONFIG_SHADOW_CALL_STACK
 #define INIT_SCS							\
 	.scs_base	= init_shadow_call_stack,			\
diff --git a/arch/arm64/kernel/debug-monitors.c b/arch/arm64/kernel/debug-monitors.c
index 29307642f4c9..c53683839155 100644
--- a/arch/arm64/kernel/debug-monitors.c
+++ b/arch/arm64/kernel/debug-monitors.c
@@ -385,11 +385,19 @@ void user_enable_single_step(struct task_struct *task)
 
 	if (!test_and_set_ti_thread_flag(ti, TIF_SINGLESTEP))
 		set_regs_spsr_ss(task_pt_regs(task));
+
+	/*
+	 * Ensure that the generic entry code triggers a trap once stepping
+	 * out of a system call prior to executing any user instruction,
+	 * as the generic entry code does not natively check for TIF_SINGLESTEP.
+	 */
+	set_task_syscall_work(task, SYSCALL_EXIT_TRAP);
 }
 NOKPROBE_SYMBOL(user_enable_single_step);
 
 void user_disable_single_step(struct task_struct *task)
 {
 	clear_ti_thread_flag(task_thread_info(task), TIF_SINGLESTEP);
+	clear_task_syscall_work(task, SYSCALL_EXIT_TRAP);
 }
 NOKPROBE_SYMBOL(user_disable_single_step);
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index 650db9b47373..61a668682947 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -8,7 +8,6 @@
  * Copyright (C) 2012 ARM Ltd.
  */
 
-#include <linux/audit.h>
 #include <linux/compat.h>
 #include <linux/kernel.h>
 #include <linux/sched/signal.h>
@@ -18,7 +17,6 @@
 #include <linux/smp.h>
 #include <linux/ptrace.h>
 #include <linux/user.h>
-#include <linux/seccomp.h>
 #include <linux/security.h>
 #include <linux/init.h>
 #include <linux/signal.h>
@@ -37,13 +35,9 @@
 #include <asm/mte.h>
 #include <asm/pointer_auth.h>
 #include <asm/stacktrace.h>
-#include <asm/syscall.h>
 #include <asm/traps.h>
 #include <asm/system_misc.h>
 
-#define CREATE_TRACE_POINTS
-#include <trace/events/syscalls.h>
-
 struct pt_regs_offset {
 	const char *name;
 	int offset;
@@ -2338,142 +2332,6 @@ long arch_ptrace(struct task_struct *child, long request,
 	return ptrace_request(child, request, addr, data);
 }
 
-enum ptrace_syscall_dir {
-	PTRACE_SYSCALL_ENTER = 0,
-	PTRACE_SYSCALL_EXIT,
-};
-
-static __always_inline unsigned long ptrace_save_reg(struct pt_regs *regs,
-						     enum ptrace_syscall_dir dir,
-						     int *regno)
-{
-	unsigned long saved_reg;
-
-	/*
-	 * We have some ABI weirdness here in the way that we handle syscall
-	 * exit stops because we indicate whether or not the stop has been
-	 * signalled from syscall entry or syscall exit by clobbering a general
-	 * purpose register (ip/r12 for AArch32, x7 for AArch64) in the tracee
-	 * and restoring its old value after the stop. This means that:
-	 *
-	 * - Any writes by the tracer to this register during the stop are
-	 *   ignored/discarded.
-	 *
-	 * - The actual value of the register is not available during the stop,
-	 *   so the tracer cannot save it and restore it later.
-	 *
-	 * - Syscall stops behave differently to seccomp and pseudo-step traps
-	 *   (the latter do not nobble any registers).
-	 */
-	*regno = (is_compat_task() ? 12 : 7);
-	saved_reg = regs->regs[*regno];
-	regs->regs[*regno] = dir;
-
-	return saved_reg;
-}
-
-static int report_syscall_entry(struct pt_regs *regs)
-{
-	unsigned long saved_reg;
-	int regno, ret;
-
-	saved_reg = ptrace_save_reg(regs, PTRACE_SYSCALL_ENTER, &regno);
-	ret = ptrace_report_syscall_entry(regs);
-	if (ret)
-		forget_syscall(regs);
-	regs->regs[regno] = saved_reg;
-
-	return ret;
-}
-
-static void report_syscall_exit(struct pt_regs *regs)
-{
-	unsigned long saved_reg;
-	int regno;
-
-	saved_reg = ptrace_save_reg(regs, PTRACE_SYSCALL_EXIT, &regno);
-	if (!test_thread_flag(TIF_SINGLESTEP)) {
-		ptrace_report_syscall_exit(regs, 0);
-		regs->regs[regno] = saved_reg;
-	} else {
-		regs->regs[regno] = saved_reg;
-
-		/*
-		 * Signal a pseudo-step exception since we are stepping but
-		 * tracer modifications to the registers may have rewound the
-		 * state machine.
-		 */
-		ptrace_report_syscall_exit(regs, 1);
-	}
-}
-
-static inline void syscall_enter_audit(struct pt_regs *regs, long syscall)
-{
-	if (unlikely(audit_context())) {
-		unsigned long args[6];
-
-		syscall_get_arguments(current, regs, args);
-		audit_syscall_entry(syscall, args[0], args[1], args[2], args[3]);
-	}
-}
-
-int syscall_trace_enter(struct pt_regs *regs, unsigned long flags)
-{
-	long syscall;
-	int ret;
-
-	if (flags & (_TIF_SYSCALL_EMU | _TIF_SYSCALL_TRACE)) {
-		ret = report_syscall_entry(regs);
-		if (ret || (flags & _TIF_SYSCALL_EMU))
-			return NO_SYSCALL;
-	}
-
-	/* Do the secure computing after ptrace; failures should be fast. */
-	if (flags & _TIF_SECCOMP) {
-		if (!__secure_computing())
-			return NO_SYSCALL;
-	}
-
-	/* Either of the above might have changed the syscall number */
-	syscall = syscall_get_nr(current, regs);
-
-	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) {
-		trace_sys_enter(regs, syscall);
-
-		/*
-		 * Probes or BPF hooks in the tracepoint may have changed the
-		 * system call number as well.
-		 */
-		 syscall = syscall_get_nr(current, regs);
-	}
-
-	syscall_enter_audit(regs, syscall);
-
-	return syscall;
-}
-
-static inline bool report_single_step(unsigned long flags)
-{
-	if (flags & _TIF_SYSCALL_EMU)
-		return false;
-
-	return flags & _TIF_SINGLESTEP;
-}
-
-void syscall_exit_work(struct pt_regs *regs, unsigned long flags)
-{
-	bool step;
-
-	audit_syscall_exit(regs);
-
-	if (flags & _TIF_SYSCALL_TRACEPOINT)
-		trace_sys_exit(regs, syscall_get_return_value(current, regs));
-
-	step = report_single_step(flags);
-	if (step || flags & _TIF_SYSCALL_TRACE)
-		report_syscall_exit(regs);
-}
-
 /*
  * SPSR_ELx bits which are always architecturally RES0 per ARM DDI 0487D.a.
  * We permit userspace to set SSBS (AArch64 bit 12, AArch32 bit 23) which is
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
index 38e6fa204c17..093eebb9d764 100644
--- a/arch/arm64/kernel/signal.c
+++ b/arch/arm64/kernel/signal.c
@@ -8,8 +8,8 @@
 
 #include <linux/cache.h>
 #include <linux/compat.h>
+#include <linux/entry-common.h>
 #include <linux/errno.h>
-#include <linux/irq-entry-common.h>
 #include <linux/kernel.h>
 #include <linux/signal.h>
 #include <linux/freezer.h>
diff --git a/arch/arm64/kernel/syscall.c b/arch/arm64/kernel/syscall.c
index 275cde8ab6f4..41a3b70a9374 100644
--- a/arch/arm64/kernel/syscall.c
+++ b/arch/arm64/kernel/syscall.c
@@ -2,6 +2,7 @@
 
 #include <linux/compiler.h>
 #include <linux/context_tracking.h>
+#include <linux/entry-common.h>
 #include <linux/errno.h>
 #include <linux/nospec.h>
 #include <linux/ptrace.h>
@@ -58,6 +59,7 @@ static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
 			   const syscall_fn_t syscall_table[])
 {
 	unsigned long flags = read_thread_flags();
+	unsigned long work;
 
 	regs->orig_x0 = regs->regs[0];
 	regs->syscallno = scno;
@@ -90,7 +92,8 @@ static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
 		return;
 	}
 
-	if (unlikely(flags & _TIF_SYSCALL_WORK)) {
+	work = READ_ONCE(current_thread_info()->syscall_work);
+	if (unlikely(work & SYSCALL_WORK_ENTER)) {
 		/*
 		 * The de-facto standard way to skip a system call using ptrace
 		 * is to set the system call to -1 (NO_SYSCALL) and set x0 to a
@@ -108,7 +111,7 @@ static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
 		 */
 		if (scno == NO_SYSCALL)
 			syscall_set_return_value(current, regs, -ENOSYS, 0);
-		scno = syscall_trace_enter(regs, read_thread_flags());
+		scno = syscall_trace_enter(regs, work);
 		if (scno == NO_SYSCALL)
 			goto trace_exit;
 	}
-- 
2.34.1



From ruanjinjie at huawei.com  Mon Jun 29 06:06:15 2026
From: ruanjinjie at huawei.com (Jinjie Ruan)
Date: Mon, 29 Jun 2026 21:06:15 +0800
Subject: [PATCH v16 17/18] arm64: Inline el0_svc_common()
In-Reply-To: <20260629130616.642022-1-ruanjinjie@huawei.com>
References: <20260629130616.642022-1-ruanjinjie@huawei.com>
Message-ID: <20260629130616.642022-18-ruanjinjie@huawei.com>

After converting arm64 to Generic Entry framework, the compiler no longer
inlines el0_svc_common() into its caller do_el0_svc(). This introduces
a small but measurable overhead in the critical system call path.

Manually forcing el0_svc_common() to be inlined restores the
performance. Benchmarking with perf bench syscall basic on a
Kunpeng 920 platform (based on v6.19-rc1) shows a ~1% performance
uplift.

Inlining this function reduces function prologue/epilogue overhead
and allows for better compiler optimization in the hot system call
dispatch path.

| Metric     | W/O this patch | With this patch | Change    |
| ---------- | -------------- | --------------- | --------- |
| Total time | 2.195 [sec]    | 2.171 [sec]     |  ?1.1%   |
| usecs/op   | 0.219575       | 0.217192        |  ?1.1%   |
| ops/sec    | 4,554,260      | 4,604,225       |  ?1.1%    |

Cc: Mark Rutland <mark.rutland at arm.com>
Cc: Will Deacon <will at kernel.org>
Cc: Catalin Marinas <catalin.marinas at arm.com>
Reviewed-by: Ada Couprie Diaz <ada.coupriediaz at arm.com>
Reviewed-by: Linus Walleij <linusw at kernel.org>
Reviewed-by: Yeoreum Yun <yeoreum.yun at arm.com>
Reviewed-by: Kevin Brodsky <kevin.brodsky at arm.com>
Signed-off-by: Jinjie Ruan <ruanjinjie at huawei.com>
---
 arch/arm64/kernel/syscall.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/kernel/syscall.c b/arch/arm64/kernel/syscall.c
index 41a3b70a9374..e0a98fac3b85 100644
--- a/arch/arm64/kernel/syscall.c
+++ b/arch/arm64/kernel/syscall.c
@@ -55,8 +55,8 @@ static void invoke_syscall(struct pt_regs *regs, unsigned int scno,
 	syscall_set_return_value(current, regs, 0, ret);
 }
 
-static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
-			   const syscall_fn_t syscall_table[])
+static __always_inline void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
+					   const syscall_fn_t syscall_table[])
 {
 	unsigned long flags = read_thread_flags();
 	unsigned long work;
-- 
2.34.1



From ruanjinjie at huawei.com  Mon Jun 29 06:06:16 2026
From: ruanjinjie at huawei.com (Jinjie Ruan)
Date: Mon, 29 Jun 2026 21:06:16 +0800
Subject: [PATCH v16 18/18] arm64: vdso: Expose sigreturn address on vdso to the kernel
In-Reply-To: <20260629130616.642022-1-ruanjinjie@huawei.com>
References: <20260629130616.642022-1-ruanjinjie@huawei.com>
Message-ID: <20260629130616.642022-19-ruanjinjie@huawei.com>

Syscall User Dispatch (SUD) requires the signal trampoline code to not be
intercepted. This is necessary to support returning with a locked selector
while avoiding infinite recursion back into the signal handler.

Implement arch_syscall_is_vdso_sigreturn() for arm64 to support this
exclusion mechanism. For native 64-bit tasks, it checks whether the current
PC matches the 'svc #0' instruction inside the vDSO sigreturn trampoline.

	SYM_CODE_START(__kernel_rt_sigreturn)
	        mov     x8, #__NR_rt_sigreturn
	        svc     #0
	SYM_CODE_END(__kernel_rt_sigreturn)

For COMPAT tasks, it verifies if the instruction falls within
the architecture's 'sigpage' range, allowing the kernel to safely bypass
dispatching syscalls originating from these areas back to userspace.

Suggested-by: Kevin Brodsky <kevin.brodsky at arm.com>
Suggested-by: kemal <kmal at cock.li>
Signed-off-by: Jinjie Ruan <ruanjinjie at huawei.com>
---
 arch/arm64/include/asm/elf.h |  1 +
 arch/arm64/kernel/vdso.c     | 16 ++++++++++++++++
 2 files changed, 17 insertions(+)

diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h
index d2779d604c7b..f43d4dea916a 100644
--- a/arch/arm64/include/asm/elf.h
+++ b/arch/arm64/include/asm/elf.h
@@ -185,6 +185,7 @@ do {									\
 struct linux_binprm;
 extern int arch_setup_additional_pages(struct linux_binprm *bprm,
 				       int uses_interp);
+extern bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs);
 
 /* 1GB of VA */
 #ifdef CONFIG_COMPAT
diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c
index 592dd8668de4..5a0314a3c26e 100644
--- a/arch/arm64/kernel/vdso.c
+++ b/arch/arm64/kernel/vdso.c
@@ -343,3 +343,19 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
 
 	return ret;
 }
+
+bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs)
+{
+	unsigned long sigtramp;
+
+#ifdef CONFIG_COMPAT
+	if (is_compat_task()) {
+		unsigned long sigpage = (unsigned long)current->mm->context.sigpage;
+
+		return regs->pc >= sigpage && regs->pc < (sigpage + PAGE_SIZE);
+	}
+#endif
+	sigtramp = (unsigned long)VDSO_SYMBOL(current->mm->context.vdso, sigtramp);
+
+	return regs->pc == (sigtramp + 8);
+}
-- 
2.34.1



From ruanjinjie at huawei.com  Mon Jun 29 06:06:09 2026
From: ruanjinjie at huawei.com (Jinjie Ruan)
Date: Mon, 29 Jun 2026 21:06:09 +0800
Subject: [PATCH v16 11/18] arm64: ptrace: Align syscall exit work semantics with generic entry
In-Reply-To: <20260629130616.642022-1-ruanjinjie@huawei.com>
References: <20260629130616.642022-1-ruanjinjie@huawei.com>
Message-ID: <20260629130616.642022-12-ruanjinjie@huawei.com>

Streamline syscall_exit_to_user_mode_work() to align arm64's syscall
exit behavior with the generic entry framework.

[Rationale]
1. Unconditional RSEQ Execution: Relocate rseq_syscall() from the slow-path
   helper to the very beginning of syscall_exit_to_user_mode_work(). This
   ensures that RSEQ validation executes unconditionally across all exit
   scenarios, preventing it from being incorrectly bypassed on fast paths
   when CONFIG_DEBUG_RSEQ is active.

2. Centralized Exit Work Gating: Introduce the `_TIF_SYSCALL_EXIT_WORK`
   mask to aggregate exit thread flags and gate the execution of
   syscall_exit_work().

   Gating audit_syscall_exit() behind this exit-time check introduces
   no functional changes. The `SYSCALL_AUDIT` flag and its context are
   statically allocated via audit_alloc() at fork time and only freed via
   audit_free() at do_exit(). Since the flag remains persistent and static
   throughout syscall execution, checking `_TIF_SYSCALL_AUDIT` in the mask
   is fully equivalent to evaluating audit_context() inside
   audit_syscall_exit().

[Changes]
- Introduce the `_TIF_SYSCALL_EXIT_WORK` mask to bundle exit-specific
  flags.

- Relocate rseq_syscall() to run unconditionally on the outermost layer.

- Gate syscall_exit_work() via the new aggregated flag check to mirror
  the generic entry loop behavior.

No functional changes intended.

Cc: Mark Rutland <mark.rutland at arm.com>
Cc: Will Deacon <will at kernel.org>
Cc: Catalin Marinas <catalin.marinas at arm.com>
Cc: Ada Couprie Diaz <ada.coupriediaz at arm.com>
Signed-off-by: Jinjie Ruan <ruanjinjie at huawei.com>
---
 arch/arm64/include/asm/syscall.h     | 6 +++++-
 arch/arm64/include/asm/thread_info.h | 3 +++
 arch/arm64/kernel/ptrace.c           | 3 ---
 3 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h
index 72461c22bb5e..b982398f8765 100644
--- a/arch/arm64/include/asm/syscall.h
+++ b/arch/arm64/include/asm/syscall.h
@@ -8,6 +8,7 @@
 #include <uapi/linux/audit.h>
 #include <linux/compat.h>
 #include <linux/err.h>
+#include <linux/rseq.h>
 
 typedef long (*syscall_fn_t)(const struct pt_regs *regs);
 
@@ -127,7 +128,10 @@ static __always_inline void syscall_exit_to_user_mode_work(struct pt_regs *regs)
 {
 	unsigned long flags = read_thread_flags();
 
-	syscall_exit_work(regs, flags);
+	rseq_syscall(regs);
+
+	if (unlikely(flags & _TIF_SYSCALL_EXIT_WORK) || flags & _TIF_SINGLESTEP)
+		syscall_exit_work(regs, flags);
 }
 
 #endif	/* __ASM_SYSCALL_H */
diff --git a/arch/arm64/include/asm/thread_info.h b/arch/arm64/include/asm/thread_info.h
index 5d7fe3e153c8..56a2c9426a32 100644
--- a/arch/arm64/include/asm/thread_info.h
+++ b/arch/arm64/include/asm/thread_info.h
@@ -112,6 +112,9 @@ void arch_setup_new_exec(void);
 				 _TIF_SYSCALL_TRACEPOINT | _TIF_SECCOMP | \
 				 _TIF_SYSCALL_EMU)
 
+#define _TIF_SYSCALL_EXIT_WORK	(_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \
+				 _TIF_SYSCALL_TRACEPOINT)
+
 #ifdef CONFIG_SHADOW_CALL_STACK
 #define INIT_SCS							\
 	.scs_base	= init_shadow_call_stack,			\
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index 9ebe3389451c..05ceb9f2d038 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -28,7 +28,6 @@
 #include <linux/hw_breakpoint.h>
 #include <linux/regset.h>
 #include <linux/elf.h>
-#include <linux/rseq.h>
 
 #include <asm/compat.h>
 #include <asm/cpufeature.h>
@@ -2455,8 +2454,6 @@ int syscall_trace_enter(struct pt_regs *regs, unsigned long flags)
 
 void syscall_exit_work(struct pt_regs *regs, unsigned long flags)
 {
-	rseq_syscall(regs);
-
 	audit_syscall_exit(regs);
 
 	if (flags & _TIF_SYSCALL_TRACEPOINT)
-- 
2.34.1



From thomas.weissschuh at linutronix.de  Tue Jun 30 08:32:04 2026
From: thomas.weissschuh at linutronix.de (Thomas =?utf-8?Q?Wei=C3=9Fschuh?=)
Date: Tue, 30 Jun 2026 17:32:04 +0200
Subject: [PATCH v16 18/18] arm64: vdso: Expose sigreturn address on vdso
 to the kernel
In-Reply-To: <20260629130616.642022-19-ruanjinjie@huawei.com>
References: <20260629130616.642022-1-ruanjinjie@huawei.com>
 <20260629130616.642022-19-ruanjinjie@huawei.com>
Message-ID: <20260630172619-6c78c2e2-c2d3-44be-88e6-6c82de12baf4@linutronix.de>

On Mon, Jun 29, 2026 at 09:06:16PM +0800, Jinjie Ruan wrote:
> Syscall User Dispatch (SUD) requires the signal trampoline code to not be
> intercepted. This is necessary to support returning with a locked selector
> while avoiding infinite recursion back into the signal handler.
> 
> Implement arch_syscall_is_vdso_sigreturn() for arm64 to support this
> exclusion mechanism. For native 64-bit tasks, it checks whether the current
> PC matches the 'svc #0' instruction inside the vDSO sigreturn trampoline.
> 
> 	SYM_CODE_START(__kernel_rt_sigreturn)
> 	        mov     x8, #__NR_rt_sigreturn
> 	        svc     #0
> 	SYM_CODE_END(__kernel_rt_sigreturn)
> 
> For COMPAT tasks, it verifies if the instruction falls within
> the architecture's 'sigpage' range, allowing the kernel to safely bypass
> dispatching syscalls originating from these areas back to userspace.
> 
> Suggested-by: Kevin Brodsky <kevin.brodsky at arm.com>
> Suggested-by: kemal <kmal at cock.li>
> Signed-off-by: Jinjie Ruan <ruanjinjie at huawei.com>
> ---
>  arch/arm64/include/asm/elf.h |  1 +
>  arch/arm64/kernel/vdso.c     | 16 ++++++++++++++++
>  2 files changed, 17 insertions(+)
> 
> diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h
> index d2779d604c7b..f43d4dea916a 100644
> --- a/arch/arm64/include/asm/elf.h
> +++ b/arch/arm64/include/asm/elf.h
> @@ -185,6 +185,7 @@ do {									\
>  struct linux_binprm;
>  extern int arch_setup_additional_pages(struct linux_binprm *bprm,
>  				       int uses_interp);
> +extern bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs);

A header for that?

>  /* 1GB of VA */
>  #ifdef CONFIG_COMPAT
> diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c
> index 592dd8668de4..5a0314a3c26e 100644
> --- a/arch/arm64/kernel/vdso.c
> +++ b/arch/arm64/kernel/vdso.c
> @@ -343,3 +343,19 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
>  
>  	return ret;
>  }
> +
> +bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs)
> +{
> +	unsigned long sigtramp;
> +
> +#ifdef CONFIG_COMPAT
> +	if (is_compat_task()) {
> +		unsigned long sigpage = (unsigned long)current->mm->context.sigpage;
> +
> +		return regs->pc >= sigpage && regs->pc < (sigpage + PAGE_SIZE);
> +	}
> +#endif
> +	sigtramp = (unsigned long)VDSO_SYMBOL(current->mm->context.vdso, sigtramp);
> +
> +	return regs->pc == (sigtramp + 8);

Instead of hardcoding 'sigtramp + 8' you could add a new label to the 'svc #0'
instruction and use that with VDSO_SYMBOL().


Thomas


From ada.coupriediaz at arm.com  Tue Jun 30 09:37:39 2026
From: ada.coupriediaz at arm.com (Ada Couprie Diaz)
Date: Tue, 30 Jun 2026 17:37:39 +0100
Subject: [PATCH v16 01/18] seccomp: Convert __secure_computing() to return
 boolean
In-Reply-To: <20260629130616.642022-2-ruanjinjie@huawei.com>
References: <20260629130616.642022-1-ruanjinjie@huawei.com>
 <20260629130616.642022-2-ruanjinjie@huawei.com>
Message-ID: <b8f3b5cd-8d8a-4396-ba0c-011a83234dd9@arm.com>

Hi Jinjie,

On 29/06/2026 14:05, Jinjie Ruan wrote:
> The return value of __secure_computing() currently uses 0 to indicate
> that a system call should be allowed, and -1 to indicate that it should
> be blocked/killed. This 0/-1 pattern is non-intuitive for a security
> check function and makes the control flow at the call sites less readable.
>
> Furthermore, any potential future changes to these return values would
> require a high-risk, error-prone audit of all its users across different
> architectures.
>
> Sanitize this logic by converting the return type of __secure_computing()
> to a proper boolean, where 'true' explicitly means 'allow' and 'false'
> means 'fail/deny'.
>
> Update all the two dozen or so call sites across the tree to align with
> this new boolean semantic. No functional changes are intended, as the
> callers still return -1 to the lower-level assembly entry code upon
> seccomp denial.
Would it be relevant to mention that this fixes the unsound return value of
`syscall_trace_enter()` in generic entry, which motivated the patch 
initially[0] ?
> Suggested-by: Thomas Gleixner <tglx at kernel.org>
> Signed-off-by: Jinjie Ruan <ruanjinjie at huawei.com>
> ---
>   arch/alpha/kernel/ptrace.c            |  2 +-
>   arch/arm/kernel/ptrace.c              |  2 +-
>   arch/arm64/kernel/ptrace.c            |  2 +-
>   arch/csky/kernel/ptrace.c             |  2 +-
>   arch/m68k/kernel/ptrace.c             |  2 +-
>   arch/mips/kernel/ptrace.c             |  2 +-
>   arch/parisc/kernel/ptrace.c           |  2 +-
>   arch/sh/kernel/ptrace_32.c            |  2 +-
>   arch/um/kernel/skas/syscall.c         |  2 +-
>   arch/x86/entry/vsyscall/vsyscall_64.c |  2 +-
>   arch/xtensa/kernel/ptrace.c           |  3 +--
>   include/linux/entry-common.h          |  7 +++---
>   include/linux/seccomp.h               | 10 ++++----
>   kernel/seccomp.c                      | 34 +++++++++++++--------------
>   14 files changed, 36 insertions(+), 38 deletions(-)

This is missing an update to the Kconfig documentation, a possible
suggestion :

diff --git a/arch/Kconfig b/arch/Kconfig
index fa7507ac8e13..9e3d40088afb 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -637,7 +637,7 @@ config HAVE_ARCH_SECCOMP_FILTER
           - syscall_set_return_value()
           - SIGSYS siginfo_t support
           - secure_computing is called from a ptrace_event()-safe context
-         - secure_computing return value is checked and a return value of -1
+         - secure_computing return value is checked and if false
             results in the system call being skipped immediately.
           - seccomp syscall wired up
           - if !HAVE_SPARSE_SYSCALL_NR, have SECCOMP_ARCH_NATIVE,

> [...]
> diff --git a/include/linux/entry-common.h b/include/linux/entry-common.h
> index 416a3352261f..3f66320e46d3 100644
> --- a/include/linux/entry-common.h
> +++ b/include/linux/entry-common.h
> @@ -100,9 +100,8 @@ static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned l
>   
>   	/* Do seccomp after ptrace, to catch any tracer changes. */
>   	if (work & SYSCALL_WORK_SECCOMP) {
> -		ret = __secure_computing();
> -		if (ret == -1L)
> -			return ret;
> +		if (!__secure_computing())
> +			return -1L;
>   	}
>   
>   	/* Either of the above might have changed the syscall number */
> @@ -113,7 +112,7 @@ static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned l
>   
>   	syscall_enter_audit(regs, syscall);
>   
> -	return ret ? : syscall;
> +	return syscall;
>   }
> [...]

Otherwise this feels like a more appropriate change with regards to
"safeguarding against new `secure_computing()` return value" !

With the updated Kconfig :
Reviewed-by: Ada Couprie Diaz <ada.coupriediaz at arm.com>

Thanks,
Ada

[0]: 
https://lore.kernel.org/r/20260511092103.1974980-2-ruanjinjie at huawei.com



