linux/mm/maccess.c
<<
>>
Prefs
   1/*
   2 * Access kernel memory without faulting.
   3 */
   4#include <linux/export.h>
   5#include <linux/mm.h>
   6#include <linux/uaccess.h>
   7
   8/**
   9 * probe_kernel_read(): safely attempt to read from a location
  10 * @dst: pointer to the buffer that shall take the data
  11 * @src: address to read from
  12 * @size: size of the data chunk
  13 *
  14 * Safely read from address @src to the buffer at @dst.  If a kernel fault
  15 * happens, handle that and return -EFAULT.
  16 *
  17 * We ensure that the copy_from_user is executed in atomic context so that
  18 * do_page_fault() doesn't attempt to take mmap_sem.  This makes
  19 * probe_kernel_read() suitable for use within regions where the caller
  20 * already holds mmap_sem, or other locks which nest inside mmap_sem.
  21 */
  22
  23long __weak probe_kernel_read(void *dst, const void *src, size_t size)
  24    __attribute__((alias("__probe_kernel_read")));
  25
  26long __probe_kernel_read(void *dst, const void *src, size_t size)
  27{
  28        long ret;
  29        mm_segment_t old_fs = get_fs();
  30
  31        set_fs(KERNEL_DS);
  32        pagefault_disable();
  33        ret = __copy_from_user_inatomic(dst,
  34                        (__force const void __user *)src, size);
  35        pagefault_enable();
  36        set_fs(old_fs);
  37
  38        return ret ? -EFAULT : 0;
  39}
  40EXPORT_SYMBOL_GPL(probe_kernel_read);
  41
  42/**
  43 * probe_kernel_write(): safely attempt to write to a location
  44 * @dst: address to write to
  45 * @src: pointer to the data that shall be written
  46 * @size: size of the data chunk
  47 *
  48 * Safely write to address @dst from the buffer at @src.  If a kernel fault
  49 * happens, handle that and return -EFAULT.
  50 */
  51long __weak probe_kernel_write(void *dst, const void *src, size_t size)
  52    __attribute__((alias("__probe_kernel_write")));
  53
  54long __probe_kernel_write(void *dst, const void *src, size_t size)
  55{
  56        long ret;
  57        mm_segment_t old_fs = get_fs();
  58
  59        set_fs(KERNEL_DS);
  60        pagefault_disable();
  61        ret = __copy_to_user_inatomic((__force void __user *)dst, src, size);
  62        pagefault_enable();
  63        set_fs(old_fs);
  64
  65        return ret ? -EFAULT : 0;
  66}
  67EXPORT_SYMBOL_GPL(probe_kernel_write);
  68
  69/**
  70 * strncpy_from_unsafe: - Copy a NUL terminated string from unsafe address.
  71 * @dst:   Destination address, in kernel space.  This buffer must be at
  72 *         least @count bytes long.
  73 * @src:   Unsafe address.
  74 * @count: Maximum number of bytes to copy, including the trailing NUL.
  75 *
  76 * Copies a NUL-terminated string from unsafe address to kernel buffer.
  77 *
  78 * On success, returns the length of the string INCLUDING the trailing NUL.
  79 *
  80 * If access fails, returns -EFAULT (some data may have been copied
  81 * and the trailing NUL added).
  82 *
  83 * If @count is smaller than the length of the string, copies @count-1 bytes,
  84 * sets the last byte of @dst buffer to NUL and returns @count.
  85 */
  86long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count)
  87{
  88        mm_segment_t old_fs = get_fs();
  89        const void *src = unsafe_addr;
  90        long ret;
  91
  92        if (unlikely(count <= 0))
  93                return 0;
  94
  95        set_fs(KERNEL_DS);
  96        pagefault_disable();
  97
  98        do {
  99                ret = __copy_from_user_inatomic(dst++,
 100                                                (const void __user __force *)src++, 1);
 101        } while (dst[-1] && ret == 0 && src - unsafe_addr < count);
 102
 103        dst[-1] = '\0';
 104        pagefault_enable();
 105        set_fs(old_fs);
 106
 107        return ret ? -EFAULT : src - unsafe_addr;
 108}
 109