| File: | obj-scan-build/../kern/syscall_emulation.c |
| Location: | line 404, column 2 |
| Description: | Null pointer argument in call to memory copy function |
| 1 | /* | |||
| 2 | * Mach Operating System | |||
| 3 | * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University | |||
| 4 | * All Rights Reserved. | |||
| 5 | * | |||
| 6 | * Permission to use, copy, modify and distribute this software and its | |||
| 7 | * documentation is hereby granted, provided that both the copyright | |||
| 8 | * notice and this permission notice appear in all copies of the | |||
| 9 | * software, derivative works or modified versions, and any portions | |||
| 10 | * thereof, and that both notices appear in supporting documentation. | |||
| 11 | * | |||
| 12 | * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" | |||
| 13 | * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR | |||
| 14 | * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. | |||
| 15 | * | |||
| 16 | * Carnegie Mellon requests users of this software to return to | |||
| 17 | * | |||
| 18 | * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU | |||
| 19 | * School of Computer Science | |||
| 20 | * Carnegie Mellon University | |||
| 21 | * Pittsburgh PA 15213-3890 | |||
| 22 | * | |||
| 23 | * any improvements or extensions that they make and grant Carnegie Mellon | |||
| 24 | * the rights to redistribute these changes. | |||
| 25 | */ | |||
| 26 | ||||
| 27 | #include <string.h> | |||
| 28 | ||||
| 29 | #include <mach/error.h> | |||
| 30 | #include <mach/vm_param.h> | |||
| 31 | #include <kern/syscall_emulation.h> | |||
| 32 | #include <kern/task.h> | |||
| 33 | #include <kern/kalloc.h> | |||
| 34 | #include <vm/vm_kern.h> | |||
| 35 | ||||
| 36 | /* XXX */ | |||
| 37 | #define syscall_emulation_sync(task) | |||
| 38 | ||||
| 39 | ||||
| 40 | ||||
| 41 | /* | |||
| 42 | * WARNING: | |||
| 43 | * This code knows that kalloc() allocates memory most efficiently | |||
| 44 | * in sizes that are powers of 2, and asks for those sizes. | |||
| 45 | */ | |||
| 46 | ||||
| 47 | /* | |||
| 48 | * Go from number of entries to size of struct eml_dispatch and back. | |||
| 49 | */ | |||
| 50 | #define base_size(sizeof(struct eml_dispatch) - sizeof(eml_routine_t)) (sizeof(struct eml_dispatch) - sizeof(eml_routine_t)) | |||
| 51 | #define count_to_size(count)((sizeof(struct eml_dispatch) - sizeof(eml_routine_t)) + sizeof (vm_offset_t) * (count)) \ | |||
| 52 | (base_size(sizeof(struct eml_dispatch) - sizeof(eml_routine_t)) + sizeof(vm_offset_t) * (count)) | |||
| 53 | ||||
| 54 | #define size_to_count(size)( ((size) - (sizeof(struct eml_dispatch) - sizeof(eml_routine_t ))) / sizeof(vm_offset_t) ) \ | |||
| 55 | ( ((size) - base_size(sizeof(struct eml_dispatch) - sizeof(eml_routine_t))) / sizeof(vm_offset_t) ) | |||
| 56 | ||||
| 57 | /* | |||
| 58 | * eml_init: initialize user space emulation code | |||
| 59 | */ | |||
| 60 | void eml_init() | |||
| 61 | { | |||
| 62 | } | |||
| 63 | ||||
| 64 | /* | |||
| 65 | * eml_task_reference() [Exported] | |||
| 66 | * | |||
| 67 | * Bumps the reference count on the common emulation | |||
| 68 | * vector. | |||
| 69 | */ | |||
| 70 | ||||
| 71 | void eml_task_reference(task, parent) | |||
| 72 | task_t task, parent; | |||
| 73 | { | |||
| 74 | eml_dispatch_t eml; | |||
| 75 | ||||
| 76 | if (parent == TASK_NULL((task_t) 0)) | |||
| 77 | eml = EML_DISPATCH_NULL(eml_dispatch_t)0; | |||
| 78 | else | |||
| 79 | eml = parent->eml_dispatch; | |||
| 80 | ||||
| 81 | if (eml != EML_DISPATCH_NULL(eml_dispatch_t)0) { | |||
| 82 | simple_lock(&eml->lock); | |||
| 83 | eml->ref_count++; | |||
| 84 | simple_unlock(&eml->lock); | |||
| 85 | } | |||
| 86 | task->eml_dispatch = eml; | |||
| 87 | } | |||
| 88 | ||||
| 89 | ||||
| 90 | /* | |||
| 91 | * eml_task_deallocate() [Exported] | |||
| 92 | * | |||
| 93 | * Cleans up after the emulation code when a process exits. | |||
| 94 | */ | |||
| 95 | ||||
| 96 | void eml_task_deallocate(task) | |||
| 97 | task_t task; | |||
| 98 | { | |||
| 99 | eml_dispatch_t eml; | |||
| 100 | ||||
| 101 | eml = task->eml_dispatch; | |||
| 102 | if (eml != EML_DISPATCH_NULL(eml_dispatch_t)0) { | |||
| 103 | int count; | |||
| 104 | ||||
| 105 | simple_lock(&eml->lock); | |||
| 106 | count = --eml->ref_count; | |||
| 107 | simple_unlock(&eml->lock); | |||
| 108 | ||||
| 109 | if (count == 0) | |||
| 110 | kfree((vm_offset_t)eml, count_to_size(eml->disp_count)((sizeof(struct eml_dispatch) - sizeof(eml_routine_t)) + sizeof (vm_offset_t) * (eml->disp_count))); | |||
| 111 | } | |||
| 112 | } | |||
| 113 | ||||
| 114 | /* | |||
| 115 | * task_set_emulation_vector: [Server Entry] | |||
| 116 | * set a list of emulated system calls for this task. | |||
| 117 | */ | |||
| 118 | kern_return_t | |||
| 119 | task_set_emulation_vector_internal(task, vector_start, emulation_vector, | |||
| 120 | emulation_vector_count) | |||
| 121 | task_t task; | |||
| 122 | int vector_start; | |||
| 123 | emulation_vector_t emulation_vector; | |||
| 124 | unsigned int emulation_vector_count; | |||
| 125 | { | |||
| 126 | eml_dispatch_t cur_eml, new_eml, old_eml; | |||
| 127 | vm_size_t new_size; | |||
| 128 | int cur_start, cur_end; | |||
| 129 | int new_start = 0, new_end = 0; | |||
| 130 | int vector_end; | |||
| 131 | ||||
| 132 | if (task == TASK_NULL((task_t) 0)) | |||
| 133 | return EML_BAD_TASK(((((0x0)&0x3f)<<26)|(((2)&0xfff)<<14))|0x0001 ); | |||
| 134 | ||||
| 135 | vector_end = vector_start + emulation_vector_count; | |||
| 136 | ||||
| 137 | /* | |||
| 138 | * We try to re-use the existing emulation vector | |||
| 139 | * if possible. We can reuse the vector if it | |||
| 140 | * is not shared with another task and if it is | |||
| 141 | * large enough to contain the entries we are | |||
| 142 | * supplying. | |||
| 143 | * | |||
| 144 | * We must grab the lock on the task to check whether | |||
| 145 | * there is an emulation vector. | |||
| 146 | * If the vector is shared or not large enough, we | |||
| 147 | * need to drop the lock and allocate a new emulation | |||
| 148 | * vector. | |||
| 149 | * | |||
| 150 | * While the lock is dropped, the emulation vector | |||
| 151 | * may be released by all other tasks (giving us | |||
| 152 | * exclusive use), or may be enlarged by another | |||
| 153 | * task_set_emulation_vector call. Therefore, | |||
| 154 | * after allocating the new emulation vector, we | |||
| 155 | * must grab the lock again to check whether we | |||
| 156 | * really need the new vector we just allocated. | |||
| 157 | * | |||
| 158 | * Since an emulation vector cannot be altered | |||
| 159 | * if it is in use by more than one task, the | |||
| 160 | * task lock is sufficient to protect the vector`s | |||
| 161 | * start, count, and contents. The lock in the | |||
| 162 | * vector protects only the reference count. | |||
| 163 | */ | |||
| 164 | ||||
| 165 | old_eml = EML_DISPATCH_NULL(eml_dispatch_t)0; /* vector to discard */ | |||
| 166 | new_eml = EML_DISPATCH_NULL(eml_dispatch_t)0; /* new vector */ | |||
| 167 | ||||
| 168 | for (;;) { | |||
| 169 | /* | |||
| 170 | * Find the current emulation vector. | |||
| 171 | * See whether we can overwrite it. | |||
| 172 | */ | |||
| 173 | task_lock(task); | |||
| 174 | cur_eml = task->eml_dispatch; | |||
| 175 | if (cur_eml != EML_DISPATCH_NULL(eml_dispatch_t)0) { | |||
| 176 | cur_start = cur_eml->disp_min; | |||
| 177 | cur_end = cur_eml->disp_count + cur_start; | |||
| 178 | ||||
| 179 | simple_lock(&cur_eml->lock); | |||
| 180 | if (cur_eml->ref_count == 1 && | |||
| 181 | cur_start <= vector_start && | |||
| 182 | cur_end >= vector_end) | |||
| 183 | { | |||
| 184 | /* | |||
| 185 | * Can use the existing emulation vector. | |||
| 186 | * Discard any new one we allocated. | |||
| 187 | */ | |||
| 188 | simple_unlock(&cur_eml->lock); | |||
| 189 | old_eml = new_eml; | |||
| 190 | break; | |||
| 191 | } | |||
| 192 | ||||
| 193 | if (new_eml != EML_DISPATCH_NULL(eml_dispatch_t)0 && | |||
| 194 | new_start <= cur_start && | |||
| 195 | new_end >= cur_end) | |||
| 196 | { | |||
| 197 | /* | |||
| 198 | * A new vector was allocated, and it is large enough | |||
| 199 | * to hold all the entries from the current vector. | |||
| 200 | * Copy the entries to the new emulation vector, | |||
| 201 | * deallocate the current one, and use the new one. | |||
| 202 | */ | |||
| 203 | memcpy(&new_eml->disp_vector[cur_start-new_start], | |||
| 204 | &cur_eml->disp_vector[0], | |||
| 205 | cur_eml->disp_count * sizeof(vm_offset_t)); | |||
| 206 | ||||
| 207 | if (--cur_eml->ref_count == 0) | |||
| 208 | old_eml = cur_eml; /* discard old vector */ | |||
| 209 | simple_unlock(&cur_eml->lock); | |||
| 210 | ||||
| 211 | task->eml_dispatch = new_eml; | |||
| 212 | syscall_emulation_sync(task); | |||
| 213 | cur_eml = new_eml; | |||
| 214 | break; | |||
| 215 | } | |||
| 216 | simple_unlock(&cur_eml->lock); | |||
| 217 | ||||
| 218 | /* | |||
| 219 | * Need a new emulation vector. | |||
| 220 | * Ensure it will hold all the entries from | |||
| 221 | * both the old and new emulation vectors. | |||
| 222 | */ | |||
| 223 | new_start = vector_start; | |||
| 224 | if (new_start > cur_start) | |||
| 225 | new_start = cur_start; | |||
| 226 | new_end = vector_end; | |||
| 227 | if (new_end < cur_end) | |||
| 228 | new_end = cur_end; | |||
| 229 | } | |||
| 230 | else { | |||
| 231 | /* | |||
| 232 | * There is no current emulation vector. | |||
| 233 | * If a new one was allocated, use it. | |||
| 234 | */ | |||
| 235 | if (new_eml != EML_DISPATCH_NULL(eml_dispatch_t)0) { | |||
| 236 | task->eml_dispatch = new_eml; | |||
| 237 | cur_eml = new_eml; | |||
| 238 | break; | |||
| 239 | } | |||
| 240 | ||||
| 241 | /* | |||
| 242 | * Compute the size needed for the new vector. | |||
| 243 | */ | |||
| 244 | new_start = vector_start; | |||
| 245 | new_end = vector_end; | |||
| 246 | } | |||
| 247 | ||||
| 248 | /* | |||
| 249 | * Have no vector (or one that is no longer large enough). | |||
| 250 | * Drop all the locks and allocate a new vector. | |||
| 251 | * Repeat the loop to check whether the old vector was | |||
| 252 | * changed while we didn`t hold the locks. | |||
| 253 | */ | |||
| 254 | ||||
| 255 | task_unlock(task); | |||
| 256 | ||||
| 257 | if (new_eml != EML_DISPATCH_NULL(eml_dispatch_t)0) | |||
| 258 | kfree((vm_offset_t)new_eml, count_to_size(new_eml->disp_count)((sizeof(struct eml_dispatch) - sizeof(eml_routine_t)) + sizeof (vm_offset_t) * (new_eml->disp_count))); | |||
| 259 | ||||
| 260 | new_size = count_to_size(new_end - new_start)((sizeof(struct eml_dispatch) - sizeof(eml_routine_t)) + sizeof (vm_offset_t) * (new_end - new_start)); | |||
| 261 | new_eml = (eml_dispatch_t) kalloc(new_size); | |||
| 262 | ||||
| 263 | memset(new_eml, 0, new_size); | |||
| 264 | simple_lock_init(&new_eml->lock); | |||
| 265 | new_eml->ref_count = 1; | |||
| 266 | new_eml->disp_min = new_start; | |||
| 267 | new_eml->disp_count = new_end - new_start; | |||
| 268 | ||||
| 269 | continue; | |||
| 270 | } | |||
| 271 | ||||
| 272 | /* | |||
| 273 | * We have the emulation vector. | |||
| 274 | * Install the new emulation entries. | |||
| 275 | */ | |||
| 276 | memcpy(&cur_eml->disp_vector[vector_start - cur_eml->disp_min], | |||
| 277 | &emulation_vector[0], | |||
| 278 | emulation_vector_count * sizeof(vm_offset_t)); | |||
| 279 | ||||
| 280 | task_unlock(task); | |||
| 281 | ||||
| 282 | /* | |||
| 283 | * Discard any old emulation vector we don`t need. | |||
| 284 | */ | |||
| 285 | if (old_eml) | |||
| 286 | kfree((vm_offset_t) old_eml, count_to_size(old_eml->disp_count)((sizeof(struct eml_dispatch) - sizeof(eml_routine_t)) + sizeof (vm_offset_t) * (old_eml->disp_count))); | |||
| 287 | ||||
| 288 | return KERN_SUCCESS0; | |||
| 289 | } | |||
| 290 | ||||
| 291 | /* | |||
| 292 | * task_set_emulation_vector: [Server Entry] | |||
| 293 | * | |||
| 294 | * Set the list of emulated system calls for this task. | |||
| 295 | * The list is out-of-line. | |||
| 296 | */ | |||
| 297 | kern_return_t | |||
| 298 | task_set_emulation_vector(task, vector_start, emulation_vector, | |||
| 299 | emulation_vector_count) | |||
| 300 | task_t task; | |||
| 301 | int vector_start; | |||
| 302 | emulation_vector_t emulation_vector; | |||
| 303 | unsigned int emulation_vector_count; | |||
| 304 | { | |||
| 305 | kern_return_t kr; | |||
| 306 | vm_offset_t emul_vector_addr; | |||
| 307 | ||||
| 308 | if (task == TASK_NULL((task_t) 0)) | |||
| 309 | return EML_BAD_TASK(((((0x0)&0x3f)<<26)|(((2)&0xfff)<<14))|0x0001 ); /* XXX sb KERN_INVALID_ARGUMENT */ | |||
| 310 | ||||
| 311 | /* | |||
| 312 | * The emulation vector is really a vm_map_copy_t. | |||
| 313 | */ | |||
| 314 | kr = vm_map_copyout(ipc_kernel_map, &emul_vector_addr, | |||
| 315 | (vm_map_copy_t) emulation_vector); | |||
| 316 | if (kr != KERN_SUCCESS0) | |||
| 317 | return kr; | |||
| 318 | ||||
| 319 | /* | |||
| 320 | * Do the work. | |||
| 321 | */ | |||
| 322 | kr = task_set_emulation_vector_internal( | |||
| 323 | task, | |||
| 324 | vector_start, | |||
| 325 | (emulation_vector_t) emul_vector_addr, | |||
| 326 | emulation_vector_count); | |||
| 327 | ||||
| 328 | /* | |||
| 329 | * Discard the memory | |||
| 330 | */ | |||
| 331 | (void) kmem_free(ipc_kernel_map, | |||
| 332 | emul_vector_addr, | |||
| 333 | emulation_vector_count * sizeof(eml_dispatch_t)); | |||
| 334 | ||||
| 335 | return kr; | |||
| 336 | } | |||
| 337 | ||||
| 338 | /* | |||
| 339 | * task_get_emulation_vector: [Server Entry] | |||
| 340 | * | |||
| 341 | * Get the list of emulated system calls for this task. | |||
| 342 | * List is returned out-of-line. | |||
| 343 | */ | |||
| 344 | kern_return_t | |||
| 345 | task_get_emulation_vector(task, vector_start, emulation_vector, | |||
| 346 | emulation_vector_count) | |||
| 347 | task_t task; | |||
| 348 | int *vector_start; /* out */ | |||
| 349 | emulation_vector_t *emulation_vector; /* out */ | |||
| 350 | unsigned int *emulation_vector_count; /* out */ | |||
| 351 | { | |||
| 352 | eml_dispatch_t eml; | |||
| 353 | vm_size_t vector_size, size; | |||
| 354 | vm_offset_t addr; | |||
| 355 | ||||
| 356 | if (task == TASK_NULL((task_t) 0)) | |||
| ||||
| 357 | return EML_BAD_TASK(((((0x0)&0x3f)<<26)|(((2)&0xfff)<<14))|0x0001 ); | |||
| 358 | ||||
| 359 | addr = 0; | |||
| 360 | size = 0; | |||
| 361 | ||||
| 362 | for(;;) { | |||
| 363 | vm_size_t size_needed; | |||
| 364 | ||||
| 365 | task_lock(task); | |||
| 366 | eml = task->eml_dispatch; | |||
| 367 | if (eml == EML_DISPATCH_NULL(eml_dispatch_t)0) { | |||
| 368 | task_unlock(task); | |||
| 369 | if (addr) | |||
| 370 | (void) kmem_free(ipc_kernel_map, addr, size); | |||
| 371 | *vector_start = 0; | |||
| 372 | *emulation_vector = 0; | |||
| 373 | *emulation_vector_count = 0; | |||
| 374 | return KERN_SUCCESS0; | |||
| 375 | } | |||
| 376 | ||||
| 377 | /* | |||
| 378 | * Do we have the memory we need? | |||
| 379 | */ | |||
| 380 | vector_size = eml->disp_count * sizeof(vm_offset_t); | |||
| 381 | ||||
| 382 | size_needed = round_page(vector_size)((vm_offset_t)((((vm_offset_t)(vector_size)) + ((1 << 12 )-1)) & ~((1 << 12)-1))); | |||
| 383 | if (size_needed <= size) | |||
| 384 | break; | |||
| 385 | ||||
| 386 | /* | |||
| 387 | * If not, unlock the task and allocate more memory. | |||
| 388 | */ | |||
| 389 | task_unlock(task); | |||
| 390 | ||||
| 391 | if (size != 0) | |||
| 392 | kmem_free(ipc_kernel_map, addr, size); | |||
| 393 | ||||
| 394 | size = size_needed; | |||
| 395 | if (kmem_alloc(ipc_kernel_map, &addr, size) != KERN_SUCCESS0) | |||
| 396 | return KERN_RESOURCE_SHORTAGE6; | |||
| 397 | } | |||
| 398 | ||||
| 399 | /* | |||
| 400 | * Copy out the dispatch addresses | |||
| 401 | */ | |||
| 402 | *vector_start = eml->disp_min; | |||
| 403 | *emulation_vector_count = eml->disp_count; | |||
| 404 | memcpy((void *)addr, | |||
| ||||
| 405 | eml->disp_vector, | |||
| 406 | vector_size); | |||
| 407 | ||||
| 408 | /* | |||
| 409 | * Unlock the task and free any memory we did not need | |||
| 410 | */ | |||
| 411 | task_unlock(task); | |||
| 412 | ||||
| 413 | { | |||
| 414 | vm_size_t size_used, size_left; | |||
| 415 | vm_map_copy_t memory; | |||
| 416 | ||||
| 417 | /* | |||
| 418 | * Free any unused memory beyond the end of the last page used | |||
| 419 | */ | |||
| 420 | size_used = round_page(vector_size)((vm_offset_t)((((vm_offset_t)(vector_size)) + ((1 << 12 )-1)) & ~((1 << 12)-1))); | |||
| 421 | if (size_used != size) | |||
| 422 | (void) kmem_free(ipc_kernel_map, | |||
| 423 | addr + size_used, | |||
| 424 | size - size_used); | |||
| 425 | ||||
| 426 | /* | |||
| 427 | * Zero the remainder of the page being returned. | |||
| 428 | */ | |||
| 429 | size_left = size_used - vector_size; | |||
| 430 | if (size_left > 0) | |||
| 431 | memset((char *)addr + vector_size, 0, size_left); | |||
| 432 | ||||
| 433 | /* | |||
| 434 | * Make memory into copyin form - this unwires it. | |||
| 435 | */ | |||
| 436 | (void) vm_map_copyin(ipc_kernel_map, addr, vector_size, TRUE((boolean_t) 1), &memory); | |||
| 437 | ||||
| 438 | *emulation_vector = (emulation_vector_t) memory; | |||
| 439 | } | |||
| 440 | ||||
| 441 | return KERN_SUCCESS0; | |||
| 442 | } | |||
| 443 | ||||
| 444 | /* | |||
| 445 | * task_set_emulation: [Server Entry] | |||
| 446 | * set up for user space emulation of syscalls within this task. | |||
| 447 | */ | |||
| 448 | kern_return_t task_set_emulation(task, routine_entry_pt, routine_number) | |||
| 449 | task_t task; | |||
| 450 | vm_offset_t routine_entry_pt; | |||
| 451 | int routine_number; | |||
| 452 | { | |||
| 453 | return task_set_emulation_vector_internal(task, routine_number, | |||
| 454 | &routine_entry_pt, 1); | |||
| 455 | } |