File: | obj-scan-build/../kern/syscall_emulation.c |
Location: | line 402, 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(void) | |||
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( | |||
72 | task_t task, | |||
73 | task_t parent) | |||
74 | { | |||
75 | eml_dispatch_t eml; | |||
76 | ||||
77 | if (parent == TASK_NULL((task_t) 0)) | |||
78 | eml = EML_DISPATCH_NULL(eml_dispatch_t)0; | |||
79 | else | |||
80 | eml = parent->eml_dispatch; | |||
81 | ||||
82 | if (eml != EML_DISPATCH_NULL(eml_dispatch_t)0) { | |||
83 | simple_lock(&eml->lock); | |||
84 | eml->ref_count++; | |||
85 | simple_unlock(&eml->lock)((void)(&eml->lock)); | |||
86 | } | |||
87 | task->eml_dispatch = eml; | |||
88 | } | |||
89 | ||||
90 | ||||
91 | /* | |||
92 | * eml_task_deallocate() [Exported] | |||
93 | * | |||
94 | * Cleans up after the emulation code when a process exits. | |||
95 | */ | |||
96 | ||||
97 | void eml_task_deallocate(task) | |||
98 | const task_t task; | |||
99 | { | |||
100 | eml_dispatch_t eml; | |||
101 | ||||
102 | eml = task->eml_dispatch; | |||
103 | if (eml != EML_DISPATCH_NULL(eml_dispatch_t)0) { | |||
104 | int count; | |||
105 | ||||
106 | simple_lock(&eml->lock); | |||
107 | count = --eml->ref_count; | |||
108 | simple_unlock(&eml->lock)((void)(&eml->lock)); | |||
109 | ||||
110 | if (count == 0) | |||
111 | 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))); | |||
112 | } | |||
113 | } | |||
114 | ||||
115 | /* | |||
116 | * task_set_emulation_vector: [Server Entry] | |||
117 | * set a list of emulated system calls for this task. | |||
118 | */ | |||
119 | kern_return_t | |||
120 | task_set_emulation_vector_internal( | |||
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)((void)(&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)((void)(&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)((void)(&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)((void)(&(task)->lock)); | |||
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)((void)(&(task)->lock)); | |||
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( | |||
299 | task_t task, | |||
300 | int vector_start, | |||
301 | emulation_vector_t emulation_vector, | |||
302 | unsigned int emulation_vector_count) | |||
303 | { | |||
304 | kern_return_t kr; | |||
305 | vm_offset_t emul_vector_addr; | |||
306 | ||||
307 | if (task == TASK_NULL((task_t) 0)) | |||
308 | return EML_BAD_TASK(((((0x0)&0x3f)<<26)|(((2)&0xfff)<<14))|0x0001 ); /* XXX sb KERN_INVALID_ARGUMENT */ | |||
309 | ||||
310 | /* | |||
311 | * The emulation vector is really a vm_map_copy_t. | |||
312 | */ | |||
313 | kr = vm_map_copyout(ipc_kernel_map, &emul_vector_addr, | |||
314 | (vm_map_copy_t) emulation_vector); | |||
315 | if (kr != KERN_SUCCESS0) | |||
316 | return kr; | |||
317 | ||||
318 | /* | |||
319 | * Do the work. | |||
320 | */ | |||
321 | kr = task_set_emulation_vector_internal( | |||
322 | task, | |||
323 | vector_start, | |||
324 | (emulation_vector_t) emul_vector_addr, | |||
325 | emulation_vector_count); | |||
326 | ||||
327 | /* | |||
328 | * Discard the memory | |||
329 | */ | |||
330 | (void) kmem_free(ipc_kernel_map, | |||
331 | emul_vector_addr, | |||
332 | emulation_vector_count * sizeof(eml_dispatch_t)); | |||
333 | ||||
334 | return kr; | |||
335 | } | |||
336 | ||||
337 | /* | |||
338 | * task_get_emulation_vector: [Server Entry] | |||
339 | * | |||
340 | * Get the list of emulated system calls for this task. | |||
341 | * List is returned out-of-line. | |||
342 | */ | |||
343 | kern_return_t | |||
344 | task_get_emulation_vector( | |||
345 | task_t task, | |||
346 | int *vector_start, /* out */ | |||
347 | emulation_vector_t *emulation_vector, /* out */ | |||
348 | unsigned int *emulation_vector_count) /* out */ | |||
349 | { | |||
350 | eml_dispatch_t eml; | |||
351 | vm_size_t vector_size, size; | |||
352 | vm_offset_t addr; | |||
353 | ||||
354 | if (task == TASK_NULL((task_t) 0)) | |||
| ||||
355 | return EML_BAD_TASK(((((0x0)&0x3f)<<26)|(((2)&0xfff)<<14))|0x0001 ); | |||
356 | ||||
357 | addr = 0; | |||
358 | size = 0; | |||
359 | ||||
360 | for(;;) { | |||
361 | vm_size_t size_needed; | |||
362 | ||||
363 | task_lock(task); | |||
364 | eml = task->eml_dispatch; | |||
365 | if (eml == EML_DISPATCH_NULL(eml_dispatch_t)0) { | |||
366 | task_unlock(task)((void)(&(task)->lock)); | |||
367 | if (addr) | |||
368 | (void) kmem_free(ipc_kernel_map, addr, size); | |||
369 | *vector_start = 0; | |||
370 | *emulation_vector = 0; | |||
371 | *emulation_vector_count = 0; | |||
372 | return KERN_SUCCESS0; | |||
373 | } | |||
374 | ||||
375 | /* | |||
376 | * Do we have the memory we need? | |||
377 | */ | |||
378 | vector_size = eml->disp_count * sizeof(vm_offset_t); | |||
379 | ||||
380 | size_needed = round_page(vector_size)((vm_offset_t)((((vm_offset_t)(vector_size)) + ((1 << 12 )-1)) & ~((1 << 12)-1))); | |||
381 | if (size_needed <= size) | |||
382 | break; | |||
383 | ||||
384 | /* | |||
385 | * If not, unlock the task and allocate more memory. | |||
386 | */ | |||
387 | task_unlock(task)((void)(&(task)->lock)); | |||
388 | ||||
389 | if (size != 0) | |||
390 | kmem_free(ipc_kernel_map, addr, size); | |||
391 | ||||
392 | size = size_needed; | |||
393 | if (kmem_alloc(ipc_kernel_map, &addr, size) != KERN_SUCCESS0) | |||
394 | return KERN_RESOURCE_SHORTAGE6; | |||
395 | } | |||
396 | ||||
397 | /* | |||
398 | * Copy out the dispatch addresses | |||
399 | */ | |||
400 | *vector_start = eml->disp_min; | |||
401 | *emulation_vector_count = eml->disp_count; | |||
402 | memcpy((void *)addr, | |||
| ||||
403 | eml->disp_vector, | |||
404 | vector_size); | |||
405 | ||||
406 | /* | |||
407 | * Unlock the task and free any memory we did not need | |||
408 | */ | |||
409 | task_unlock(task)((void)(&(task)->lock)); | |||
410 | ||||
411 | { | |||
412 | vm_size_t size_used, size_left; | |||
413 | vm_map_copy_t memory; | |||
414 | ||||
415 | /* | |||
416 | * Free any unused memory beyond the end of the last page used | |||
417 | */ | |||
418 | size_used = round_page(vector_size)((vm_offset_t)((((vm_offset_t)(vector_size)) + ((1 << 12 )-1)) & ~((1 << 12)-1))); | |||
419 | if (size_used != size) | |||
420 | (void) kmem_free(ipc_kernel_map, | |||
421 | addr + size_used, | |||
422 | size - size_used); | |||
423 | ||||
424 | /* | |||
425 | * Zero the remainder of the page being returned. | |||
426 | */ | |||
427 | size_left = size_used - vector_size; | |||
428 | if (size_left > 0) | |||
429 | memset((char *)addr + vector_size, 0, size_left); | |||
430 | ||||
431 | /* | |||
432 | * Make memory into copyin form - this unwires it. | |||
433 | */ | |||
434 | (void) vm_map_copyin(ipc_kernel_map, addr, vector_size, TRUE((boolean_t) 1), &memory); | |||
435 | ||||
436 | *emulation_vector = (emulation_vector_t) memory; | |||
437 | } | |||
438 | ||||
439 | return KERN_SUCCESS0; | |||
440 | } | |||
441 | ||||
442 | /* | |||
443 | * task_set_emulation: [Server Entry] | |||
444 | * set up for user space emulation of syscalls within this task. | |||
445 | */ | |||
446 | kern_return_t task_set_emulation( | |||
447 | task_t task, | |||
448 | vm_offset_t routine_entry_pt, | |||
449 | int routine_number) | |||
450 | { | |||
451 | return task_set_emulation_vector_internal(task, routine_number, | |||
452 | &routine_entry_pt, 1); | |||
453 | } |