diff mbox series

parisc: Fix user access miscompilation

Message ID YgQGzle/mBRK9lBc@p100 (mailing list archive)
State Superseded
Headers show
Series parisc: Fix user access miscompilation | expand

Commit Message

Helge Deller Feb. 9, 2022, 6:24 p.m. UTC
After commit 4b9d2a731c3d ("parisc: Switch user access functions
to signal errors in r29 instead of r8") bash suddenly started
to report those warnings after login:

-bash: cannot set terminal process group (-1): Bad file descriptor
-bash: no job control in this shell

It turned out, that another function call inside a put_user(), e.g.:
  put_user(vt_do_kdgkbmode(console), (int __user *)arg);
clobbered the error register (r29) and thus failed.
Avoid this miscompilation by first calculate the value in
__put_user() before calling __put_user_internal() to actually
write the value to user space.

Additionally, prefer the "+" constraint on pu_err and gu_err registers to tell
the compiler that those operands are both read and written by the assembly
instruction.

Signed-off-by: Helge Deller <deller@gmx.de>
Fixes: 4b9d2a731c3d ("parisc: Switch user access functions to signal errors in r29 instead of r8")

Comments

John David Anglin Feb. 10, 2022, 10:26 a.m. UTC | #1
On 2022-02-09 1:24 p.m., Helge Deller wrote:
> After commit 4b9d2a731c3d ("parisc: Switch user access functions
> to signal errors in r29 instead of r8") bash suddenly started
> to report those warnings after login:
>
> -bash: cannot set terminal process group (-1): Bad file descriptor
> -bash: no job control in this shell
>
> It turned out, that another function call inside a put_user(), e.g.:
>    put_user(vt_do_kdgkbmode(console), (int __user *)arg);
> clobbered the error register (r29) and thus failed.
> Avoid this miscompilation by first calculate the value in
> __put_user() before calling __put_user_internal() to actually
> write the value to user space.
Couldn't the same issue occur with ptr argument in these macros?
>
> Additionally, prefer the "+" constraint on pu_err and gu_err registers to tell
> the compiler that those operands are both read and written by the assembly
> instruction.
>
> Signed-off-by: Helge Deller <deller@gmx.de>
> Fixes: 4b9d2a731c3d ("parisc: Switch user access functions to signal errors in r29 instead of r8")
>
> diff --git a/arch/parisc/include/asm/uaccess.h b/arch/parisc/include/asm/uaccess.h
> index ebf8a845b017..68f5c1eaaa6f 100644
> --- a/arch/parisc/include/asm/uaccess.h
> +++ b/arch/parisc/include/asm/uaccess.h
> @@ -89,7 +89,7 @@ struct exception_table_entry {
>   	__asm__("1: " ldx " 0(" sr "%2),%0\n"		\
>   		"9:\n"					\
>   		ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b)	\
> -		: "=r"(__gu_val), "=r"(__gu_err)        \
> +		: "=&r"(__gu_val), "+r"(__gu_err)        \
I don't believe the early clobber is needed on __gnu_val.
>   		: "r"(ptr), "1"(__gu_err));		\
>   							\
>   	(val) = (__force __typeof__(*(ptr))) __gu_val;	\
> @@ -123,7 +123,7 @@ struct exception_table_entry {
>   		"9:\n"					\
>   		ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b)	\
>   		ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 9b)	\
> -		: "=&r"(__gu_tmp.l), "=r"(__gu_err)	\
> +		: "=&r"(__gu_tmp.l), "+r"(__gu_err)	\
>   		: "r"(ptr), "1"(__gu_err));		\
>   							\
>   	(val) = __gu_tmp.t;				\
> @@ -132,10 +132,9 @@ struct exception_table_entry {
>   #endif /* !defined(CONFIG_64BIT) */
>
>
> -#define __put_user_internal(sr, x, ptr)				\
> +#define __put_user_internal(sr, __x, ptr)			\
>   ({								\
>   	ASM_EXCEPTIONTABLE_VAR(__pu_err);		      	\
> -        __typeof__(*(ptr)) __x = (__typeof__(*(ptr)))(x);	\
>   								\
>   	switch (sizeof(*(ptr))) {				\
>   	case 1: __put_user_asm(sr, "stb", __x, ptr); break;	\
> @@ -150,7 +149,9 @@ struct exception_table_entry {
>
>   #define __put_user(x, ptr)					\
>   ({								\
> -	__put_user_internal("%%sr3,", x, ptr);			\
> +	 __typeof__(*(ptr)) __x = (__typeof__(*(ptr)))(x);	\
Whitespace?
> +								\
> +	__put_user_internal("%%sr3,", __x, ptr);		\
>   })
>
>   #define __put_kernel_nofault(dst, src, type, err_label)		\
> @@ -180,7 +181,7 @@ struct exception_table_entry {
>   		"1: " stx " %2,0(" sr "%1)\n"			\
>   		"9:\n"						\
>   		ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b)		\
> -		: "=r"(__pu_err)				\
> +		: "+r"(__pu_err)				\
>   		: "r"(ptr), "r"(x), "0"(__pu_err))
>
>
> @@ -193,7 +194,7 @@ struct exception_table_entry {
>   		"9:\n"						\
>   		ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b)		\
>   		ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 9b)		\
> -		: "=r"(__pu_err)				\
> +		: "+r"(__pu_err)				\
>   		: "r"(ptr), "r"(__val), "0"(__pu_err));		\
>   } while (0)
>

Dave
Helge Deller Feb. 10, 2022, 10:56 a.m. UTC | #2
On 2/10/22 11:26, John David Anglin wrote:
> On 2022-02-09 1:24 p.m., Helge Deller wrote:
>> After commit 4b9d2a731c3d ("parisc: Switch user access functions
>> to signal errors in r29 instead of r8") bash suddenly started
>> to report those warnings after login:
>>
>> -bash: cannot set terminal process group (-1): Bad file descriptor
>> -bash: no job control in this shell
>>
>> It turned out, that another function call inside a put_user(), e.g.:
>>    put_user(vt_do_kdgkbmode(console), (int __user *)arg);
>> clobbered the error register (r29) and thus failed.
>> Avoid this miscompilation by first calculate the value in
>> __put_user() before calling __put_user_internal() to actually
>> write the value to user space.
> Couldn't the same issue occur with ptr argument in these macros?

Theoretically, yes. Haven't seen it though.

>> Additionally, prefer the "+" constraint on pu_err and gu_err registers to tell
>> the compiler that those operands are both read and written by the assembly
>> instruction.
>>
>> Signed-off-by: Helge Deller <deller@gmx.de>
>> Fixes: 4b9d2a731c3d ("parisc: Switch user access functions to signal errors in r29 instead of r8")
>>
>> diff --git a/arch/parisc/include/asm/uaccess.h b/arch/parisc/include/asm/uaccess.h
>> index ebf8a845b017..68f5c1eaaa6f 100644
>> --- a/arch/parisc/include/asm/uaccess.h
>> +++ b/arch/parisc/include/asm/uaccess.h
>> @@ -89,7 +89,7 @@ struct exception_table_entry {
>>       __asm__("1: " ldx " 0(" sr "%2),%0\n"        \
>>           "9:\n"                    \
>>           ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b)    \
>> -        : "=r"(__gu_val), "=r"(__gu_err)        \
>> +        : "=&r"(__gu_val), "+r"(__gu_err)        \
> I don't believe the early clobber is needed on __gnu_val.

Right.

>>           : "r"(ptr), "1"(__gu_err));        \
>>                               \
>>       (val) = (__force __typeof__(*(ptr))) __gu_val;    \
>> @@ -123,7 +123,7 @@ struct exception_table_entry {
>>           "9:\n"                    \
>>           ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b)    \
>>           ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 9b)    \
>> -        : "=&r"(__gu_tmp.l), "=r"(__gu_err)    \
>> +        : "=&r"(__gu_tmp.l), "+r"(__gu_err)    \
>>           : "r"(ptr), "1"(__gu_err));        \
>>                               \
>>       (val) = __gu_tmp.t;                \
>> @@ -132,10 +132,9 @@ struct exception_table_entry {
>>   #endif /* !defined(CONFIG_64BIT) */
>>
>>
>> -#define __put_user_internal(sr, x, ptr)                \
>> +#define __put_user_internal(sr, __x, ptr)            \
>>   ({                                \
>>       ASM_EXCEPTIONTABLE_VAR(__pu_err);                  \
>> -        __typeof__(*(ptr)) __x = (__typeof__(*(ptr)))(x);    \
>>                                   \
>>       switch (sizeof(*(ptr))) {                \
>>       case 1: __put_user_asm(sr, "stb", __x, ptr); break;    \
>> @@ -150,7 +149,9 @@ struct exception_table_entry {
>>
>>   #define __put_user(x, ptr)                    \
>>   ({                                \
>> -    __put_user_internal("%%sr3,", x, ptr);            \
>> +     __typeof__(*(ptr)) __x = (__typeof__(*(ptr)))(x);    \
> Whitespace?

Will fix.

>> +                                \
>> +    __put_user_internal("%%sr3,", __x, ptr);        \
>>   })
>>
>>   #define __put_kernel_nofault(dst, src, type, err_label)        \
>> @@ -180,7 +181,7 @@ struct exception_table_entry {
>>           "1: " stx " %2,0(" sr "%1)\n"            \
>>           "9:\n"                        \
>>           ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b)        \
>> -        : "=r"(__pu_err)                \
>> +        : "+r"(__pu_err)                \
>>           : "r"(ptr), "r"(x), "0"(__pu_err))
>>
>>
>> @@ -193,7 +194,7 @@ struct exception_table_entry {
>>           "9:\n"                        \
>>           ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b)        \
>>           ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 9b)        \
>> -        : "=r"(__pu_err)                \
>> +        : "+r"(__pu_err)                \
>>           : "r"(ptr), "r"(__val), "0"(__pu_err));        \
>>   } while (0)

Overall, this current patch turned out to not cover all cases.
I think it's too complicated to really try prevent the compiler to
optimize it here, so I'll send out a new patch shortly which
simply initializes __gu_err and __pu_err in the assembly statement itself.
My other codings to avoid that would increase the code a lot,
and I'm not sure if it's even then possible to avoid the failure always.

Helge
diff mbox series

Patch

diff --git a/arch/parisc/include/asm/uaccess.h b/arch/parisc/include/asm/uaccess.h
index ebf8a845b017..68f5c1eaaa6f 100644
--- a/arch/parisc/include/asm/uaccess.h
+++ b/arch/parisc/include/asm/uaccess.h
@@ -89,7 +89,7 @@  struct exception_table_entry {
 	__asm__("1: " ldx " 0(" sr "%2),%0\n"		\
 		"9:\n"					\
 		ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b)	\
-		: "=r"(__gu_val), "=r"(__gu_err)        \
+		: "=&r"(__gu_val), "+r"(__gu_err)        \
 		: "r"(ptr), "1"(__gu_err));		\
 							\
 	(val) = (__force __typeof__(*(ptr))) __gu_val;	\
@@ -123,7 +123,7 @@  struct exception_table_entry {
 		"9:\n"					\
 		ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b)	\
 		ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 9b)	\
-		: "=&r"(__gu_tmp.l), "=r"(__gu_err)	\
+		: "=&r"(__gu_tmp.l), "+r"(__gu_err)	\
 		: "r"(ptr), "1"(__gu_err));		\
 							\
 	(val) = __gu_tmp.t;				\
@@ -132,10 +132,9 @@  struct exception_table_entry {
 #endif /* !defined(CONFIG_64BIT) */


-#define __put_user_internal(sr, x, ptr)				\
+#define __put_user_internal(sr, __x, ptr)			\
 ({								\
 	ASM_EXCEPTIONTABLE_VAR(__pu_err);		      	\
-        __typeof__(*(ptr)) __x = (__typeof__(*(ptr)))(x);	\
 								\
 	switch (sizeof(*(ptr))) {				\
 	case 1: __put_user_asm(sr, "stb", __x, ptr); break;	\
@@ -150,7 +149,9 @@  struct exception_table_entry {

 #define __put_user(x, ptr)					\
 ({								\
-	__put_user_internal("%%sr3,", x, ptr);			\
+	 __typeof__(*(ptr)) __x = (__typeof__(*(ptr)))(x);	\
+								\
+	__put_user_internal("%%sr3,", __x, ptr);		\
 })

 #define __put_kernel_nofault(dst, src, type, err_label)		\
@@ -180,7 +181,7 @@  struct exception_table_entry {
 		"1: " stx " %2,0(" sr "%1)\n"			\
 		"9:\n"						\
 		ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b)		\
-		: "=r"(__pu_err)				\
+		: "+r"(__pu_err)				\
 		: "r"(ptr), "r"(x), "0"(__pu_err))


@@ -193,7 +194,7 @@  struct exception_table_entry {
 		"9:\n"						\
 		ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b)		\
 		ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 9b)		\
-		: "=r"(__pu_err)				\
+		: "+r"(__pu_err)				\
 		: "r"(ptr), "r"(__val), "0"(__pu_err));		\
 } while (0)