From: "Christophe Leroy (CS GROUP)" <chleroy@kernel.org>
To: Sayali Patil <sayalip@linux.ibm.com>,
linuxppc-dev@lists.ozlabs.org, maddy@linux.ibm.com
Cc: aboorvad@linux.ibm.com, sshegde@linux.ibm.com,
riteshh@linux.ibm.com, hbathini@linux.ibm.com,
ming.lei@redhat.com, csander@purestorage.com, czhong@redhat.com,
venkat88@linux.ibm.com
Subject: Re: [PATCH v2 1/2] powerpc: fix KUAP warning in VMX usercopy path
Date: Tue, 3 Mar 2026 15:57:35 +0100 [thread overview]
Message-ID: <65abe055-38b4-44ec-a7a0-7b4161677ead@kernel.org> (raw)
In-Reply-To: <c5138d29-e192-4a6f-b5d2-40d3a29d5bd4@linux.ibm.com>
Hi,
Le 03/03/2026 à 10:19, Sayali Patil a écrit :
>
> On 02/03/26 16:42, Christophe Leroy (CS GROUP) wrote:
>> Hi Sayali,
>>
>> Le 28/02/2026 à 14:53, Sayali Patil a écrit :
>>> On powerpc with PREEMPT_FULL or PREEMPT_LAZY and function tracing
>>> enabled,
>>> KUAP warnings can be triggered from the VMX usercopy path under memory
>>> stress workloads.
>>>
>>> KUAP requires that no subfunctions are called once userspace access has
>>> been enabled. The existing VMX copy implementation violates this
>>> requirement by invoking enter_vmx_usercopy() from the assembly path
>>> after
>>> userspace access has already been enabled. If preemption occurs
>>> in this window, the AMR state may not be preserved correctly,
>>> leading to unexpected userspace access state and resulting in
>>> KUAP warnings.
>>>
>>> Fix this by restructuring the VMX usercopy flow so that VMX selection
>>> and VMX state management are centralized in raw_copy_tofrom_user(),
>>> which is invoked by the raw_copy_{to,from,in}_user() wrappers.
>>>
>>> Introduce a usercopy_mode enum to describe the copy direction
>>> (IN, FROM, TO) and use it to derive the required KUAP permissions.
>>> Userspace access is now enabled and disabled through common helpers
>>> based on the selected mode, ensuring that the correct read/write
>>> permissions are applied consistently.
>>>
>>> The new flow is:
>>>
>>> - raw_copy_{to,from,in}_user() calls raw_copy_tofrom_user()
>>> - raw_copy_tofrom_user() decides whether to use the VMX path
>>> based on size and CPU capability
>>> - Call enter_vmx_usercopy() before enabling userspace access
>>> - Enable userspace access as per the usercopy mode
>>> and perform the VMX copy
>>> - Disable userspace access as per the usercopy mode
>>> - Call exit_vmx_usercopy()
>>> - Fall back to the base copy routine if the VMX copy faults
>>>
>>> With this change, the VMX assembly routines no longer perform VMX state
>>> management or call helper functions; they only implement the
>>> copy operations.
>>> The previous feature-section based VMX selection inside
>>> __copy_tofrom_user_power7() is removed, and a dedicated
>>> __copy_tofrom_user_power7_vmx() entry point is introduced.
>>>
>>> This ensures correct KUAP ordering, avoids subfunction calls
>>> while KUAP is unlocked, and eliminates the warnings while preserving
>>> the VMX fast path.
>>>
>>> Fixes: de78a9c42a79 ("powerpc: Add a framework for Kernel Userspace
>>> Access Protection")
>>> Reported-by: Shrikanth Hegde <sshegde@linux.ibm.com>
>>> Closes: https://lore.kernel.org/all/20260109064917.777587-2-
>>> sshegde@linux.ibm.com/
>>> Suggested-by: Christophe Leroy <chleroy@kernel.org>
>>> Co-developed-by: Aboorva Devarajan <aboorvad@linux.ibm.com>
>>> Signed-off-by: Aboorva Devarajan <aboorvad@linux.ibm.com>
>>> Signed-off-by: Sayali Patil <sayalip@linux.ibm.com>
>>> ---
>>>
>>> v1->v2
>>> - Updated as per the review comments.
>>> - Centralized VMX usercopy handling in __copy_tofrom_user_vmx() in
>>> arch/powerpc/lib/vmx-helper.c.
>>> - Introduced a usercopy_mode enum to describe the copy direction
>>> (IN, FROM, TO) and derive the required KUAP permissions, avoiding
>>> duplication across the different usercopy paths.
>>
>> I like the reduction of duplication you propose but I can't see the
>> added value of that enum, what about:
>>
>> diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/
>> include/asm/uaccess.h
>> index 63d6eb8b004e..14a3219db838 100644
>> --- a/arch/powerpc/include/asm/uaccess.h
>> +++ b/arch/powerpc/include/asm/uaccess.h
>> @@ -329,12 +329,6 @@ do { \
>> extern unsigned long __copy_tofrom_user(void __user *to,
>> const void __user *from, unsigned long size);
>>
>> -enum usercopy_mode {
>> - USERCOPY_IN,
>> - USERCOPY_FROM,
>> - USERCOPY_TO,
>> -};
>> -
>> unsigned long __copy_tofrom_user_vmx(void __user *to, const void
>> __user *from,
>> unsigned long size, enum usercopy_mode mode);
>>
>> @@ -352,48 +346,18 @@ static inline bool will_use_vmx(unsigned long n)
>> n > VMX_COPY_THRESHOLD;
>> }
>>
>> -static inline void raw_copy_allow(void __user *to, enum usercopy_mode
>> mode)
>> -{
>> - switch (mode) {
>> - case USERCOPY_IN:
>> - allow_user_access(to, KUAP_READ_WRITE);
>> - break;
>> - case USERCOPY_FROM:
>> - allow_user_access(NULL, KUAP_READ);
>> - break;
>> - case USERCOPY_TO:
>> - allow_user_access(to, KUAP_WRITE);
>> - break;
>> - }
>> -}
>> -
>> -static inline void raw_copy_prevent(enum usercopy_mode mode)
>> -{
>> - switch (mode) {
>> - case USERCOPY_IN:
>> - prevent_user_access(KUAP_READ_WRITE);
>> - break;
>> - case USERCOPY_FROM:
>> - prevent_user_access(KUAP_READ);
>> - break;
>> - case USERCOPY_TO:
>> - prevent_user_access(KUAP_WRITE);
>> - break;
>> - }
>> -}
>> -
>> static inline unsigned long raw_copy_tofrom_user(void __user *to,
>> const void __user *from, unsigned long n,
>> - enum usercopy_mode mode)
>> + unsigned long dir)
>> {
>> unsigned long ret;
>>
>> if (will_use_vmx(n))
>> return __copy_tofrom_user_vmx(to, from, n, mode);
>>
>> - raw_copy_allow(to, mode);
>> + allow_user_access(to, dir);
>> ret = __copy_tofrom_user(to, from, n);
>> - raw_copy_prevent(mode);
>> + prevent_user_access(dir);
>> return ret;
>>
>> }
>> @@ -403,22 +367,20 @@ static inline unsigned long
>> raw_copy_in_user(void __user *to, const void __user *from, unsigned
>> long n)
>> {
>> barrier_nospec();
>> - return raw_copy_tofrom_user(to, from, n, USERCOPY_IN);
>> + return raw_copy_tofrom_user(to, from, n, KUAP_READ_WRITE);
>> }
>> #endif /* __powerpc64__ */
>>
>> static inline unsigned long raw_copy_from_user(void *to,
>> const void __user *from, unsigned long n)
>> {
>> - return raw_copy_tofrom_user((__force void __user *)to, from,
>> - n, USERCOPY_FROM);
>> + return raw_copy_tofrom_user((__force void __user *)to, from, n,
>> KUAP_READ);
>> }
>>
>> static inline unsigned long
>> raw_copy_to_user(void __user *to, const void *from, unsigned long n)
>> {
>> - return raw_copy_tofrom_user(to, (__force const void __user *)from,
>> - n, USERCOPY_TO);
>> + return raw_copy_tofrom_user(to, (__force const void __user
>> *)from, n, KUAP_WRITE);
>> }
>>
>> unsigned long __arch_clear_user(void __user *addr, unsigned long size);
>> diff --git a/arch/powerpc/lib/vmx-helper.c b/arch/powerpc/lib/vmx-
>> helper.c
>> index 35080885204b..4610f7153fd9 100644
>> --- a/arch/powerpc/lib/vmx-helper.c
>> +++ b/arch/powerpc/lib/vmx-helper.c
>> @@ -11,25 +11,25 @@
>> #include <asm/switch_to.h>
>>
>> unsigned long __copy_tofrom_user_vmx(void __user *to, const void
>> __user *from,
>> - unsigned long size, enum usercopy_mode mode)
>> + unsigned long size, unsigned long dir)
>> {
>> unsigned long ret;
>>
>> if (!enter_vmx_usercopy()) {
>> - raw_copy_allow(to, mode);
>> + allow_user_access(to, dir);
>> ret = __copy_tofrom_user(to, from, size);
>> - raw_copy_prevent(mode);
>> + prevent_user_access(dir);
>> return ret;
>> }
>>
>> - raw_copy_allow(to, mode);
>> + allow_user_access(to, dir);
>> ret = __copy_tofrom_user_power7_vmx(to, from, size);
>> - raw_copy_prevent(mode);
>> + prevent_user_access(dir);
>> exit_vmx_usercopy();
>> if (unlikely(ret)) {
>> - raw_copy_allow(to, mode);
>> + allow_user_access(to, dir);
>> ret = __copy_tofrom_user_base(to, from, size);
>> - raw_copy_prevent(mode);
>> + prevent_user_access(dir);
>> }
>>
>> return ret;
>>
>>
>>
>> Christophe
>>
>>
> Hi Christophe,
> Thanks for the review.
> With the suggested change, we are hitting a compilation error.
>
> The issue is related to how KUAP enforces the access direction.
> allow_user_access() contains:
>
> BUILD_BUG_ON(!__builtin_constant_p(dir));
>
> which requires that the access direction is a compile-time constant.
> If we pass a runtime value (for example, an unsigned long), the
> __builtin_constant_p() check fails and triggers the following build error.
>
> Error:
> In function 'allow_user_access', inlined from '__copy_tofrom_user_vmx'
> at arch/powerpc/lib/vmx-helper.c:19:3:
> BUILD_BUG_ON failed: !__builtin_constant_p(dir) 706
>
>
> The previous implementation worked because allow_user_access() was
> invoked with enum
> constants (READ, WRITE, READ_WRITE), which satisfied the
> __builtin_constant_p() requirement.
> So in this case, the function must be called with a compile-time
> constant to satisfy KUAP.
>
> Please let me know if you would prefer a different approach.
>
Ah, right, I missed that. The problem should only be in vmx-helper.c
What about:
diff --git a/arch/powerpc/include/asm/uaccess.h
b/arch/powerpc/include/asm/uaccess.h
index 63d6eb8b004e..1e05f66fa647 100644
--- a/arch/powerpc/include/asm/uaccess.h
+++ b/arch/powerpc/include/asm/uaccess.h
@@ -329,14 +329,8 @@ do { \
extern unsigned long __copy_tofrom_user(void __user *to,
const void __user *from, unsigned long size);
-enum usercopy_mode {
- USERCOPY_IN,
- USERCOPY_FROM,
- USERCOPY_TO,
-};
-
unsigned long __copy_tofrom_user_vmx(void __user *to, const void
__user *from,
- unsigned long size, enum usercopy_mode mode);
+ unsigned long size, unsigned long dir);
unsigned long __copy_tofrom_user_base(void __user *to,
const void __user *from, unsigned long size);
@@ -345,55 +339,25 @@ unsigned long __copy_tofrom_user_power7_vmx(void
__user *to,
const void __user *from, unsigned long size);
-static inline bool will_use_vmx(unsigned long n)
+static __always_inline bool will_use_vmx(unsigned long n)
{
return IS_ENABLED(CONFIG_ALTIVEC) &&
cpu_has_feature(CPU_FTR_VMX_COPY) &&
n > VMX_COPY_THRESHOLD;
}
-static inline void raw_copy_allow(void __user *to, enum usercopy_mode mode)
-{
- switch (mode) {
- case USERCOPY_IN:
- allow_user_access(to, KUAP_READ_WRITE);
- break;
- case USERCOPY_FROM:
- allow_user_access(NULL, KUAP_READ);
- break;
- case USERCOPY_TO:
- allow_user_access(to, KUAP_WRITE);
- break;
- }
-}
-
-static inline void raw_copy_prevent(enum usercopy_mode mode)
-{
- switch (mode) {
- case USERCOPY_IN:
- prevent_user_access(KUAP_READ_WRITE);
- break;
- case USERCOPY_FROM:
- prevent_user_access(KUAP_READ);
- break;
- case USERCOPY_TO:
- prevent_user_access(KUAP_WRITE);
- break;
- }
-}
-
-static inline unsigned long raw_copy_tofrom_user(void __user *to,
+static __always_inline unsigned long raw_copy_tofrom_user(void __user *to,
const void __user *from, unsigned long n,
- enum usercopy_mode mode)
+ unsigned long dir)
{
unsigned long ret;
if (will_use_vmx(n))
- return __copy_tofrom_user_vmx(to, from, n, mode);
+ return __copy_tofrom_user_vmx(to, from, n, dir);
- raw_copy_allow(to, mode);
+ allow_user_access(to, dir);
ret = __copy_tofrom_user(to, from, n);
- raw_copy_prevent(mode);
+ prevent_user_access(dir);
return ret;
}
@@ -403,22 +367,20 @@ static inline unsigned long
raw_copy_in_user(void __user *to, const void __user *from, unsigned
long n)
{
barrier_nospec();
- return raw_copy_tofrom_user(to, from, n, USERCOPY_IN);
+ return raw_copy_tofrom_user(to, from, n, KUAP_READ_WRITE);
}
#endif /* __powerpc64__ */
static inline unsigned long raw_copy_from_user(void *to,
const void __user *from, unsigned long n)
{
- return raw_copy_tofrom_user((__force void __user *)to, from,
- n, USERCOPY_FROM);
+ return raw_copy_tofrom_user((__force void __user *)to, from, n,
KUAP_READ);
}
static inline unsigned long
raw_copy_to_user(void __user *to, const void *from, unsigned long n)
{
- return raw_copy_tofrom_user(to, (__force const void __user *)from,
- n, USERCOPY_TO);
+ return raw_copy_tofrom_user(to, (__force const void __user *)from, n,
KUAP_WRITE);
}
unsigned long __arch_clear_user(void __user *addr, unsigned long size);
diff --git a/arch/powerpc/lib/vmx-helper.c b/arch/powerpc/lib/vmx-helper.c
index 35080885204b..5fc6b2997158 100644
--- a/arch/powerpc/lib/vmx-helper.c
+++ b/arch/powerpc/lib/vmx-helper.c
@@ -10,26 +10,49 @@
#include <linux/hardirq.h>
#include <asm/switch_to.h>
+static void __allow_user_access(void __user *to, unsigned long dir)
+{
+ if (dir == KUAP_READ)
+ allow_user_access(to, KUAP_READ);
+ else if (dir == KUAP_WRITE)
+ allow_user_access(to, KUAP_WRITE);
+ else if (dir == KUAP_READ_WRITE)
+ allow_user_access(to, KUAP_READ_WRITE);
+}
+
+static void __prevent_user_access(unsigned long dir)
+{
+ if (dir == KUAP_READ)
+ prevent_user_access(KUAP_READ);
+ else if (dir == KUAP_WRITE)
+ prevent_user_access(KUAP_WRITE);
+ else if (dir == KUAP_READ_WRITE)
+ prevent_user_access(KUAP_READ_WRITE);
+}
+
unsigned long __copy_tofrom_user_vmx(void __user *to, const void
__user *from,
- unsigned long size, enum usercopy_mode mode)
+ unsigned long size, unsigned long dir)
{
unsigned long ret;
+ if (WARN_ON_ONCE(!dir || dir > KUAP_READ_WRITE))
+ return size;
+
if (!enter_vmx_usercopy()) {
- raw_copy_allow(to, mode);
+ __allow_user_access(to, dir);
ret = __copy_tofrom_user(to, from, size);
- raw_copy_prevent(mode);
+ __prevent_user_access(dir);
return ret;
}
- raw_copy_allow(to, mode);
+ __allow_user_access(to, dir);
ret = __copy_tofrom_user_power7_vmx(to, from, size);
- raw_copy_prevent(mode);
+ __prevent_user_access(dir);
exit_vmx_usercopy();
if (unlikely(ret)) {
- raw_copy_allow(to, mode);
+ __allow_user_access(to, dir);
ret = __copy_tofrom_user_base(to, from, size);
- raw_copy_prevent(mode);
+ __prevent_user_access(dir);
}
return ret;
Christophe
next prev parent reply other threads:[~2026-03-03 14:57 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-28 13:53 [PATCH v2 1/2] powerpc: fix KUAP warning in VMX usercopy path Sayali Patil
2026-02-28 13:53 ` [PATCH v2 2/2] powerpc/selftests/copyloops: extend selftest to exercise __copy_tofrom_user_power7_vmx Sayali Patil
2026-03-02 11:12 ` [PATCH v2 1/2] powerpc: fix KUAP warning in VMX usercopy path Christophe Leroy (CS GROUP)
2026-03-03 9:19 ` Sayali Patil
2026-03-03 14:57 ` Christophe Leroy (CS GROUP) [this message]
2026-03-03 15:10 ` Christophe Leroy (CS GROUP)
2026-03-03 15:17 ` Christophe Leroy (CS GROUP)
2026-03-04 5:40 ` Sayali Patil
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=65abe055-38b4-44ec-a7a0-7b4161677ead@kernel.org \
--to=chleroy@kernel.org \
--cc=aboorvad@linux.ibm.com \
--cc=csander@purestorage.com \
--cc=czhong@redhat.com \
--cc=hbathini@linux.ibm.com \
--cc=linuxppc-dev@lists.ozlabs.org \
--cc=maddy@linux.ibm.com \
--cc=ming.lei@redhat.com \
--cc=riteshh@linux.ibm.com \
--cc=sayalip@linux.ibm.com \
--cc=sshegde@linux.ibm.com \
--cc=venkat88@linux.ibm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.