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