Commit c014953d authored by Russell King's avatar Russell King

ARM: fix uaccess_with_memcpy() with SW_DOMAIN_PAN

The uaccess_with_memcpy() code is currently incompatible with the SW
PAN code: it takes locks within the region that we've changed the DACR,
potentially sleeping as a result.  As we do not save and restore the
DACR across co-operative sleep events, can lead to an incorrect DACR
value later in this code path.
Reported-by: default avatarPeter Rosin <peda@axentia.se>
Tested-by: default avatarPeter Rosin <peda@axentia.se>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 77f1b959
...@@ -510,10 +510,14 @@ __copy_to_user_std(void __user *to, const void *from, unsigned long n); ...@@ -510,10 +510,14 @@ __copy_to_user_std(void __user *to, const void *from, unsigned long n);
static inline unsigned long __must_check static inline unsigned long __must_check
__copy_to_user(void __user *to, const void *from, unsigned long n) __copy_to_user(void __user *to, const void *from, unsigned long n)
{ {
#ifndef CONFIG_UACCESS_WITH_MEMCPY
unsigned int __ua_flags = uaccess_save_and_enable(); unsigned int __ua_flags = uaccess_save_and_enable();
n = arm_copy_to_user(to, from, n); n = arm_copy_to_user(to, from, n);
uaccess_restore(__ua_flags); uaccess_restore(__ua_flags);
return n; return n;
#else
return arm_copy_to_user(to, from, n);
#endif
} }
extern unsigned long __must_check extern unsigned long __must_check
......
...@@ -88,6 +88,7 @@ pin_page_for_write(const void __user *_addr, pte_t **ptep, spinlock_t **ptlp) ...@@ -88,6 +88,7 @@ pin_page_for_write(const void __user *_addr, pte_t **ptep, spinlock_t **ptlp)
static unsigned long noinline static unsigned long noinline
__copy_to_user_memcpy(void __user *to, const void *from, unsigned long n) __copy_to_user_memcpy(void __user *to, const void *from, unsigned long n)
{ {
unsigned long ua_flags;
int atomic; int atomic;
if (unlikely(segment_eq(get_fs(), KERNEL_DS))) { if (unlikely(segment_eq(get_fs(), KERNEL_DS))) {
...@@ -118,7 +119,9 @@ __copy_to_user_memcpy(void __user *to, const void *from, unsigned long n) ...@@ -118,7 +119,9 @@ __copy_to_user_memcpy(void __user *to, const void *from, unsigned long n)
if (tocopy > n) if (tocopy > n)
tocopy = n; tocopy = n;
ua_flags = uaccess_save_and_enable();
memcpy((void *)to, from, tocopy); memcpy((void *)to, from, tocopy);
uaccess_restore(ua_flags);
to += tocopy; to += tocopy;
from += tocopy; from += tocopy;
n -= tocopy; n -= tocopy;
...@@ -145,14 +148,21 @@ arm_copy_to_user(void __user *to, const void *from, unsigned long n) ...@@ -145,14 +148,21 @@ arm_copy_to_user(void __user *to, const void *from, unsigned long n)
* With frame pointer disabled, tail call optimization kicks in * With frame pointer disabled, tail call optimization kicks in
* as well making this test almost invisible. * as well making this test almost invisible.
*/ */
if (n < 64) if (n < 64) {
return __copy_to_user_std(to, from, n); unsigned long ua_flags = uaccess_save_and_enable();
return __copy_to_user_memcpy(to, from, n); n = __copy_to_user_std(to, from, n);
uaccess_restore(ua_flags);
} else {
n = __copy_to_user_memcpy(to, from, n);
}
return n;
} }
static unsigned long noinline static unsigned long noinline
__clear_user_memset(void __user *addr, unsigned long n) __clear_user_memset(void __user *addr, unsigned long n)
{ {
unsigned long ua_flags;
if (unlikely(segment_eq(get_fs(), KERNEL_DS))) { if (unlikely(segment_eq(get_fs(), KERNEL_DS))) {
memset((void *)addr, 0, n); memset((void *)addr, 0, n);
return 0; return 0;
...@@ -175,7 +185,9 @@ __clear_user_memset(void __user *addr, unsigned long n) ...@@ -175,7 +185,9 @@ __clear_user_memset(void __user *addr, unsigned long n)
if (tocopy > n) if (tocopy > n)
tocopy = n; tocopy = n;
ua_flags = uaccess_save_and_enable();
memset((void *)addr, 0, tocopy); memset((void *)addr, 0, tocopy);
uaccess_restore(ua_flags);
addr += tocopy; addr += tocopy;
n -= tocopy; n -= tocopy;
...@@ -193,9 +205,14 @@ __clear_user_memset(void __user *addr, unsigned long n) ...@@ -193,9 +205,14 @@ __clear_user_memset(void __user *addr, unsigned long n)
unsigned long arm_clear_user(void __user *addr, unsigned long n) unsigned long arm_clear_user(void __user *addr, unsigned long n)
{ {
/* See rational for this in __copy_to_user() above. */ /* See rational for this in __copy_to_user() above. */
if (n < 64) if (n < 64) {
return __clear_user_std(addr, n); unsigned long ua_flags = uaccess_save_and_enable();
return __clear_user_memset(addr, n); n = __clear_user_std(addr, n);
uaccess_restore(ua_flags);
} else {
n = __clear_user_memset(addr, n);
}
return n;
} }
#if 0 #if 0
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment