Commit 91428ca9 authored by John David Anglin's avatar John David Anglin Committed by Helge Deller
Browse files

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: default avatarJohn David Anglin <dave.anglin@bell.net>
Signed-off-by: default avatarHelge Deller <deller@gmx.de>
Cc: stable@vger.kernel.org # v5.12+
parent cb22f247
Loading
Loading
Loading
Loading
+28 −0
Original line number Diff line number Diff line
@@ -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 */
+18 −1
Original line number Diff line number Diff line
@@ -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);