Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c014953d authored by Russell King's avatar Russell King
Browse files

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
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -510,10 +510,14 @@ __copy_to_user_std(void __user *to, const void *from, unsigned long n);
static inline unsigned long __must_check
__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();
	n = arm_copy_to_user(to, from, n);
	uaccess_restore(__ua_flags);
	return n;
#else
	return arm_copy_to_user(to, from, n);
#endif
}

extern unsigned long __must_check
+23 −6
Original line number Diff line number Diff line
@@ -88,6 +88,7 @@ pin_page_for_write(const void __user *_addr, pte_t **ptep, spinlock_t **ptlp)
static unsigned long noinline
__copy_to_user_memcpy(void __user *to, const void *from, unsigned long n)
{
	unsigned long ua_flags;
	int atomic;

	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)
		if (tocopy > n)
			tocopy = n;

		ua_flags = uaccess_save_and_enable();
		memcpy((void *)to, from, tocopy);
		uaccess_restore(ua_flags);
		to += tocopy;
		from += tocopy;
		n -= tocopy;
@@ -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
	 * as well making this test almost invisible.
	 */
	if (n < 64)
		return __copy_to_user_std(to, from, n);
	return __copy_to_user_memcpy(to, from, n);
	if (n < 64) {
		unsigned long ua_flags = uaccess_save_and_enable();
		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
__clear_user_memset(void __user *addr, unsigned long n)
{
	unsigned long ua_flags;

	if (unlikely(segment_eq(get_fs(), KERNEL_DS))) {
		memset((void *)addr, 0, n);
		return 0;
@@ -175,7 +185,9 @@ __clear_user_memset(void __user *addr, unsigned long n)
		if (tocopy > n)
			tocopy = n;

		ua_flags = uaccess_save_and_enable();
		memset((void *)addr, 0, tocopy);
		uaccess_restore(ua_flags);
		addr += tocopy;
		n -= tocopy;

@@ -193,9 +205,14 @@ __clear_user_memset(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. */
	if (n < 64)
		return __clear_user_std(addr, n);
	return __clear_user_memset(addr, n);
	if (n < 64) {
		unsigned long ua_flags = uaccess_save_and_enable();
		n = __clear_user_std(addr, n);
		uaccess_restore(ua_flags);
	} else {
		n = __clear_user_memset(addr, n);
	}
	return n;
}

#if 0