Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0
2 : /*
3 : * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
4 : */
5 :
6 : #include <linux/err.h>
7 : #include <linux/highmem.h>
8 : #include <linux/mm.h>
9 : #include <linux/module.h>
10 : #include <linux/sched.h>
11 : #include <asm/current.h>
12 : #include <asm/page.h>
13 : #include <kern_util.h>
14 : #include <asm/futex.h>
15 : #include <os.h>
16 :
17 0 : pte_t *virt_to_pte(struct mm_struct *mm, unsigned long addr)
18 : {
19 : pgd_t *pgd;
20 : p4d_t *p4d;
21 : pud_t *pud;
22 : pmd_t *pmd;
23 :
24 0 : if (mm == NULL)
25 : return NULL;
26 :
27 0 : pgd = pgd_offset(mm, addr);
28 : if (!pgd_present(*pgd))
29 : return NULL;
30 :
31 0 : p4d = p4d_offset(pgd, addr);
32 : if (!p4d_present(*p4d))
33 : return NULL;
34 :
35 0 : pud = pud_offset(p4d, addr);
36 0 : if (!pud_present(*pud))
37 : return NULL;
38 :
39 0 : pmd = pmd_offset(pud, addr);
40 0 : if (!pmd_present(*pmd))
41 : return NULL;
42 :
43 0 : return pte_offset_kernel(pmd, addr);
44 : }
45 :
46 0 : static pte_t *maybe_map(unsigned long virt, int is_write)
47 : {
48 0 : pte_t *pte = virt_to_pte(current->mm, virt);
49 : int err, dummy_code;
50 :
51 0 : if ((pte == NULL) || !pte_present(*pte) ||
52 0 : (is_write && !pte_write(*pte))) {
53 0 : err = handle_page_fault(virt, 0, is_write, 1, &dummy_code);
54 0 : if (err)
55 : return NULL;
56 0 : pte = virt_to_pte(current->mm, virt);
57 : }
58 0 : if (!pte_present(*pte))
59 0 : pte = NULL;
60 :
61 : return pte;
62 : }
63 :
64 0 : static int do_op_one_page(unsigned long addr, int len, int is_write,
65 : int (*op)(unsigned long addr, int len, void *arg), void *arg)
66 : {
67 : struct page *page;
68 : pte_t *pte;
69 : int n;
70 :
71 0 : pte = maybe_map(addr, is_write);
72 0 : if (pte == NULL)
73 : return -1;
74 :
75 0 : page = pte_page(*pte);
76 : #ifdef CONFIG_64BIT
77 0 : pagefault_disable();
78 0 : addr = (unsigned long) page_address(page) +
79 0 : (addr & ~PAGE_MASK);
80 : #else
81 : addr = (unsigned long) kmap_atomic(page) +
82 : (addr & ~PAGE_MASK);
83 : #endif
84 0 : n = (*op)(addr, len, arg);
85 :
86 : #ifdef CONFIG_64BIT
87 0 : pagefault_enable();
88 : #else
89 : kunmap_atomic((void *)addr);
90 : #endif
91 :
92 0 : return n;
93 : }
94 :
95 0 : static long buffer_op(unsigned long addr, int len, int is_write,
96 : int (*op)(unsigned long, int, void *), void *arg)
97 : {
98 : long size, remain, n;
99 :
100 0 : size = min(PAGE_ALIGN(addr) - addr, (unsigned long) len);
101 0 : remain = len;
102 :
103 0 : n = do_op_one_page(addr, size, is_write, op, arg);
104 0 : if (n != 0) {
105 0 : remain = (n < 0 ? remain : 0);
106 : goto out;
107 : }
108 :
109 0 : addr += size;
110 0 : remain -= size;
111 0 : if (remain == 0)
112 : goto out;
113 :
114 0 : while (addr < ((addr + remain) & PAGE_MASK)) {
115 0 : n = do_op_one_page(addr, PAGE_SIZE, is_write, op, arg);
116 0 : if (n != 0) {
117 0 : remain = (n < 0 ? remain : 0);
118 : goto out;
119 : }
120 :
121 0 : addr += PAGE_SIZE;
122 0 : remain -= PAGE_SIZE;
123 : }
124 0 : if (remain == 0)
125 : goto out;
126 :
127 0 : n = do_op_one_page(addr, remain, is_write, op, arg);
128 0 : if (n != 0) {
129 0 : remain = (n < 0 ? remain : 0);
130 : goto out;
131 : }
132 :
133 : return 0;
134 : out:
135 : return remain;
136 : }
137 :
138 0 : static int copy_chunk_from_user(unsigned long from, int len, void *arg)
139 : {
140 0 : unsigned long *to_ptr = arg, to = *to_ptr;
141 :
142 0 : memcpy((void *) to, (void *) from, len);
143 0 : *to_ptr += len;
144 0 : return 0;
145 : }
146 :
147 0 : unsigned long raw_copy_from_user(void *to, const void __user *from, unsigned long n)
148 : {
149 0 : return buffer_op((unsigned long) from, n, 0, copy_chunk_from_user, &to);
150 : }
151 : EXPORT_SYMBOL(raw_copy_from_user);
152 :
153 0 : static int copy_chunk_to_user(unsigned long to, int len, void *arg)
154 : {
155 0 : unsigned long *from_ptr = arg, from = *from_ptr;
156 :
157 0 : memcpy((void *) to, (void *) from, len);
158 0 : *from_ptr += len;
159 0 : return 0;
160 : }
161 :
162 0 : unsigned long raw_copy_to_user(void __user *to, const void *from, unsigned long n)
163 : {
164 0 : return buffer_op((unsigned long) to, n, 1, copy_chunk_to_user, &from);
165 : }
166 : EXPORT_SYMBOL(raw_copy_to_user);
167 :
168 0 : static int strncpy_chunk_from_user(unsigned long from, int len, void *arg)
169 : {
170 0 : char **to_ptr = arg, *to = *to_ptr;
171 : int n;
172 :
173 0 : strncpy(to, (void *) from, len);
174 0 : n = strnlen(to, len);
175 0 : *to_ptr += n;
176 :
177 0 : if (n < len)
178 : return 1;
179 0 : return 0;
180 : }
181 :
182 0 : long strncpy_from_user(char *dst, const char __user *src, long count)
183 : {
184 : long n;
185 0 : char *ptr = dst;
186 :
187 0 : if (!access_ok(src, 1))
188 : return -EFAULT;
189 0 : n = buffer_op((unsigned long) src, count, 0, strncpy_chunk_from_user,
190 : &ptr);
191 0 : if (n != 0)
192 : return -EFAULT;
193 0 : return strnlen(dst, count);
194 : }
195 : EXPORT_SYMBOL(strncpy_from_user);
196 :
197 0 : static int clear_chunk(unsigned long addr, int len, void *unused)
198 : {
199 0 : memset((void *) addr, 0, len);
200 0 : return 0;
201 : }
202 :
203 0 : unsigned long __clear_user(void __user *mem, unsigned long len)
204 : {
205 0 : return buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL);
206 : }
207 : EXPORT_SYMBOL(__clear_user);
208 :
209 0 : static int strnlen_chunk(unsigned long str, int len, void *arg)
210 : {
211 0 : int *len_ptr = arg, n;
212 :
213 0 : n = strnlen((void *) str, len);
214 0 : *len_ptr += n;
215 :
216 0 : if (n < len)
217 : return 1;
218 0 : return 0;
219 : }
220 :
221 0 : long strnlen_user(const char __user *str, long len)
222 : {
223 0 : int count = 0, n;
224 :
225 0 : if (!access_ok(str, 1))
226 : return -EFAULT;
227 0 : n = buffer_op((unsigned long) str, len, 0, strnlen_chunk, &count);
228 0 : if (n == 0)
229 0 : return count + 1;
230 : return 0;
231 : }
232 : EXPORT_SYMBOL(strnlen_user);
233 :
234 : /**
235 : * arch_futex_atomic_op_inuser() - Atomic arithmetic operation with constant
236 : * argument and comparison of the previous
237 : * futex value with another constant.
238 : *
239 : * @encoded_op: encoded operation to execute
240 : * @uaddr: pointer to user space address
241 : *
242 : * Return:
243 : * 0 - On success
244 : * -EFAULT - User access resulted in a page fault
245 : * -EAGAIN - Atomic operation was unable to complete due to contention
246 : * -ENOSYS - Operation not supported
247 : */
248 :
249 0 : int arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
250 : {
251 : int oldval, ret;
252 : struct page *page;
253 0 : unsigned long addr = (unsigned long) uaddr;
254 : pte_t *pte;
255 :
256 0 : ret = -EFAULT;
257 0 : if (!access_ok(uaddr, sizeof(*uaddr)))
258 : return -EFAULT;
259 0 : preempt_disable();
260 0 : pte = maybe_map(addr, 1);
261 0 : if (pte == NULL)
262 : goto out_inuser;
263 :
264 0 : page = pte_page(*pte);
265 : #ifdef CONFIG_64BIT
266 0 : pagefault_disable();
267 0 : addr = (unsigned long) page_address(page) +
268 0 : (((unsigned long) addr) & ~PAGE_MASK);
269 : #else
270 : addr = (unsigned long) kmap_atomic(page) +
271 : ((unsigned long) addr & ~PAGE_MASK);
272 : #endif
273 0 : uaddr = (u32 *) addr;
274 0 : oldval = *uaddr;
275 :
276 0 : ret = 0;
277 :
278 0 : switch (op) {
279 : case FUTEX_OP_SET:
280 0 : *uaddr = oparg;
281 0 : break;
282 : case FUTEX_OP_ADD:
283 0 : *uaddr += oparg;
284 0 : break;
285 : case FUTEX_OP_OR:
286 0 : *uaddr |= oparg;
287 0 : break;
288 : case FUTEX_OP_ANDN:
289 0 : *uaddr &= ~oparg;
290 0 : break;
291 : case FUTEX_OP_XOR:
292 0 : *uaddr ^= oparg;
293 0 : break;
294 : default:
295 : ret = -ENOSYS;
296 : }
297 : #ifdef CONFIG_64BIT
298 : pagefault_enable();
299 : #else
300 : kunmap_atomic((void *)addr);
301 : #endif
302 :
303 : out_inuser:
304 0 : preempt_enable();
305 :
306 0 : if (ret == 0)
307 0 : *oval = oldval;
308 :
309 : return ret;
310 : }
311 : EXPORT_SYMBOL(arch_futex_atomic_op_inuser);
312 :
313 : /**
314 : * futex_atomic_cmpxchg_inatomic() - Compare and exchange the content of the
315 : * uaddr with newval if the current value is
316 : * oldval.
317 : * @uval: pointer to store content of @uaddr
318 : * @uaddr: pointer to user space address
319 : * @oldval: old value
320 : * @newval: new value to store to @uaddr
321 : *
322 : * Return:
323 : * 0 - On success
324 : * -EFAULT - User access resulted in a page fault
325 : * -EAGAIN - Atomic operation was unable to complete due to contention
326 : */
327 :
328 0 : int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
329 : u32 oldval, u32 newval)
330 : {
331 : struct page *page;
332 : pte_t *pte;
333 0 : int ret = -EFAULT;
334 :
335 0 : if (!access_ok(uaddr, sizeof(*uaddr)))
336 : return -EFAULT;
337 :
338 0 : preempt_disable();
339 0 : pte = maybe_map((unsigned long) uaddr, 1);
340 0 : if (pte == NULL)
341 : goto out_inatomic;
342 :
343 0 : page = pte_page(*pte);
344 : #ifdef CONFIG_64BIT
345 0 : pagefault_disable();
346 0 : uaddr = page_address(page) + (((unsigned long) uaddr) & ~PAGE_MASK);
347 : #else
348 : uaddr = kmap_atomic(page) + ((unsigned long) uaddr & ~PAGE_MASK);
349 : #endif
350 :
351 0 : *uval = *uaddr;
352 :
353 0 : ret = cmpxchg(uaddr, oldval, newval);
354 :
355 : #ifdef CONFIG_64BIT
356 0 : pagefault_enable();
357 : #else
358 : kunmap_atomic(uaddr);
359 : #endif
360 0 : ret = 0;
361 :
362 : out_inatomic:
363 0 : preempt_enable();
364 0 : return ret;
365 : }
366 : EXPORT_SYMBOL(futex_atomic_cmpxchg_inatomic);
|