mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	uaccess: Add non-pagefault user-space read functions
Add probe_user_read(), strncpy_from_unsafe_user() and strnlen_unsafe_user() which allows caller to access user-space in IRQ context. Current probe_kernel_read() and strncpy_from_unsafe() are not available for user-space memory, because it sets KERNEL_DS while accessing data. On some arch, user address space and kernel address space can be co-exist, but others can not. In that case, setting KERNEL_DS means given address is treated as a kernel address space. Also strnlen_user() is only available from user context since it can sleep if pagefault is enabled. To access user-space memory without pagefault, we need these new functions which sets USER_DS while accessing the data. Link: http://lkml.kernel.org/r/155789869802.26965.4940338412595759063.stgit@devnote2 Acked-by: Ingo Molnar <mingo@kernel.org> Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org> Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
This commit is contained in:
		
							parent
							
								
									2d8d8fac3b
								
							
						
					
					
						commit
						3d7081822f
					
				
					 2 changed files with 130 additions and 6 deletions
				
			
		| 
						 | 
					@ -242,6 +242,17 @@ static inline unsigned long __copy_from_user_inatomic_nocache(void *to,
 | 
				
			||||||
extern long probe_kernel_read(void *dst, const void *src, size_t size);
 | 
					extern long probe_kernel_read(void *dst, const void *src, size_t size);
 | 
				
			||||||
extern long __probe_kernel_read(void *dst, const void *src, size_t size);
 | 
					extern long __probe_kernel_read(void *dst, const void *src, size_t size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * probe_user_read(): safely attempt to read from a location in user space
 | 
				
			||||||
 | 
					 * @dst: pointer to the buffer that shall take the data
 | 
				
			||||||
 | 
					 * @src: address to read from
 | 
				
			||||||
 | 
					 * @size: size of the data chunk
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Safely read from address @src to the buffer at @dst.  If a kernel fault
 | 
				
			||||||
 | 
					 * happens, handle that and return -EFAULT.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					extern long probe_user_read(void *dst, const void __user *src, size_t size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * probe_kernel_write(): safely attempt to write to a location
 | 
					 * probe_kernel_write(): safely attempt to write to a location
 | 
				
			||||||
 * @dst: address to write to
 | 
					 * @dst: address to write to
 | 
				
			||||||
| 
						 | 
					@ -255,6 +266,9 @@ extern long notrace probe_kernel_write(void *dst, const void *src, size_t size);
 | 
				
			||||||
extern long notrace __probe_kernel_write(void *dst, const void *src, size_t size);
 | 
					extern long notrace __probe_kernel_write(void *dst, const void *src, size_t size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count);
 | 
					extern long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count);
 | 
				
			||||||
 | 
					extern long strncpy_from_unsafe_user(char *dst, const void __user *unsafe_addr,
 | 
				
			||||||
 | 
									     long count);
 | 
				
			||||||
 | 
					extern long strnlen_unsafe_user(const void __user *unsafe_addr, long count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * probe_kernel_address(): safely attempt to read from a location
 | 
					 * probe_kernel_address(): safely attempt to read from a location
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										122
									
								
								mm/maccess.c
									
									
									
									
									
								
							
							
						
						
									
										122
									
								
								mm/maccess.c
									
									
									
									
									
								
							| 
						 | 
					@ -5,8 +5,20 @@
 | 
				
			||||||
#include <linux/mm.h>
 | 
					#include <linux/mm.h>
 | 
				
			||||||
#include <linux/uaccess.h>
 | 
					#include <linux/uaccess.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static __always_inline long
 | 
				
			||||||
 | 
					probe_read_common(void *dst, const void __user *src, size_t size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						long ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pagefault_disable();
 | 
				
			||||||
 | 
						ret = __copy_from_user_inatomic(dst, src, size);
 | 
				
			||||||
 | 
						pagefault_enable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret ? -EFAULT : 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * probe_kernel_read(): safely attempt to read from a location
 | 
					 * probe_kernel_read(): safely attempt to read from a kernel-space location
 | 
				
			||||||
 * @dst: pointer to the buffer that shall take the data
 | 
					 * @dst: pointer to the buffer that shall take the data
 | 
				
			||||||
 * @src: address to read from
 | 
					 * @src: address to read from
 | 
				
			||||||
 * @size: size of the data chunk
 | 
					 * @size: size of the data chunk
 | 
				
			||||||
| 
						 | 
					@ -29,16 +41,40 @@ long __probe_kernel_read(void *dst, const void *src, size_t size)
 | 
				
			||||||
	mm_segment_t old_fs = get_fs();
 | 
						mm_segment_t old_fs = get_fs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	set_fs(KERNEL_DS);
 | 
						set_fs(KERNEL_DS);
 | 
				
			||||||
	pagefault_disable();
 | 
						ret = probe_read_common(dst, (__force const void __user *)src, size);
 | 
				
			||||||
	ret = __copy_from_user_inatomic(dst,
 | 
					 | 
				
			||||||
			(__force const void __user *)src, size);
 | 
					 | 
				
			||||||
	pagefault_enable();
 | 
					 | 
				
			||||||
	set_fs(old_fs);
 | 
						set_fs(old_fs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return ret ? -EFAULT : 0;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL_GPL(probe_kernel_read);
 | 
					EXPORT_SYMBOL_GPL(probe_kernel_read);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * probe_user_read(): safely attempt to read from a user-space location
 | 
				
			||||||
 | 
					 * @dst: pointer to the buffer that shall take the data
 | 
				
			||||||
 | 
					 * @src: address to read from. This must be a user address.
 | 
				
			||||||
 | 
					 * @size: size of the data chunk
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Safely read from user address @src to the buffer at @dst. If a kernel fault
 | 
				
			||||||
 | 
					 * happens, handle that and return -EFAULT.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					long __weak probe_user_read(void *dst, const void __user *src, size_t size)
 | 
				
			||||||
 | 
					    __attribute__((alias("__probe_user_read")));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					long __probe_user_read(void *dst, const void __user *src, size_t size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						long ret = -EFAULT;
 | 
				
			||||||
 | 
						mm_segment_t old_fs = get_fs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						set_fs(USER_DS);
 | 
				
			||||||
 | 
						if (access_ok(src, size))
 | 
				
			||||||
 | 
							ret = probe_read_common(dst, src, size);
 | 
				
			||||||
 | 
						set_fs(old_fs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(probe_user_read);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * probe_kernel_write(): safely attempt to write to a location
 | 
					 * probe_kernel_write(): safely attempt to write to a location
 | 
				
			||||||
 * @dst: address to write to
 | 
					 * @dst: address to write to
 | 
				
			||||||
| 
						 | 
					@ -66,6 +102,7 @@ long __probe_kernel_write(void *dst, const void *src, size_t size)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL_GPL(probe_kernel_write);
 | 
					EXPORT_SYMBOL_GPL(probe_kernel_write);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * strncpy_from_unsafe: - Copy a NUL terminated string from unsafe address.
 | 
					 * strncpy_from_unsafe: - Copy a NUL terminated string from unsafe address.
 | 
				
			||||||
 * @dst:   Destination address, in kernel space.  This buffer must be at
 | 
					 * @dst:   Destination address, in kernel space.  This buffer must be at
 | 
				
			||||||
| 
						 | 
					@ -105,3 +142,76 @@ long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return ret ? -EFAULT : src - unsafe_addr;
 | 
						return ret ? -EFAULT : src - unsafe_addr;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * strncpy_from_unsafe_user: - Copy a NUL terminated string from unsafe user
 | 
				
			||||||
 | 
					 *				address.
 | 
				
			||||||
 | 
					 * @dst:   Destination address, in kernel space.  This buffer must be at
 | 
				
			||||||
 | 
					 *         least @count bytes long.
 | 
				
			||||||
 | 
					 * @unsafe_addr: Unsafe user address.
 | 
				
			||||||
 | 
					 * @count: Maximum number of bytes to copy, including the trailing NUL.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copies a NUL-terminated string from unsafe user address to kernel buffer.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * On success, returns the length of the string INCLUDING the trailing NUL.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * If access fails, returns -EFAULT (some data may have been copied
 | 
				
			||||||
 | 
					 * and the trailing NUL added).
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * If @count is smaller than the length of the string, copies @count-1 bytes,
 | 
				
			||||||
 | 
					 * sets the last byte of @dst buffer to NUL and returns @count.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					long strncpy_from_unsafe_user(char *dst, const void __user *unsafe_addr,
 | 
				
			||||||
 | 
								      long count)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mm_segment_t old_fs = get_fs();
 | 
				
			||||||
 | 
						long ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (unlikely(count <= 0))
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						set_fs(USER_DS);
 | 
				
			||||||
 | 
						pagefault_disable();
 | 
				
			||||||
 | 
						ret = strncpy_from_user(dst, unsafe_addr, count);
 | 
				
			||||||
 | 
						pagefault_enable();
 | 
				
			||||||
 | 
						set_fs(old_fs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ret >= count) {
 | 
				
			||||||
 | 
							ret = count;
 | 
				
			||||||
 | 
							dst[ret - 1] = '\0';
 | 
				
			||||||
 | 
						} else if (ret > 0) {
 | 
				
			||||||
 | 
							ret++;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * strnlen_unsafe_user: - Get the size of a user string INCLUDING final NUL.
 | 
				
			||||||
 | 
					 * @unsafe_addr: The string to measure.
 | 
				
			||||||
 | 
					 * @count: Maximum count (including NUL)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Get the size of a NUL-terminated string in user space without pagefault.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Returns the size of the string INCLUDING the terminating NUL.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * If the string is too long, returns a number larger than @count. User
 | 
				
			||||||
 | 
					 * has to check the return value against "> count".
 | 
				
			||||||
 | 
					 * On exception (or invalid count), returns 0.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Unlike strnlen_user, this can be used from IRQ handler etc. because
 | 
				
			||||||
 | 
					 * it disables pagefaults.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					long strnlen_unsafe_user(const void __user *unsafe_addr, long count)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mm_segment_t old_fs = get_fs();
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						set_fs(USER_DS);
 | 
				
			||||||
 | 
						pagefault_disable();
 | 
				
			||||||
 | 
						ret = strnlen_user(unsafe_addr, count);
 | 
				
			||||||
 | 
						pagefault_enable();
 | 
				
			||||||
 | 
						set_fs(old_fs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue