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
John David Anglin 2025-07-21 15:39:26 -04:00 committed by Helge Deller
parent cb22f247f3
commit 91428ca932
2 changed files with 46 additions and 1 deletions

View File

@ -32,6 +32,34 @@
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_CR16 16 /* CR16 Interval Timer */
#define CR_EIRR 23 /* External Interrupt Request Register */

View File

@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/compiler.h>
#include <linux/uaccess.h>
#include <linux/mm.h>
#define get_user_space() mfsp(SR_USER)
#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 len)
{
unsigned long start = (unsigned long) src;
unsigned long end = start + len;
unsigned long newlen = len;
mtsp(get_user_space(), SR_TEMP1);
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);