cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

maccess.c (5938B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Access kernel or user memory without faulting.
      4 */
      5#include <linux/export.h>
      6#include <linux/mm.h>
      7#include <linux/uaccess.h>
      8
      9bool __weak copy_from_kernel_nofault_allowed(const void *unsafe_src,
     10		size_t size)
     11{
     12	return true;
     13}
     14
     15#define copy_from_kernel_nofault_loop(dst, src, len, type, err_label)	\
     16	while (len >= sizeof(type)) {					\
     17		__get_kernel_nofault(dst, src, type, err_label);		\
     18		dst += sizeof(type);					\
     19		src += sizeof(type);					\
     20		len -= sizeof(type);					\
     21	}
     22
     23long copy_from_kernel_nofault(void *dst, const void *src, size_t size)
     24{
     25	unsigned long align = 0;
     26
     27	if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS))
     28		align = (unsigned long)dst | (unsigned long)src;
     29
     30	if (!copy_from_kernel_nofault_allowed(src, size))
     31		return -ERANGE;
     32
     33	pagefault_disable();
     34	if (!(align & 7))
     35		copy_from_kernel_nofault_loop(dst, src, size, u64, Efault);
     36	if (!(align & 3))
     37		copy_from_kernel_nofault_loop(dst, src, size, u32, Efault);
     38	if (!(align & 1))
     39		copy_from_kernel_nofault_loop(dst, src, size, u16, Efault);
     40	copy_from_kernel_nofault_loop(dst, src, size, u8, Efault);
     41	pagefault_enable();
     42	return 0;
     43Efault:
     44	pagefault_enable();
     45	return -EFAULT;
     46}
     47EXPORT_SYMBOL_GPL(copy_from_kernel_nofault);
     48
     49#define copy_to_kernel_nofault_loop(dst, src, len, type, err_label)	\
     50	while (len >= sizeof(type)) {					\
     51		__put_kernel_nofault(dst, src, type, err_label);		\
     52		dst += sizeof(type);					\
     53		src += sizeof(type);					\
     54		len -= sizeof(type);					\
     55	}
     56
     57long copy_to_kernel_nofault(void *dst, const void *src, size_t size)
     58{
     59	unsigned long align = 0;
     60
     61	if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS))
     62		align = (unsigned long)dst | (unsigned long)src;
     63
     64	pagefault_disable();
     65	if (!(align & 7))
     66		copy_to_kernel_nofault_loop(dst, src, size, u64, Efault);
     67	if (!(align & 3))
     68		copy_to_kernel_nofault_loop(dst, src, size, u32, Efault);
     69	if (!(align & 1))
     70		copy_to_kernel_nofault_loop(dst, src, size, u16, Efault);
     71	copy_to_kernel_nofault_loop(dst, src, size, u8, Efault);
     72	pagefault_enable();
     73	return 0;
     74Efault:
     75	pagefault_enable();
     76	return -EFAULT;
     77}
     78
     79long strncpy_from_kernel_nofault(char *dst, const void *unsafe_addr, long count)
     80{
     81	const void *src = unsafe_addr;
     82
     83	if (unlikely(count <= 0))
     84		return 0;
     85	if (!copy_from_kernel_nofault_allowed(unsafe_addr, count))
     86		return -ERANGE;
     87
     88	pagefault_disable();
     89	do {
     90		__get_kernel_nofault(dst, src, u8, Efault);
     91		dst++;
     92		src++;
     93	} while (dst[-1] && src - unsafe_addr < count);
     94	pagefault_enable();
     95
     96	dst[-1] = '\0';
     97	return src - unsafe_addr;
     98Efault:
     99	pagefault_enable();
    100	dst[-1] = '\0';
    101	return -EFAULT;
    102}
    103
    104/**
    105 * copy_from_user_nofault(): safely attempt to read from a user-space location
    106 * @dst: pointer to the buffer that shall take the data
    107 * @src: address to read from. This must be a user address.
    108 * @size: size of the data chunk
    109 *
    110 * Safely read from user address @src to the buffer at @dst. If a kernel fault
    111 * happens, handle that and return -EFAULT.
    112 */
    113long copy_from_user_nofault(void *dst, const void __user *src, size_t size)
    114{
    115	long ret = -EFAULT;
    116	if (access_ok(src, size)) {
    117		pagefault_disable();
    118		ret = __copy_from_user_inatomic(dst, src, size);
    119		pagefault_enable();
    120	}
    121
    122	if (ret)
    123		return -EFAULT;
    124	return 0;
    125}
    126EXPORT_SYMBOL_GPL(copy_from_user_nofault);
    127
    128/**
    129 * copy_to_user_nofault(): safely attempt to write to a user-space location
    130 * @dst: address to write to
    131 * @src: pointer to the data that shall be written
    132 * @size: size of the data chunk
    133 *
    134 * Safely write to address @dst from the buffer at @src.  If a kernel fault
    135 * happens, handle that and return -EFAULT.
    136 */
    137long copy_to_user_nofault(void __user *dst, const void *src, size_t size)
    138{
    139	long ret = -EFAULT;
    140
    141	if (access_ok(dst, size)) {
    142		pagefault_disable();
    143		ret = __copy_to_user_inatomic(dst, src, size);
    144		pagefault_enable();
    145	}
    146
    147	if (ret)
    148		return -EFAULT;
    149	return 0;
    150}
    151EXPORT_SYMBOL_GPL(copy_to_user_nofault);
    152
    153/**
    154 * strncpy_from_user_nofault: - Copy a NUL terminated string from unsafe user
    155 *				address.
    156 * @dst:   Destination address, in kernel space.  This buffer must be at
    157 *         least @count bytes long.
    158 * @unsafe_addr: Unsafe user address.
    159 * @count: Maximum number of bytes to copy, including the trailing NUL.
    160 *
    161 * Copies a NUL-terminated string from unsafe user address to kernel buffer.
    162 *
    163 * On success, returns the length of the string INCLUDING the trailing NUL.
    164 *
    165 * If access fails, returns -EFAULT (some data may have been copied
    166 * and the trailing NUL added).
    167 *
    168 * If @count is smaller than the length of the string, copies @count-1 bytes,
    169 * sets the last byte of @dst buffer to NUL and returns @count.
    170 */
    171long strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
    172			      long count)
    173{
    174	long ret;
    175
    176	if (unlikely(count <= 0))
    177		return 0;
    178
    179	pagefault_disable();
    180	ret = strncpy_from_user(dst, unsafe_addr, count);
    181	pagefault_enable();
    182
    183	if (ret >= count) {
    184		ret = count;
    185		dst[ret - 1] = '\0';
    186	} else if (ret > 0) {
    187		ret++;
    188	}
    189
    190	return ret;
    191}
    192
    193/**
    194 * strnlen_user_nofault: - Get the size of a user string INCLUDING final NUL.
    195 * @unsafe_addr: The string to measure.
    196 * @count: Maximum count (including NUL)
    197 *
    198 * Get the size of a NUL-terminated string in user space without pagefault.
    199 *
    200 * Returns the size of the string INCLUDING the terminating NUL.
    201 *
    202 * If the string is too long, returns a number larger than @count. User
    203 * has to check the return value against "> count".
    204 * On exception (or invalid count), returns 0.
    205 *
    206 * Unlike strnlen_user, this can be used from IRQ handler etc. because
    207 * it disables pagefaults.
    208 */
    209long strnlen_user_nofault(const void __user *unsafe_addr, long count)
    210{
    211	int ret;
    212
    213	pagefault_disable();
    214	ret = strnlen_user(unsafe_addr, count);
    215	pagefault_enable();
    216
    217	return ret;
    218}
    219
    220void __copy_overflow(int size, unsigned long count)
    221{
    222	WARN(1, "Buffer overflow detected (%d < %lu)!\n", size, count);
    223}
    224EXPORT_SYMBOL(__copy_overflow);