1
2
3
4
5
6
7
8
9
10#include <linux/module.h>
11#include <linux/sched.h>
12#include <linux/errno.h>
13#include <linux/mm.h>
14#include <linux/highmem.h>
15#include <linux/pagemap.h>
16#include <linux/smp_lock.h>
17#include <linux/ptrace.h>
18#include <linux/interrupt.h>
19
20#include <asm/pgtable.h>
21#include <asm/uaccess.h>
22#include <asm/atomic_kmap.h>
23
24
25
26
27static inline struct page *pin_page(unsigned long addr, int write,
28 pte_t *pte)
29{
30 struct mm_struct *mm = current->mm ? : &init_mm;
31 struct page *page = NULL;
32 int ret;
33
34 if (addr >= current_thread_info()->addr_limit.seg)
35 return (struct page *)-1UL;
36
37
38
39retry:
40 page = follow_page_pte(mm, addr, write, pte);
41 if (likely(page != NULL)) {
42 if (!PageReserved(page))
43 get_page(page);
44 return page;
45 }
46 if (pte_present(*pte))
47 return NULL;
48
49
50
51
52
53 spin_unlock(&mm->page_table_lock);
54
55
56
57
58
59
60
61 if (in_atomic()) {
62 spin_lock(&mm->page_table_lock);
63 return NULL;
64 }
65
66 down_read(&mm->mmap_sem);
67 ret = get_user_pages(current, mm, addr, 1, write, 0, NULL, NULL);
68 up_read(&mm->mmap_sem);
69 spin_lock(&mm->page_table_lock);
70
71 if (ret <= 0)
72 return NULL;
73
74
75
76
77 goto retry;
78}
79
80static inline void unpin_page(struct page *page)
81{
82 put_page(page);
83}
84
85
86
87
88
89
90static int rw_vm(unsigned long addr, void *buf, int len, int write)
91{
92 struct mm_struct *mm = current->mm ? : &init_mm;
93
94 if (!len)
95 return 0;
96
97 spin_lock(&mm->page_table_lock);
98
99
100 while (len) {
101 struct page *page = NULL;
102 pte_t pte;
103 int bytes, offset;
104 void *maddr;
105
106 page = pin_page(addr, write, &pte);
107 if ((page == (struct page *)-1UL) ||
108 (!page && !pte_present(pte)))
109 break;
110
111 bytes = len;
112 offset = addr & (PAGE_SIZE-1);
113 if (bytes > PAGE_SIZE-offset)
114 bytes = PAGE_SIZE-offset;
115
116 if (page)
117 maddr = kmap_atomic(page, KM_USER_COPY);
118 else
119
120
121 maddr = kmap_atomic_pte(&pte, KM_USER_COPY);
122
123#define HANDLE_TYPE(type) \
124 case sizeof(type): *(type *)(maddr+offset) = *(type *)(buf); break;
125
126 if (write) {
127 switch (bytes) {
128 HANDLE_TYPE(char);
129 HANDLE_TYPE(int);
130 HANDLE_TYPE(long long);
131 default:
132 memcpy(maddr + offset, buf, bytes);
133 }
134 } else {
135#undef HANDLE_TYPE
136#define HANDLE_TYPE(type) \
137 case sizeof(type): *(type *)(buf) = *(type *)(maddr+offset); break;
138 switch (bytes) {
139 HANDLE_TYPE(char);
140 HANDLE_TYPE(int);
141 HANDLE_TYPE(long long);
142 default:
143 memcpy(buf, maddr + offset, bytes);
144 }
145#undef HANDLE_TYPE
146 }
147 kunmap_atomic(maddr, KM_USER_COPY);
148 if (page)
149 unpin_page(page);
150 len -= bytes;
151 buf += bytes;
152 addr += bytes;
153 }
154 spin_unlock(&mm->page_table_lock);
155
156 return len;
157}
158
159static int str_vm(unsigned long addr, void *buf0, int len, int copy)
160{
161 struct mm_struct *mm = current->mm ? : &init_mm;
162 struct page *page;
163 void *buf = buf0;
164
165 if (!len)
166 return len;
167
168 spin_lock(&mm->page_table_lock);
169
170
171 while (len) {
172 int bytes, offset, left, copied;
173 pte_t pte;
174 char *maddr;
175
176 page = pin_page(addr, copy == 2, &pte);
177 if ((page == (struct page *)-1UL) ||
178 (!page && !pte_present(pte))) {
179 spin_unlock(&mm->page_table_lock);
180 return -EFAULT;
181 }
182 bytes = len;
183 offset = addr & (PAGE_SIZE-1);
184 if (bytes > PAGE_SIZE-offset)
185 bytes = PAGE_SIZE-offset;
186
187 if (page)
188 maddr = kmap_atomic(page, KM_USER_COPY);
189 else
190
191
192 maddr = kmap_atomic_pte(&pte, KM_USER_COPY);
193 if (copy == 2) {
194 memset(maddr + offset, 0, bytes);
195 copied = bytes;
196 left = 0;
197 } else if (copy == 1) {
198 left = strncpy_count(buf, maddr + offset, bytes);
199 copied = bytes - left;
200 } else {
201 copied = strnlen(maddr + offset, bytes);
202 left = bytes - copied;
203 }
204 BUG_ON(bytes < 0 || copied < 0);
205 kunmap_atomic(maddr, KM_USER_COPY);
206 if (page)
207 unpin_page(page);
208 len -= copied;
209 buf += copied;
210 addr += copied;
211 if (left)
212 break;
213 }
214 spin_unlock(&mm->page_table_lock);
215
216 return len;
217}
218
219
220
221
222
223
224int get_user_size(unsigned int size, void *val, const void *ptr)
225{
226 int ret;
227
228 if (unlikely(segment_eq(get_fs(), KERNEL_DS)))
229 ret = __direct_copy_from_user_inatomic(val, ptr, size);
230 else
231 ret = rw_vm((unsigned long)ptr, val, size, 0);
232 if (ret)
233
234
235
236 memset(val + size - ret, 0, ret);
237 return ret;
238}
239
240
241
242
243
244
245int put_user_size(unsigned int size, const void *val, void *ptr)
246{
247 if (unlikely(segment_eq(get_fs(), KERNEL_DS)))
248 return __direct_copy_to_user_inatomic(ptr, val, size);
249 else
250 return rw_vm((unsigned long)ptr, (void *)val, size, 1);
251}
252
253int copy_str_fromuser_size(unsigned int size, void *val, const void *ptr)
254{
255 int copied, left;
256
257 if (unlikely(segment_eq(get_fs(), KERNEL_DS))) {
258 left = strncpy_count(val, ptr, size);
259 copied = size - left;
260 BUG_ON(copied < 0);
261
262 return copied;
263 }
264 left = str_vm((unsigned long)ptr, val, size, 1);
265 if (left < 0)
266 return left;
267 copied = size - left;
268 BUG_ON(copied < 0);
269
270 return copied;
271}
272
273int strlen_fromuser_size(unsigned int size, const void *ptr)
274{
275 int copied, left;
276
277 if (unlikely(segment_eq(get_fs(), KERNEL_DS))) {
278 copied = strnlen(ptr, size) + 1;
279 BUG_ON(copied < 0);
280
281 return copied;
282 }
283 left = str_vm((unsigned long)ptr, NULL, size, 0);
284 if (left < 0)
285 return 0;
286 copied = size - left + 1;
287 BUG_ON(copied < 0);
288
289 return copied;
290}
291
292int zero_user_size(unsigned int size, void *ptr)
293{
294 int left;
295
296 if (unlikely(segment_eq(get_fs(), KERNEL_DS))) {
297 memset(ptr, 0, size);
298 return 0;
299 }
300 left = str_vm((unsigned long)ptr, NULL, size, 2);
301 if (left < 0)
302 return size;
303 return left;
304}
305
306EXPORT_SYMBOL(get_user_size);
307EXPORT_SYMBOL(put_user_size);
308EXPORT_SYMBOL(zero_user_size);
309EXPORT_SYMBOL(copy_str_fromuser_size);
310EXPORT_SYMBOL(strlen_fromuser_size);
311