ARM: 9179/1: uaccess: avoid alignment faults in copy_[from|to]_kernel_nofault
authorArd Biesheuvel <ardb@kernel.org>
Tue, 18 Jan 2022 12:45:09 +0000 (13:45 +0100)
committerRussell King (Oracle) <rmk+kernel@armlinux.org.uk>
Wed, 19 Jan 2022 11:10:53 +0000 (11:10 +0000)
The helpers that are used to implement copy_from_kernel_nofault() and
copy_to_kernel_nofault() cast a void* to a pointer to a wider type,
which may result in alignment faults on ARM if the compiler decides to
use double-word or multiple-word load/store instructions.

Only configurations that define CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y
are affected, given that commit 2423de2e6f4d ("ARM: 9115/1: mm/maccess:
fix unaligned copy_{from,to}_kernel_nofault") ensures that dst and src
are sufficiently aligned otherwise.

So use the unaligned accessors for accessing dst and src in cases where
they may be misaligned.

Cc: <stable@vger.kernel.org> # depends on 2423de2e6f4d
Fixes: 2df4c9a741a0 ("ARM: 9112/1: uaccess: add __{get,put}_kernel_nofault")
Reviewed-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
arch/arm/include/asm/uaccess.h

index 36fbc33..32dbfd8 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/string.h>
 #include <asm/memory.h>
 #include <asm/domain.h>
+#include <asm/unaligned.h>
 #include <asm/unified.h>
 #include <asm/compiler.h>
 
@@ -497,7 +498,10 @@ do {                                                                       \
        }                                                               \
        default: __err = __get_user_bad(); break;                       \
        }                                                               \
-       *(type *)(dst) = __val;                                         \
+       if (IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS))         \
+               put_unaligned(__val, (type *)(dst));                    \
+       else                                                            \
+               *(type *)(dst) = __val; /* aligned by caller */         \
        if (__err)                                                      \
                goto err_label;                                         \
 } while (0)
@@ -507,7 +511,9 @@ do {                                                                        \
        const type *__pk_ptr = (dst);                                   \
        unsigned long __dst = (unsigned long)__pk_ptr;                  \
        int __err = 0;                                                  \
-       type __val = *(type *)src;                                      \
+       type __val = IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) \
+                    ? get_unaligned((type *)(src))                     \
+                    : *(type *)(src);  /* aligned by caller */         \
        switch (sizeof(type)) {                                         \
        case 1: __put_user_asm_byte(__val, __dst, __err, ""); break;    \
        case 2: __put_user_asm_half(__val, __dst, __err, ""); break;    \