parisc: Check region is readable by user in raw_copy_from_user()
Because of the way the _PAGE_READ is handled in the parisc PTE, an access interruption is not generated when the kernel reads from a region where the _PAGE_READ is zero. The current code was written assuming read access faults would also occur in the kernel. This change adds user access checks to raw_copy_from_user(). The prober_user() define checks whether user code has read access to a virtual address. Note that page faults are not handled in the exception support for the probe instruction. For this reason, we precede the probe by a ldb access check. Signed-off-by: John David Anglin <dave.anglin@bell.net> Signed-off-by: Helge Deller <deller@gmx.de> Cc: stable@vger.kernel.org # v5.12+pull/1320/head
parent
cb22f247f3
commit
91428ca932
|
|
@ -32,6 +32,34 @@
|
||||||
pa; \
|
pa; \
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* prober_user() - Probe user read access
|
||||||
|
* @sr: Space regster.
|
||||||
|
* @va: Virtual address.
|
||||||
|
*
|
||||||
|
* Return: Non-zero if address is accessible.
|
||||||
|
*
|
||||||
|
* Due to the way _PAGE_READ is handled in TLB entries, we need
|
||||||
|
* a special check to determine whether a user address is accessible.
|
||||||
|
* The ldb instruction does the initial access check. If it is
|
||||||
|
* successful, the probe instruction checks user access rights.
|
||||||
|
*/
|
||||||
|
#define prober_user(sr, va) ({ \
|
||||||
|
unsigned long read_allowed; \
|
||||||
|
__asm__ __volatile__( \
|
||||||
|
"copy %%r0,%0\n" \
|
||||||
|
"8:\tldb 0(%%sr%1,%2),%%r0\n" \
|
||||||
|
"\tproberi (%%sr%1,%2),%3,%0\n" \
|
||||||
|
"9:\n" \
|
||||||
|
ASM_EXCEPTIONTABLE_ENTRY(8b, 9b, \
|
||||||
|
"or %%r0,%%r0,%%r0") \
|
||||||
|
: "=&r" (read_allowed) \
|
||||||
|
: "i" (sr), "r" (va), "i" (PRIV_USER) \
|
||||||
|
: "memory" \
|
||||||
|
); \
|
||||||
|
read_allowed; \
|
||||||
|
})
|
||||||
|
|
||||||
#define CR_EIEM 15 /* External Interrupt Enable Mask */
|
#define CR_EIEM 15 /* External Interrupt Enable Mask */
|
||||||
#define CR_CR16 16 /* CR16 Interval Timer */
|
#define CR_CR16 16 /* CR16 Interval Timer */
|
||||||
#define CR_EIRR 23 /* External Interrupt Request Register */
|
#define CR_EIRR 23 /* External Interrupt Request Register */
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/compiler.h>
|
#include <linux/compiler.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
|
|
||||||
#define get_user_space() mfsp(SR_USER)
|
#define get_user_space() mfsp(SR_USER)
|
||||||
#define get_kernel_space() SR_KERNEL
|
#define get_kernel_space() SR_KERNEL
|
||||||
|
|
@ -32,9 +33,25 @@ EXPORT_SYMBOL(raw_copy_to_user);
|
||||||
unsigned long raw_copy_from_user(void *dst, const void __user *src,
|
unsigned long raw_copy_from_user(void *dst, const void __user *src,
|
||||||
unsigned long len)
|
unsigned long len)
|
||||||
{
|
{
|
||||||
|
unsigned long start = (unsigned long) src;
|
||||||
|
unsigned long end = start + len;
|
||||||
|
unsigned long newlen = len;
|
||||||
|
|
||||||
mtsp(get_user_space(), SR_TEMP1);
|
mtsp(get_user_space(), SR_TEMP1);
|
||||||
mtsp(get_kernel_space(), SR_TEMP2);
|
mtsp(get_kernel_space(), SR_TEMP2);
|
||||||
return pa_memcpy(dst, (void __force *)src, len);
|
|
||||||
|
/* Check region is user accessible */
|
||||||
|
if (start)
|
||||||
|
while (start < end) {
|
||||||
|
if (!prober_user(SR_TEMP1, start)) {
|
||||||
|
newlen = (start - (unsigned long) src);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
start += PAGE_SIZE;
|
||||||
|
/* align to page boundry which may have different permission */
|
||||||
|
start = PAGE_ALIGN_DOWN(start);
|
||||||
|
}
|
||||||
|
return len - newlen + pa_memcpy(dst, (void __force *)src, newlen);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(raw_copy_from_user);
|
EXPORT_SYMBOL(raw_copy_from_user);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue