source: mainline/kernel/generic/src/ddi/ddi.c@ 3e7948c

Last change on this file since 3e7948c was 07d4271, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 17 months ago

Fix some unsound task reference manipulation and locking

In some operations that take task ID as an argument,
there's a possibility of the task being destroyed mid-operation
and a subsequent use-after-free situation.
As a general solution, task_find_by_id() is reimplemented to
check for this situation and always return a valid strong reference.
The callers then only need to handle the reference itself, and
don't need to concern themselves with tasks_lock.

  • Property mode set to 100644
File size: 13.6 KB
Line 
1/*
2 * Copyright (c) 2006 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup kernel_generic_ddi
30 * @{
31 */
32
33/**
34 * @file
35 * @brief Device Driver Interface functions.
36 *
37 * This file contains functions that comprise the Device Driver Interface.
38 * These are the functions for mapping physical memory and enabling I/O
39 * space to tasks.
40 */
41
42#include <assert.h>
43#include <ddi/ddi.h>
44#include <proc/task.h>
45#include <security/perm.h>
46#include <mm/frame.h>
47#include <mm/as.h>
48#include <mm/km.h>
49#include <mm/page.h>
50#include <synch/mutex.h>
51#include <syscall/copy.h>
52#include <adt/odict.h>
53#include <arch.h>
54#include <align.h>
55#include <errno.h>
56#include <memw.h>
57#include <trace.h>
58#include <bitops.h>
59#include <arch/asm.h>
60
61/** This lock protects the @c pareas ordered dictionary. */
62static mutex_t pareas_lock;
63
64/** Ordered dictionary of enabled physical memory areas by base address. */
65static odict_t pareas;
66
67static void *pareas_getkey(odlink_t *);
68static int pareas_cmp(void *, void *);
69
70/** Initialize DDI.
71 *
72 */
73void ddi_init(void)
74{
75 odict_initialize(&pareas, pareas_getkey, pareas_cmp);
76 mutex_initialize(&pareas_lock, MUTEX_PASSIVE);
77}
78
79/** Initialize physical area structure.
80 *
81 * This should always be called first on the parea structure before
82 * filling in fields and calling ddi_parea_register.
83 *
84 * @param parea Pointer to physical area structure.
85 *
86 */
87void ddi_parea_init(parea_t *parea)
88{
89 memset(parea, 0, sizeof(parea_t));
90}
91
92/** Enable piece of physical memory for mapping by physmem_map().
93 *
94 * @param parea Pointer to physical area structure.
95 *
96 */
97void ddi_parea_register(parea_t *parea)
98{
99 mutex_lock(&pareas_lock);
100
101 /*
102 * We don't check for overlaps here as the kernel is pretty sane.
103 */
104 odict_insert(&parea->lpareas, &pareas, NULL);
105
106 mutex_unlock(&pareas_lock);
107}
108
109/** Norify physical area has been unmapped.
110 *
111 * @param parea Physical area
112 */
113void ddi_parea_unmap_notify(parea_t *parea)
114{
115 parea->mapped = false;
116 if (parea->mapped_changed != NULL)
117 parea->mapped_changed(parea->arg);
118}
119
120/** Map piece of physical memory into virtual address space of current task.
121 *
122 * @param phys Physical address of the starting frame.
123 * @param pages Number of pages to map.
124 * @param flags Address space area flags for the mapping.
125 * @param virt Virtual address of the starting page.
126 * @param bound Lowest virtual address bound.
127 *
128 * @return EOK on success.
129 * @return EPERM if the caller lacks permissions to use this syscall.
130 * @return EBADMEM if phys is not page aligned.
131 * @return ENOENT if there is no task matching the specified ID or
132 * the physical address space is not enabled for mapping.
133 * @return ENOMEM if there was a problem in creating address space area.
134 *
135 */
136_NO_TRACE static errno_t physmem_map(uintptr_t phys, size_t pages,
137 unsigned int flags, uintptr_t *virt, uintptr_t bound)
138{
139 assert(TASK);
140
141 if ((phys % FRAME_SIZE) != 0)
142 return EBADMEM;
143
144 /*
145 * Unprivileged tasks are only allowed to map pareas
146 * which are explicitly marked as such.
147 */
148 bool priv =
149 ((perm_get(TASK) & PERM_MEM_MANAGER) == PERM_MEM_MANAGER);
150
151 mem_backend_data_t backend_data;
152 backend_data.base = phys;
153 backend_data.frames = pages;
154 backend_data.anonymous = false;
155
156 /*
157 * Check if the memory region is explicitly enabled
158 * for mapping by any parea structure.
159 */
160
161 mutex_lock(&pareas_lock);
162 odlink_t *odlink = odict_find_eq(&pareas, &phys, NULL);
163 parea_t *parea = odlink != NULL ?
164 odict_get_instance(odlink, parea_t, lpareas) : NULL;
165
166 if ((parea != NULL) && (parea->frames >= pages)) {
167 if ((!priv) && (!parea->unpriv)) {
168 mutex_unlock(&pareas_lock);
169 return EPERM;
170 }
171
172 goto map;
173 }
174
175 parea = NULL;
176 mutex_unlock(&pareas_lock);
177
178 /*
179 * Check if the memory region is part of physical
180 * memory generally enabled for mapping.
181 */
182
183 irq_spinlock_lock(&zones.lock, true);
184 size_t znum = find_zone(ADDR2PFN(phys), pages, 0);
185
186 if (znum == (size_t) -1) {
187 /*
188 * Frames not found in any zone
189 * -> assume it is a hardware device and allow mapping
190 * for privileged tasks.
191 */
192 irq_spinlock_unlock(&zones.lock, true);
193
194 if (!priv)
195 return EPERM;
196
197 goto map;
198 }
199
200 if (zones.info[znum].flags & (ZONE_FIRMWARE | ZONE_RESERVED)) {
201 /*
202 * Frames are part of firmware or reserved zone
203 * -> allow mapping for privileged tasks.
204 */
205 irq_spinlock_unlock(&zones.lock, true);
206
207 if (!priv)
208 return EPERM;
209
210 goto map;
211 }
212
213 irq_spinlock_unlock(&zones.lock, true);
214 return ENOENT;
215
216map:
217 backend_data.parea = parea;
218
219 if (!as_area_create(TASK->as, flags, FRAMES2SIZE(pages),
220 AS_AREA_ATTR_NONE, &phys_backend, &backend_data, virt, bound)) {
221 /*
222 * The address space area was not created.
223 * We report it using ENOMEM.
224 */
225
226 if (parea != NULL)
227 mutex_unlock(&pareas_lock);
228
229 return ENOMEM;
230 }
231
232 /*
233 * Mapping is created on-demand during page fault.
234 */
235
236 if (parea != NULL) {
237 parea->mapped = true;
238 mutex_unlock(&pareas_lock);
239 }
240
241 return EOK;
242}
243
244_NO_TRACE static errno_t physmem_unmap(uintptr_t virt)
245{
246 assert(TASK);
247
248 return as_area_destroy(TASK->as, virt);
249}
250
251/** Wrapper for SYS_PHYSMEM_MAP syscall.
252 *
253 * @param phys Physical base address to map
254 * @param pages Number of pages
255 * @param flags Flags of newly mapped pages
256 * @param virt_ptr Destination virtual address
257 * @param bound Lowest virtual address bound.
258 *
259 * @return 0 on success, otherwise it returns error code found in errno.h
260 *
261 */
262sys_errno_t sys_physmem_map(uintptr_t phys, size_t pages, unsigned int flags,
263 uspace_ptr_uintptr_t virt_ptr, uintptr_t bound)
264{
265 uintptr_t virt;
266 errno_t rc = copy_from_uspace(&virt, virt_ptr, sizeof(virt));
267 if (rc != EOK)
268 return rc;
269
270 rc = physmem_map(ALIGN_DOWN(phys, FRAME_SIZE), pages, flags, &virt,
271 bound);
272 if (rc != EOK)
273 return rc;
274
275 rc = copy_to_uspace(virt_ptr, &virt, sizeof(virt));
276 if (rc != EOK) {
277 physmem_unmap(virt);
278 return rc;
279 }
280
281 return EOK;
282}
283
284sys_errno_t sys_physmem_unmap(uintptr_t virt)
285{
286 return physmem_unmap(virt);
287}
288
289/** Get key function for the @c pareas ordered dictionary.
290 *
291 * @param odlink Link
292 * @return Pointer to base address cast as 'void *'
293 */
294static void *pareas_getkey(odlink_t *odlink)
295{
296 parea_t *parea = odict_get_instance(odlink, parea_t, lpareas);
297 return (void *) &parea->pbase;
298}
299
300/** Key comparison function for the @c pareas ordered dictionary.
301 *
302 * @param a Pointer to parea A base
303 * @param b Pointer to parea B base
304 * @return -1, 0, 1 iff base of A is less than, equal to, greater than B
305 */
306static int pareas_cmp(void *a, void *b)
307{
308 uintptr_t pa = *(uintptr_t *)a;
309 uintptr_t pb = *(uintptr_t *)b;
310
311 if (pa < pb)
312 return -1;
313 else if (pa == pb)
314 return 0;
315 else
316 return +1;
317}
318
319/** Enable range of I/O space for task.
320 *
321 * @param id Task ID of the destination task.
322 * @param ioaddr Starting I/O address.
323 * @param size Size of the enabled I/O space.
324 *
325 * @return 0 on success, EPERM if the caller lacks permissions to use this
326 * syscall, ENOENT if there is no task matching the specified ID.
327 *
328 */
329_NO_TRACE static errno_t iospace_enable(task_id_t id, uintptr_t ioaddr, size_t size)
330{
331 /*
332 * Make sure the caller is authorised to make this syscall.
333 */
334 perm_t perms = perm_get(TASK);
335 if (!(perms & PERM_IO_MANAGER))
336 return EPERM;
337
338 task_t *task = task_find_by_id(id);
339
340 if (!task)
341 return ENOENT;
342
343 errno_t rc = ENOENT;
344
345 irq_spinlock_lock(&task->lock, true);
346
347 /* Check that the task belongs to the correct security context. */
348 if (container_check(CONTAINER, task->container))
349 rc = ddi_iospace_enable_arch(task, ioaddr, size);
350
351 irq_spinlock_unlock(&task->lock, true);
352 task_release(task);
353 return rc;
354}
355
356/** Disable range of I/O space for task.
357 *
358 * @param id Task ID of the destination task.
359 * @param ioaddr Starting I/O address.
360 * @param size Size of the enabled I/O space.
361 *
362 * @return 0 on success, EPERM if the caller lacks permissions to use this
363 * syscall, ENOENT if there is no task matching the specified ID.
364 *
365 */
366_NO_TRACE static errno_t iospace_disable(task_id_t id, uintptr_t ioaddr, size_t size)
367{
368 /*
369 * Make sure the caller is authorised to make this syscall.
370 */
371 perm_t perms = perm_get(TASK);
372 if (!(perms & PERM_IO_MANAGER))
373 return EPERM;
374
375 task_t *task = task_find_by_id(id);
376
377 if (!task)
378 return ENOENT;
379
380 errno_t rc = ENOENT;
381
382 irq_spinlock_lock(&task->lock, true);
383
384 /* Check that the task belongs to the correct security context. */
385 if (container_check(CONTAINER, task->container))
386 rc = ddi_iospace_disable_arch(task, ioaddr, size);
387
388 irq_spinlock_unlock(&task->lock, true);
389 task_release(task);
390 return rc;
391}
392
393/** Wrapper for SYS_ENABLE_IOSPACE syscall.
394 *
395 * @param uspace_io_arg User space address of DDI argument structure.
396 *
397 * @return 0 on success, otherwise it returns error code found in errno.h
398 *
399 */
400sys_errno_t sys_iospace_enable(uspace_ptr_ddi_ioarg_t uspace_io_arg)
401{
402 ddi_ioarg_t arg;
403 errno_t rc = copy_from_uspace(&arg, uspace_io_arg, sizeof(ddi_ioarg_t));
404 if (rc != EOK)
405 return (sys_errno_t) rc;
406
407 return (sys_errno_t) iospace_enable((task_id_t) arg.task_id,
408 (uintptr_t) arg.ioaddr, (size_t) arg.size);
409}
410
411sys_errno_t sys_iospace_disable(uspace_ptr_ddi_ioarg_t uspace_io_arg)
412{
413 ddi_ioarg_t arg;
414 errno_t rc = copy_from_uspace(&arg, uspace_io_arg, sizeof(ddi_ioarg_t));
415 if (rc != EOK)
416 return (sys_errno_t) rc;
417
418 return (sys_errno_t) iospace_disable((task_id_t) arg.task_id,
419 (uintptr_t) arg.ioaddr, (size_t) arg.size);
420}
421
422_NO_TRACE static errno_t dmamem_map(uintptr_t virt, size_t size, unsigned int map_flags,
423 unsigned int flags, uintptr_t *phys)
424{
425 assert(TASK);
426
427 // TODO: implement locking of non-anonymous mapping
428 return page_find_mapping(virt, phys);
429}
430
431_NO_TRACE static errno_t dmamem_map_anonymous(size_t size, uintptr_t constraint,
432 unsigned int map_flags, unsigned int flags, uintptr_t *phys,
433 uintptr_t *virt, uintptr_t bound)
434{
435 assert(TASK);
436
437 size_t frames = SIZE2FRAMES(size);
438 if (frames == 0)
439 return EINVAL;
440
441 // FIXME: probably need to ensure that the memory is suitable for DMA
442 *phys = frame_alloc(frames, FRAME_ATOMIC, constraint);
443 if (*phys == 0)
444 return ENOMEM;
445
446 mem_backend_data_t backend_data;
447 backend_data.base = *phys;
448 backend_data.frames = frames;
449 backend_data.anonymous = true;
450 backend_data.parea = NULL;
451
452 if (!as_area_create(TASK->as, map_flags, size,
453 AS_AREA_ATTR_NONE, &phys_backend, &backend_data, virt, bound)) {
454 frame_free(*phys, frames);
455 return ENOMEM;
456 }
457
458 return EOK;
459}
460
461_NO_TRACE static errno_t dmamem_unmap(uintptr_t virt, size_t size)
462{
463 // TODO: implement unlocking & unmap
464 return EOK;
465}
466
467_NO_TRACE static errno_t dmamem_unmap_anonymous(uintptr_t virt)
468{
469 return as_area_destroy(TASK->as, virt);
470}
471
472sys_errno_t sys_dmamem_map(size_t size, unsigned int map_flags, unsigned int flags,
473 uspace_ptr_uintptr_t phys_ptr, uspace_ptr_uintptr_t virt_ptr, uintptr_t bound)
474{
475 if ((flags & DMAMEM_FLAGS_ANONYMOUS) == 0) {
476 /*
477 * Non-anonymous DMA mapping
478 */
479
480 uintptr_t phys;
481 errno_t rc = dmamem_map(virt_ptr, size, map_flags,
482 flags, &phys);
483
484 if (rc != EOK)
485 return rc;
486
487 rc = copy_to_uspace(phys_ptr, &phys, sizeof(phys));
488 if (rc != EOK) {
489 dmamem_unmap(virt_ptr, size);
490 return rc;
491 }
492 } else {
493 /*
494 * Anonymous DMA mapping
495 */
496
497 uintptr_t constraint;
498 errno_t rc = copy_from_uspace(&constraint, phys_ptr,
499 sizeof(constraint));
500 if (rc != EOK)
501 return rc;
502
503 uintptr_t virt;
504 rc = copy_from_uspace(&virt, virt_ptr, sizeof(virt));
505 if (rc != EOK)
506 return rc;
507
508 uintptr_t phys;
509 rc = dmamem_map_anonymous(size, constraint, map_flags, flags,
510 &phys, &virt, bound);
511 if (rc != EOK)
512 return rc;
513
514 rc = copy_to_uspace(phys_ptr, &phys, sizeof(phys));
515 if (rc != EOK) {
516 dmamem_unmap_anonymous(virt);
517 return rc;
518 }
519
520 rc = copy_to_uspace(virt_ptr, &virt, sizeof(virt));
521 if (rc != EOK) {
522 dmamem_unmap_anonymous(virt);
523 return rc;
524 }
525 }
526
527 return EOK;
528}
529
530sys_errno_t sys_dmamem_unmap(uintptr_t virt, size_t size, unsigned int flags)
531{
532 if ((flags & DMAMEM_FLAGS_ANONYMOUS) == 0)
533 return dmamem_unmap(virt, size);
534 else
535 return dmamem_unmap_anonymous(virt);
536}
537void *pio_map(void *phys, size_t size)
538{
539#ifdef IO_SPACE_BOUNDARY
540 if (phys < IO_SPACE_BOUNDARY)
541 return phys;
542#endif
543 return (void *) km_map((uintptr_t) phys, size, KM_NATURAL_ALIGNMENT,
544 PAGE_READ | PAGE_WRITE | PAGE_NOT_CACHEABLE);
545}
546
547void pio_unmap(void *phys, void *virt, size_t size)
548{
549#ifdef IO_SPACE_BOUNDARY
550 if (phys < IO_SPACE_BOUNDARY)
551 return;
552#endif
553 km_unmap((uintptr_t) virt, size);
554}
555
556/** @}
557 */
Note: See TracBrowser for help on using the repository browser.