Blame view

mm/maccess.c 3.05 KB
c33fa9f56   Ingo Molnar   uaccess: add prob...
1
2
3
  /*
   * Access kernel memory without faulting.
   */
b95f1b31b   Paul Gortmaker   mm: Map most file...
4
  #include <linux/export.h>
c33fa9f56   Ingo Molnar   uaccess: add prob...
5
  #include <linux/mm.h>
7c7fcf762   David Howells   MN10300: Save fra...
6
  #include <linux/uaccess.h>
c33fa9f56   Ingo Molnar   uaccess: add prob...
7
8
9
10
11
12
13
14
15
  
  /**
   * probe_kernel_read(): safely attempt to read from a location
   * @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.
0ab32b6f1   Andrew Morton   uaccess: reimplem...
16
17
18
19
20
   *
   * We ensure that the copy_from_user is executed in atomic context so that
   * do_page_fault() doesn't attempt to take mmap_sem.  This makes
   * probe_kernel_read() suitable for use within regions where the caller
   * already holds mmap_sem, or other locks which nest inside mmap_sem.
c33fa9f56   Ingo Molnar   uaccess: add prob...
21
   */
6144a85a0   Jason Wessel   maccess,probe_ker...
22

f29c50419   Steven Rostedt   maccess,probe_ker...
23
  long __weak probe_kernel_read(void *dst, const void *src, size_t size)
6144a85a0   Jason Wessel   maccess,probe_ker...
24
      __attribute__((alias("__probe_kernel_read")));
f29c50419   Steven Rostedt   maccess,probe_ker...
25
  long __probe_kernel_read(void *dst, const void *src, size_t size)
c33fa9f56   Ingo Molnar   uaccess: add prob...
26
27
  {
  	long ret;
b4b8ac524   Jason Wessel   kgdb: fix optiona...
28
  	mm_segment_t old_fs = get_fs();
c33fa9f56   Ingo Molnar   uaccess: add prob...
29

b4b8ac524   Jason Wessel   kgdb: fix optiona...
30
  	set_fs(KERNEL_DS);
c33fa9f56   Ingo Molnar   uaccess: add prob...
31
32
33
34
  	pagefault_disable();
  	ret = __copy_from_user_inatomic(dst,
  			(__force const void __user *)src, size);
  	pagefault_enable();
b4b8ac524   Jason Wessel   kgdb: fix optiona...
35
  	set_fs(old_fs);
c33fa9f56   Ingo Molnar   uaccess: add prob...
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  
  	return ret ? -EFAULT : 0;
  }
  EXPORT_SYMBOL_GPL(probe_kernel_read);
  
  /**
   * probe_kernel_write(): safely attempt to write to a location
   * @dst: address to write to
   * @src: pointer to the data that shall be written
   * @size: size of the data chunk
   *
   * Safely write to address @dst from the buffer at @src.  If a kernel fault
   * happens, handle that and return -EFAULT.
   */
f29c50419   Steven Rostedt   maccess,probe_ker...
50
  long __weak probe_kernel_write(void *dst, const void *src, size_t size)
6144a85a0   Jason Wessel   maccess,probe_ker...
51
      __attribute__((alias("__probe_kernel_write")));
f29c50419   Steven Rostedt   maccess,probe_ker...
52
  long __probe_kernel_write(void *dst, const void *src, size_t size)
c33fa9f56   Ingo Molnar   uaccess: add prob...
53
54
  {
  	long ret;
b4b8ac524   Jason Wessel   kgdb: fix optiona...
55
  	mm_segment_t old_fs = get_fs();
c33fa9f56   Ingo Molnar   uaccess: add prob...
56

b4b8ac524   Jason Wessel   kgdb: fix optiona...
57
  	set_fs(KERNEL_DS);
c33fa9f56   Ingo Molnar   uaccess: add prob...
58
59
60
  	pagefault_disable();
  	ret = __copy_to_user_inatomic((__force void __user *)dst, src, size);
  	pagefault_enable();
b4b8ac524   Jason Wessel   kgdb: fix optiona...
61
  	set_fs(old_fs);
c33fa9f56   Ingo Molnar   uaccess: add prob...
62
63
64
65
  
  	return ret ? -EFAULT : 0;
  }
  EXPORT_SYMBOL_GPL(probe_kernel_write);
dbb7ee0e4   Alexei Starovoitov   lib: move strncpy...
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
  
  /**
   * strncpy_from_unsafe: - Copy a NUL terminated string from unsafe address.
   * @dst:   Destination address, in kernel space.  This buffer must be at
   *         least @count bytes long.
   * @src:   Unsafe address.
   * @count: Maximum number of bytes to copy, including the trailing NUL.
   *
   * Copies a NUL-terminated string from unsafe 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(char *dst, const void *unsafe_addr, long count)
  {
  	mm_segment_t old_fs = get_fs();
  	const void *src = unsafe_addr;
  	long ret;
  
  	if (unlikely(count <= 0))
  		return 0;
  
  	set_fs(KERNEL_DS);
  	pagefault_disable();
  
  	do {
bd28b1459   Linus Torvalds   x86: remove more ...
97
  		ret = __get_user(*dst++, (const char __user __force *)src++);
dbb7ee0e4   Alexei Starovoitov   lib: move strncpy...
98
99
100
101
102
  	} while (dst[-1] && ret == 0 && src - unsafe_addr < count);
  
  	dst[-1] = '\0';
  	pagefault_enable();
  	set_fs(old_fs);
9dd861d55   Rasmus Villemoes   mm/maccess.c: act...
103
  	return ret ? -EFAULT : src - unsafe_addr;
dbb7ee0e4   Alexei Starovoitov   lib: move strncpy...
104
  }