2024-06-10 21:04:41

by Kees Cook

[permalink] [raw]
Subject: [PATCH] x86/uaccess: Fix missed zeroing of ia32 u64 get_user() range checking

When reworking the range checking for get_user(), the get_user_8() case
on 32-bit wasn't zeroing the high register. (The jump to bad_get_user_8
was accidentally dropped.) Restore the correct error handling
destination (and rename the jump to using the expected ".L" prefix).

While here, switch to using a named argument ("size") for the call
template ("%c4" to "%c[size]") as already used in the other call
templates in this file.

Found after moving the usercopy selftests to KUnit:

# usercopy_test_invalid: EXPECTATION FAILED at
lib/usercopy_kunit.c:278
Expected val_u64 == 0, but
val_u64 == -60129542144 (0xfffffff200000000)

Reported-by: David Gow <[email protected]>
Closes: https://lore.kernel.org/all/CABVgOSn=tb=Lj9SxHuT4_9MTjjKVxsq-ikdXC4kGHO4CfKVmGQ@mail.gmail.com
Fixes: b19b74bc99b1 ("x86/mm: Rework address range check in get_user() and put_user()")
Signed-off-by: Kees Cook <[email protected]>
---
Cc: Thomas Gleixner <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Borislav Petkov <[email protected]>
Cc: Dave Hansen <[email protected]>
Cc: [email protected]
Cc: "H. Peter Anvin" <[email protected]>
Cc: Sean Christopherson <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Arnd Bergmann <[email protected]>
Cc: "Kirill A. Shutemov" <[email protected]>
Cc: Qiuxu Zhuo <[email protected]>
Cc: Nadav Amit <[email protected]>
Cc: Masahiro Yamada <[email protected]>
---
arch/x86/include/asm/uaccess.h | 4 ++--
arch/x86/lib/getuser.S | 6 +++++-
2 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h
index 0f9bab92a43d..3a7755c1a441 100644
--- a/arch/x86/include/asm/uaccess.h
+++ b/arch/x86/include/asm/uaccess.h
@@ -78,10 +78,10 @@ extern int __get_user_bad(void);
int __ret_gu; \
register __inttype(*(ptr)) __val_gu asm("%"_ASM_DX); \
__chk_user_ptr(ptr); \
- asm volatile("call __" #fn "_%c4" \
+ asm volatile("call __" #fn "_%c[size]" \
: "=a" (__ret_gu), "=r" (__val_gu), \
ASM_CALL_CONSTRAINT \
- : "0" (ptr), "i" (sizeof(*(ptr)))); \
+ : "0" (ptr), [size] "i" (sizeof(*(ptr)))); \
instrument_get_user(__val_gu); \
(x) = (__force __typeof__(*(ptr))) __val_gu; \
__builtin_expect(__ret_gu, 0); \
diff --git a/arch/x86/lib/getuser.S b/arch/x86/lib/getuser.S
index 10d5ed8b5990..a1cb3a4e6742 100644
--- a/arch/x86/lib/getuser.S
+++ b/arch/x86/lib/getuser.S
@@ -44,7 +44,11 @@
or %rdx, %rax
.else
cmp $TASK_SIZE_MAX-\size+1, %eax
+.if \size != 8
jae .Lbad_get_user
+.else
+ jae .Lbad_get_user_8
+.endif
sbb %edx, %edx /* array_index_mask_nospec() */
and %edx, %eax
.endif
@@ -154,7 +158,7 @@ SYM_CODE_END(__get_user_handle_exception)
#ifdef CONFIG_X86_32
SYM_CODE_START_LOCAL(__get_user_8_handle_exception)
ASM_CLAC
-bad_get_user_8:
+.Lbad_get_user_8:
xor %edx,%edx
xor %ecx,%ecx
mov $(-EFAULT),%_ASM_AX
--
2.34.1



2024-06-11 06:14:31

by David Gow

[permalink] [raw]
Subject: Re: [PATCH] x86/uaccess: Fix missed zeroing of ia32 u64 get_user() range checking

On Tue, 11 Jun 2024 at 05:02, Kees Cook <[email protected]> wrote:
>
> When reworking the range checking for get_user(), the get_user_8() case
> on 32-bit wasn't zeroing the high register. (The jump to bad_get_user_8
> was accidentally dropped.) Restore the correct error handling
> destination (and rename the jump to using the expected ".L" prefix).
>
> While here, switch to using a named argument ("size") for the call
> template ("%c4" to "%c[size]") as already used in the other call
> templates in this file.
>
> Found after moving the usercopy selftests to KUnit:
>
> # usercopy_test_invalid: EXPECTATION FAILED at
> lib/usercopy_kunit.c:278
> Expected val_u64 == 0, but
> val_u64 == -60129542144 (0xfffffff200000000)
>
> Reported-by: David Gow <[email protected]>
> Closes: https://lore.kernel.org/all/CABVgOSn=tb=Lj9SxHuT4_9MTjjKVxsq-ikdXC4kGHO4CfKVmGQ@mail.gmail.com
> Fixes: b19b74bc99b1 ("x86/mm: Rework address range check in get_user() and put_user()")
> Signed-off-by: Kees Cook <[email protected]>
> ---
> Cc: Thomas Gleixner <[email protected]>
> Cc: Ingo Molnar <[email protected]>
> Cc: Borislav Petkov <[email protected]>
> Cc: Dave Hansen <[email protected]>
> Cc: [email protected]
> Cc: "H. Peter Anvin" <[email protected]>
> Cc: Sean Christopherson <[email protected]>
> Cc: Peter Zijlstra <[email protected]>
> Cc: Arnd Bergmann <[email protected]>
> Cc: "Kirill A. Shutemov" <[email protected]>
> Cc: Qiuxu Zhuo <[email protected]>
> Cc: Nadav Amit <[email protected]>
> Cc: Masahiro Yamada <[email protected]>
> ---

Thanks: this fixes it here, both under qemu and on real hardware.

Tested-by: David Gow <[email protected]>

-- David


Attachments:
smime.p7s (3.92 kB)
S/MIME Cryptographic Signature

2024-06-11 06:21:16

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH] x86/uaccess: Fix missed zeroing of ia32 u64 get_user() range checking

On Mon, Jun 10, 2024 at 02:02:27PM -0700, Kees Cook wrote:
> When reworking the range checking for get_user(), the get_user_8() case
> on 32-bit wasn't zeroing the high register. (The jump to bad_get_user_8
> was accidentally dropped.) Restore the correct error handling
> destination (and rename the jump to using the expected ".L" prefix).
>
> While here, switch to using a named argument ("size") for the call
> template ("%c4" to "%c[size]") as already used in the other call
> templates in this file.
>
> Found after moving the usercopy selftests to KUnit:
>
> # usercopy_test_invalid: EXPECTATION FAILED at
> lib/usercopy_kunit.c:278
> Expected val_u64 == 0, but
> val_u64 == -60129542144 (0xfffffff200000000)
>
> Reported-by: David Gow <[email protected]>
> Closes: https://lore.kernel.org/all/CABVgOSn=tb=Lj9SxHuT4_9MTjjKVxsq-ikdXC4kGHO4CfKVmGQ@mail.gmail.com
> Fixes: b19b74bc99b1 ("x86/mm: Rework address range check in get_user() and put_user()")
> Signed-off-by: Kees Cook <[email protected]>
> ---
> Cc: Thomas Gleixner <[email protected]>
> Cc: Ingo Molnar <[email protected]>
> Cc: Borislav Petkov <[email protected]>
> Cc: Dave Hansen <[email protected]>
> Cc: [email protected]
> Cc: "H. Peter Anvin" <[email protected]>
> Cc: Sean Christopherson <[email protected]>
> Cc: Peter Zijlstra <[email protected]>
> Cc: Arnd Bergmann <[email protected]>
> Cc: "Kirill A. Shutemov" <[email protected]>
> Cc: Qiuxu Zhuo <[email protected]>
> Cc: Nadav Amit <[email protected]>
> Cc: Masahiro Yamada <[email protected]>
> ---
> arch/x86/include/asm/uaccess.h | 4 ++--
> arch/x86/lib/getuser.S | 6 +++++-
> 2 files changed, 7 insertions(+), 3 deletions(-)
>

Hi,

This is the friendly patch-bot of Greg Kroah-Hartman. You have sent him
a patch that has triggered this response. He used to manually respond
to these common problems, but in order to save his sanity (he kept
writing the same thing over and over, yet to different people), I was
created. Hopefully you will not take offence and will fix the problem
in your patch and resubmit it so that it can be accepted into the Linux
kernel tree.

You are receiving this message because of the following common error(s)
as indicated below:

- You have marked a patch with a "Fixes:" tag for a commit that is in an
older released kernel, yet you do not have a cc: stable line in the
signed-off-by area at all, which means that the patch will not be
applied to any older kernel releases. To properly fix this, please
follow the documented rules in the
Documentation/process/stable-kernel-rules.rst file for how to resolve
this.

If you wish to discuss this problem further, or you have questions about
how to resolve this issue, please feel free to respond to this email and
Greg will reply once he has dug out from the pending patches received
from other developers.

thanks,

greg k-h's patch email bot

2024-06-11 07:00:18

by Qiuxu Zhuo

[permalink] [raw]
Subject: RE: [PATCH] x86/uaccess: Fix missed zeroing of ia32 u64 get_user() range checking

> From: Kees Cook <[email protected]>
> [...]
> Subject: [PATCH] x86/uaccess: Fix missed zeroing of ia32 u64 get_user() range
> checking
>
> When reworking the range checking for get_user(), the get_user_8() case on
> 32-bit wasn't zeroing the high register. (The jump to bad_get_user_8 was
> accidentally dropped.) Restore the correct error handling destination (and
> rename the jump to using the expected ".L" prefix).
>
> While here, switch to using a named argument ("size") for the call template
> ("%c4" to "%c[size]") as already used in the other call templates in this file.
>
> Found after moving the usercopy selftests to KUnit:
>
> # usercopy_test_invalid: EXPECTATION FAILED at
> lib/usercopy_kunit.c:278
> Expected val_u64 == 0, but
> val_u64 == -60129542144 (0xfffffff200000000)
>
> Reported-by: David Gow <[email protected]>
> Closes: https://lore.kernel.org/all/CABVgOSn=tb=Lj9SxHuT4_9MTjjKVxsq-
> [email protected]
> Fixes: b19b74bc99b1 ("x86/mm: Rework address range check in get_user()
> and put_user()")
> Signed-off-by: Kees Cook <[email protected]>
> [...]
> arch/x86/include/asm/uaccess.h | 4 ++--
> arch/x86/lib/getuser.S | 6 +++++-
> 2 files changed, 7 insertions(+), 3 deletions(-)
>
> diff --git a/arch/x86/include/asm/uaccess.h
> b/arch/x86/include/asm/uaccess.h index 0f9bab92a43d..3a7755c1a441
> 100644
> --- a/arch/x86/include/asm/uaccess.h
> +++ b/arch/x86/include/asm/uaccess.h
> @@ -78,10 +78,10 @@ extern int __get_user_bad(void);
> int __ret_gu; \
> register __inttype(*(ptr)) __val_gu asm("%"_ASM_DX); \
> __chk_user_ptr(ptr); \
> - asm volatile("call __" #fn "_%c4" \
> + asm volatile("call __" #fn "_%c[size]" \
> : "=a" (__ret_gu), "=r" (__val_gu), \
> ASM_CALL_CONSTRAINT
> \
> - : "0" (ptr), "i" (sizeof(*(ptr)))); \
> + : "0" (ptr), [size] "i" (sizeof(*(ptr)))); \
> instrument_get_user(__val_gu);
> \
> (x) = (__force __typeof__(*(ptr))) __val_gu; \
> __builtin_expect(__ret_gu, 0); \
> diff --git a/arch/x86/lib/getuser.S b/arch/x86/lib/getuser.S index
> 10d5ed8b5990..a1cb3a4e6742 100644
> --- a/arch/x86/lib/getuser.S
> +++ b/arch/x86/lib/getuser.S
> @@ -44,7 +44,11 @@
> or %rdx, %rax
> .else
> cmp $TASK_SIZE_MAX-\size+1, %eax
> +.if \size != 8
> jae .Lbad_get_user
> +.else
> + jae .Lbad_get_user_8
> +.endif
> sbb %edx, %edx /* array_index_mask_nospec() */
> and %edx, %eax
> .endif
> @@ -154,7 +158,7 @@ SYM_CODE_END(__get_user_handle_exception)
> #ifdef CONFIG_X86_32
> SYM_CODE_START_LOCAL(__get_user_8_handle_exception)
> ASM_CLAC
> -bad_get_user_8:
> +.Lbad_get_user_8:
> xor %edx,%edx
> xor %ecx,%ecx
> mov $(-EFAULT),%_ASM_AX
> --
> 2.34.1

LGTM. Thanks!

Reviewed-by: Qiuxu Zhuo <[email protected]>

2024-06-11 10:29:57

by Kirill A. Shutemov

[permalink] [raw]
Subject: Re: [PATCH] x86/uaccess: Fix missed zeroing of ia32 u64 get_user() range checking

On Mon, Jun 10, 2024 at 02:02:27PM -0700, Kees Cook wrote:
> When reworking the range checking for get_user(), the get_user_8() case
> on 32-bit wasn't zeroing the high register. (The jump to bad_get_user_8
> was accidentally dropped.) Restore the correct error handling
> destination (and rename the jump to using the expected ".L" prefix).

Oh. My bad.

Reviewed-by: Kirill A. Shutemov <[email protected]>

--
Kiryl Shutsemau / Kirill A. Shutemov