162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Access kernel or user memory without faulting.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/export.h>
662306a36Sopenharmony_ci#include <linux/mm.h>
762306a36Sopenharmony_ci#include <linux/uaccess.h>
862306a36Sopenharmony_ci#include <asm/tlb.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_cibool __weak copy_from_kernel_nofault_allowed(const void *unsafe_src,
1162306a36Sopenharmony_ci		size_t size)
1262306a36Sopenharmony_ci{
1362306a36Sopenharmony_ci	return true;
1462306a36Sopenharmony_ci}
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define copy_from_kernel_nofault_loop(dst, src, len, type, err_label)	\
1762306a36Sopenharmony_ci	while (len >= sizeof(type)) {					\
1862306a36Sopenharmony_ci		__get_kernel_nofault(dst, src, type, err_label);		\
1962306a36Sopenharmony_ci		dst += sizeof(type);					\
2062306a36Sopenharmony_ci		src += sizeof(type);					\
2162306a36Sopenharmony_ci		len -= sizeof(type);					\
2262306a36Sopenharmony_ci	}
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cilong copy_from_kernel_nofault(void *dst, const void *src, size_t size)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	unsigned long align = 0;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS))
2962306a36Sopenharmony_ci		align = (unsigned long)dst | (unsigned long)src;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	if (!copy_from_kernel_nofault_allowed(src, size))
3262306a36Sopenharmony_ci		return -ERANGE;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	pagefault_disable();
3562306a36Sopenharmony_ci	if (!(align & 7))
3662306a36Sopenharmony_ci		copy_from_kernel_nofault_loop(dst, src, size, u64, Efault);
3762306a36Sopenharmony_ci	if (!(align & 3))
3862306a36Sopenharmony_ci		copy_from_kernel_nofault_loop(dst, src, size, u32, Efault);
3962306a36Sopenharmony_ci	if (!(align & 1))
4062306a36Sopenharmony_ci		copy_from_kernel_nofault_loop(dst, src, size, u16, Efault);
4162306a36Sopenharmony_ci	copy_from_kernel_nofault_loop(dst, src, size, u8, Efault);
4262306a36Sopenharmony_ci	pagefault_enable();
4362306a36Sopenharmony_ci	return 0;
4462306a36Sopenharmony_ciEfault:
4562306a36Sopenharmony_ci	pagefault_enable();
4662306a36Sopenharmony_ci	return -EFAULT;
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(copy_from_kernel_nofault);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci#define copy_to_kernel_nofault_loop(dst, src, len, type, err_label)	\
5162306a36Sopenharmony_ci	while (len >= sizeof(type)) {					\
5262306a36Sopenharmony_ci		__put_kernel_nofault(dst, src, type, err_label);		\
5362306a36Sopenharmony_ci		dst += sizeof(type);					\
5462306a36Sopenharmony_ci		src += sizeof(type);					\
5562306a36Sopenharmony_ci		len -= sizeof(type);					\
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cilong copy_to_kernel_nofault(void *dst, const void *src, size_t size)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	unsigned long align = 0;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS))
6362306a36Sopenharmony_ci		align = (unsigned long)dst | (unsigned long)src;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	pagefault_disable();
6662306a36Sopenharmony_ci	if (!(align & 7))
6762306a36Sopenharmony_ci		copy_to_kernel_nofault_loop(dst, src, size, u64, Efault);
6862306a36Sopenharmony_ci	if (!(align & 3))
6962306a36Sopenharmony_ci		copy_to_kernel_nofault_loop(dst, src, size, u32, Efault);
7062306a36Sopenharmony_ci	if (!(align & 1))
7162306a36Sopenharmony_ci		copy_to_kernel_nofault_loop(dst, src, size, u16, Efault);
7262306a36Sopenharmony_ci	copy_to_kernel_nofault_loop(dst, src, size, u8, Efault);
7362306a36Sopenharmony_ci	pagefault_enable();
7462306a36Sopenharmony_ci	return 0;
7562306a36Sopenharmony_ciEfault:
7662306a36Sopenharmony_ci	pagefault_enable();
7762306a36Sopenharmony_ci	return -EFAULT;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cilong strncpy_from_kernel_nofault(char *dst, const void *unsafe_addr, long count)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	const void *src = unsafe_addr;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	if (unlikely(count <= 0))
8562306a36Sopenharmony_ci		return 0;
8662306a36Sopenharmony_ci	if (!copy_from_kernel_nofault_allowed(unsafe_addr, count))
8762306a36Sopenharmony_ci		return -ERANGE;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	pagefault_disable();
9062306a36Sopenharmony_ci	do {
9162306a36Sopenharmony_ci		__get_kernel_nofault(dst, src, u8, Efault);
9262306a36Sopenharmony_ci		dst++;
9362306a36Sopenharmony_ci		src++;
9462306a36Sopenharmony_ci	} while (dst[-1] && src - unsafe_addr < count);
9562306a36Sopenharmony_ci	pagefault_enable();
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	dst[-1] = '\0';
9862306a36Sopenharmony_ci	return src - unsafe_addr;
9962306a36Sopenharmony_ciEfault:
10062306a36Sopenharmony_ci	pagefault_enable();
10162306a36Sopenharmony_ci	dst[0] = '\0';
10262306a36Sopenharmony_ci	return -EFAULT;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/**
10662306a36Sopenharmony_ci * copy_from_user_nofault(): safely attempt to read from a user-space location
10762306a36Sopenharmony_ci * @dst: pointer to the buffer that shall take the data
10862306a36Sopenharmony_ci * @src: address to read from. This must be a user address.
10962306a36Sopenharmony_ci * @size: size of the data chunk
11062306a36Sopenharmony_ci *
11162306a36Sopenharmony_ci * Safely read from user address @src to the buffer at @dst. If a kernel fault
11262306a36Sopenharmony_ci * happens, handle that and return -EFAULT.
11362306a36Sopenharmony_ci */
11462306a36Sopenharmony_cilong copy_from_user_nofault(void *dst, const void __user *src, size_t size)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	long ret = -EFAULT;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	if (!__access_ok(src, size))
11962306a36Sopenharmony_ci		return ret;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (!nmi_uaccess_okay())
12262306a36Sopenharmony_ci		return ret;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	pagefault_disable();
12562306a36Sopenharmony_ci	ret = __copy_from_user_inatomic(dst, src, size);
12662306a36Sopenharmony_ci	pagefault_enable();
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	if (ret)
12962306a36Sopenharmony_ci		return -EFAULT;
13062306a36Sopenharmony_ci	return 0;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(copy_from_user_nofault);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci/**
13562306a36Sopenharmony_ci * copy_to_user_nofault(): safely attempt to write to a user-space location
13662306a36Sopenharmony_ci * @dst: address to write to
13762306a36Sopenharmony_ci * @src: pointer to the data that shall be written
13862306a36Sopenharmony_ci * @size: size of the data chunk
13962306a36Sopenharmony_ci *
14062306a36Sopenharmony_ci * Safely write to address @dst from the buffer at @src.  If a kernel fault
14162306a36Sopenharmony_ci * happens, handle that and return -EFAULT.
14262306a36Sopenharmony_ci */
14362306a36Sopenharmony_cilong copy_to_user_nofault(void __user *dst, const void *src, size_t size)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	long ret = -EFAULT;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if (access_ok(dst, size)) {
14862306a36Sopenharmony_ci		pagefault_disable();
14962306a36Sopenharmony_ci		ret = __copy_to_user_inatomic(dst, src, size);
15062306a36Sopenharmony_ci		pagefault_enable();
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	if (ret)
15462306a36Sopenharmony_ci		return -EFAULT;
15562306a36Sopenharmony_ci	return 0;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(copy_to_user_nofault);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci/**
16062306a36Sopenharmony_ci * strncpy_from_user_nofault: - Copy a NUL terminated string from unsafe user
16162306a36Sopenharmony_ci *				address.
16262306a36Sopenharmony_ci * @dst:   Destination address, in kernel space.  This buffer must be at
16362306a36Sopenharmony_ci *         least @count bytes long.
16462306a36Sopenharmony_ci * @unsafe_addr: Unsafe user address.
16562306a36Sopenharmony_ci * @count: Maximum number of bytes to copy, including the trailing NUL.
16662306a36Sopenharmony_ci *
16762306a36Sopenharmony_ci * Copies a NUL-terminated string from unsafe user address to kernel buffer.
16862306a36Sopenharmony_ci *
16962306a36Sopenharmony_ci * On success, returns the length of the string INCLUDING the trailing NUL.
17062306a36Sopenharmony_ci *
17162306a36Sopenharmony_ci * If access fails, returns -EFAULT (some data may have been copied
17262306a36Sopenharmony_ci * and the trailing NUL added).
17362306a36Sopenharmony_ci *
17462306a36Sopenharmony_ci * If @count is smaller than the length of the string, copies @count-1 bytes,
17562306a36Sopenharmony_ci * sets the last byte of @dst buffer to NUL and returns @count.
17662306a36Sopenharmony_ci */
17762306a36Sopenharmony_cilong strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
17862306a36Sopenharmony_ci			      long count)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	long ret;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (unlikely(count <= 0))
18362306a36Sopenharmony_ci		return 0;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	pagefault_disable();
18662306a36Sopenharmony_ci	ret = strncpy_from_user(dst, unsafe_addr, count);
18762306a36Sopenharmony_ci	pagefault_enable();
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	if (ret >= count) {
19062306a36Sopenharmony_ci		ret = count;
19162306a36Sopenharmony_ci		dst[ret - 1] = '\0';
19262306a36Sopenharmony_ci	} else if (ret > 0) {
19362306a36Sopenharmony_ci		ret++;
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	return ret;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci/**
20062306a36Sopenharmony_ci * strnlen_user_nofault: - Get the size of a user string INCLUDING final NUL.
20162306a36Sopenharmony_ci * @unsafe_addr: The string to measure.
20262306a36Sopenharmony_ci * @count: Maximum count (including NUL)
20362306a36Sopenharmony_ci *
20462306a36Sopenharmony_ci * Get the size of a NUL-terminated string in user space without pagefault.
20562306a36Sopenharmony_ci *
20662306a36Sopenharmony_ci * Returns the size of the string INCLUDING the terminating NUL.
20762306a36Sopenharmony_ci *
20862306a36Sopenharmony_ci * If the string is too long, returns a number larger than @count. User
20962306a36Sopenharmony_ci * has to check the return value against "> count".
21062306a36Sopenharmony_ci * On exception (or invalid count), returns 0.
21162306a36Sopenharmony_ci *
21262306a36Sopenharmony_ci * Unlike strnlen_user, this can be used from IRQ handler etc. because
21362306a36Sopenharmony_ci * it disables pagefaults.
21462306a36Sopenharmony_ci */
21562306a36Sopenharmony_cilong strnlen_user_nofault(const void __user *unsafe_addr, long count)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	int ret;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	pagefault_disable();
22062306a36Sopenharmony_ci	ret = strnlen_user(unsafe_addr, count);
22162306a36Sopenharmony_ci	pagefault_enable();
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	return ret;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_civoid __copy_overflow(int size, unsigned long count)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	WARN(1, "Buffer overflow detected (%d < %lu)!\n", size, count);
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ciEXPORT_SYMBOL(__copy_overflow);
231