xref: /xnu-11215/libkern/OSKextLib.cpp (revision a3bb9fcc)
1 /*
2  * Copyright (c) 2008-2012 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 extern "C" {
30 #include <libkern/OSKextLibPrivate.h>
31 #include <libkern/mkext.h>
32 };
33 
34 #include <libkern/c++/OSContainers.h>
35 #include <libkern/c++/OSKext.h>
36 #include <libkern/OSKextLib.h>
37 #include <libkern/OSKextLibPrivate.h>
38 
39 extern "C" {
40 
41 #if PRAGMA_MARK
42 #pragma mark C-based kext interface (loading/loaded kexts only)
43 #endif
44 /*********************************************************************
45 *********************************************************************/
46 kern_return_t OSKextLoadKextWithIdentifier(const char * bundle_id)
47 {
48     return OSKext::loadKextWithIdentifier(bundle_id);
49 }
50 
51 /*********************************************************************
52 *********************************************************************/
53 uint32_t
54 OSKextGetLoadTagForIdentifier(const char * kextIdentifier)
55 {
56     uint32_t result  = kOSKextInvalidLoadTag;
57     OSKext * theKext = NULL;  // must release
58 
59     if (!kextIdentifier) {
60         goto finish;
61     }
62 
63     theKext = OSKext::lookupKextWithIdentifier(kextIdentifier);
64     if (theKext && theKext->isLoaded()) {
65         result = theKext->getLoadTag();
66     }
67 finish:
68     if (theKext) theKext->release();
69     return result;
70 }
71 
72 /*********************************************************************
73 *********************************************************************/
74 OSReturn OSKextRetainKextWithLoadTag(uint32_t loadTag)
75 {
76     OSReturn   result = kOSKextReturnNotFound;
77     OSKext   * theKext = NULL;  // do not release; as this function is a retain
78 
79     if (loadTag == kOSKextInvalidLoadTag) {
80         result = kOSKextReturnInvalidArgument;
81         goto finish;
82     }
83     theKext = OSKext::lookupKextWithLoadTag(loadTag);
84     if (theKext) {
85         result = kOSReturnSuccess;
86 
87         OSKextLog(theKext,
88             kOSKextLogDebugLevel |
89             kOSKextLogKextBookkeepingFlag,
90             "Kext %s (load tag %d) has been retained.",
91             theKext->getIdentifierCString(),
92             loadTag);
93 
94        /* Call this after so a log message about autounload comes second.
95         */
96         theKext->setAutounloadEnabled(true);
97     } else {
98         OSKextLog(theKext,
99             kOSKextLogErrorLevel |
100             kOSKextLogKextBookkeepingFlag,
101             "Can't retain kext with load tag %d - no such kext is loaded.",
102            loadTag);
103     }
104 finish:
105     return result;
106 }
107 
108 /*********************************************************************
109 *********************************************************************/
110 OSReturn OSKextReleaseKextWithLoadTag(uint32_t loadTag)
111 {
112     OSReturn result  = kOSKextReturnNotFound;
113     OSKext * theKext = NULL;  // must release twice!
114 
115     if (loadTag == kOSKextInvalidLoadTag) {
116         result = kOSKextReturnInvalidArgument;
117         goto finish;
118     }
119     theKext = OSKext::lookupKextWithLoadTag(loadTag);
120     if (theKext) {
121         result = kOSReturnSuccess;
122         OSKext::considerUnloads();  // schedule autounload pass
123         theKext->release();  // do the release the caller wants
124         theKext->release();  // now do the release on the lookup
125         OSKextLog(theKext,
126             kOSKextLogDebugLevel |
127             kOSKextLogKextBookkeepingFlag,
128             "Kext %s (load tag %d) has been released.",
129             theKext->getIdentifierCString(),
130             loadTag);
131     } else {
132         OSKextLog(theKext,
133             kOSKextLogErrorLevel |
134             kOSKextLogKextBookkeepingFlag,
135             "Can't release kext with load tag %d - no such kext is loaded.",
136            loadTag);
137     }
138 
139     // xxx - should I check that the refcount of the OSKext is above the lower bound?
140     // xxx - do we want a OSKextGetRetainCountOfKextWithLoadTag()?
141 finish:
142     return result;
143 }
144 
145 /*********************************************************************
146 * Not to be called by the kext being unloaded!
147 *********************************************************************/
148 OSReturn OSKextUnloadKextWithLoadTag(uint32_t loadTag)
149 {
150     return OSKext::removeKextWithLoadTag(loadTag,
151         /* terminateServicesAndRemovePersonalitiesFlag */ false);
152 }
153 
154 
155 #if PRAGMA_MARK
156 #pragma mark Kext Requests
157 #endif
158 /*********************************************************************
159 * Kext Requests
160 *********************************************************************/
161 OSReturn OSKextRequestResource(
162     const char                    * kextIdentifier,
163     const char                    * resourceName,
164     OSKextRequestResourceCallback   callback,
165     void                          * context,
166     OSKextRequestTag              * requestTagOut)
167 {
168     return OSKext::requestResource(kextIdentifier, resourceName,
169         callback, context, requestTagOut);
170 }
171 
172 /*********************************************************************
173 *********************************************************************/
174 OSReturn OSKextCancelRequest(
175     OSKextRequestTag    requestTag,
176     void             ** contextOut)
177 {
178     return OSKext::cancelRequest(requestTag, contextOut);
179 }
180 
181 #if PRAGMA_MARK
182 #pragma mark MIG Functions & Wrappers
183 #endif
184 /*********************************************************************
185 * IMPORTANT: Once we have done the vm_map_copyout(), we *must* return
186 * KERN_SUCCESS or the kernel map gets messed up (reason as yet
187 * unknown). We use op_result to return the real result of our work.
188 *********************************************************************/
189 kern_return_t kext_request(
190     host_priv_t                             hostPriv,
191     /* in only */  uint32_t                 clientLogSpec,
192     /* in only */  vm_offset_t              requestIn,
193     /* in only */  mach_msg_type_number_t   requestLengthIn,
194     /* out only */ vm_offset_t            * responseOut,
195     /* out only */ mach_msg_type_number_t * responseLengthOut,
196     /* out only */ vm_offset_t            * logDataOut,
197     /* out only */ mach_msg_type_number_t * logDataLengthOut,
198     /* out only */ kern_return_t          * op_result)
199 {
200     kern_return_t     result          = KERN_FAILURE;
201     vm_map_address_t  map_addr        = 0;     // do not free/deallocate
202     char            * request         = NULL;  // must vm_deallocate
203 
204     mkext2_header   * mkextHeader     = NULL;  // do not release
205     bool              isMkext         = false;
206 
207     char            * response        = NULL;  // must kmem_free
208     uint32_t          responseLength  = 0;
209     char            * logData         = NULL;  // must kmem_free
210     uint32_t          logDataLength   = 0;
211 
212    /* MIG doesn't pass "out" parameters as empty, so clear them immediately
213     * just in case, or MIG will try to copy out bogus data.
214     */
215     *op_result = KERN_FAILURE;
216     *responseOut = NULL;
217     *responseLengthOut = 0;
218     *logDataOut = NULL;
219     *logDataLengthOut = 0;
220 
221    /* Check for input. Don't discard what isn't there, though.
222     */
223     if (!requestLengthIn || !requestIn) {
224 		OSKextLog(/* kext */ NULL,
225             kOSKextLogErrorLevel |
226             kOSKextLogIPCFlag,
227             "Invalid request from user space (no data).");
228         *op_result = KERN_INVALID_ARGUMENT;
229         goto finish;
230     }
231 
232    /* Once we have done the vm_map_copyout(), we *must* return KERN_SUCCESS
233     * or the kernel map gets messed up (reason as yet unknown). We will use
234     * op_result to return the real result of our work.
235     */
236     result = vm_map_copyout(kernel_map, &map_addr, (vm_map_copy_t)requestIn);
237     if (result != KERN_SUCCESS) {
238         OSKextLog(/* kext */ NULL,
239             kOSKextLogErrorLevel |
240             kOSKextLogIPCFlag,
241             "vm_map_copyout() failed for request from user space.");
242         vm_map_copy_discard((vm_map_copy_t)requestIn);
243         goto finish;
244     }
245     request = CAST_DOWN(char *, map_addr);
246 
247    /* Check if request is an mkext; this is always a load request
248     * and requires root access. If it isn't an mkext, see if it's
249     * an XML request, and check the request to see if that requires
250     * root access.
251     */
252     if (requestLengthIn > sizeof(mkext2_header)) {
253         mkextHeader = (mkext2_header *)request;
254         if (MKEXT_GET_MAGIC(mkextHeader) == MKEXT_MAGIC &&
255             MKEXT_GET_SIGNATURE(mkextHeader) == MKEXT_SIGN) {
256 
257             isMkext = true;
258         }
259     }
260 
261     if (isMkext) {
262 #ifdef SECURE_KERNEL
263         // xxx - something tells me if we have a secure kernel we don't even
264         // xxx - want to log a message here. :-)
265         *op_result = KERN_NOT_SUPPORTED;
266         goto finish;
267 #else
268         // xxx - can we find out if calling task is kextd?
269         // xxx - can we find the name of the calling task?
270         if (hostPriv == HOST_PRIV_NULL) {
271             OSKextLog(/* kext */ NULL,
272                 kOSKextLogErrorLevel |
273                 kOSKextLogLoadFlag | kOSKextLogIPCFlag,
274                 "Attempt by non-root process to load a kext.");
275             *op_result = kOSKextReturnNotPrivileged;
276             goto finish;
277         }
278 
279         *op_result = OSKext::loadFromMkext((OSKextLogSpec)clientLogSpec,
280             request, requestLengthIn,
281             &logData, &logDataLength);
282 
283 #endif /* defined(SECURE_KERNEL) */
284 
285     } else {
286 
287        /* If the request isn't an mkext, then is should be XML. Parse it
288         * if possible and hand the request over to OSKext.
289         */
290         *op_result = OSKext::handleRequest(hostPriv,
291             (OSKextLogSpec)clientLogSpec,
292             request, requestLengthIn,
293             &response, &responseLength,
294             &logData, &logDataLength);
295     }
296 
297     if (response && responseLength > 0) {
298         kern_return_t copyin_result;
299 
300         copyin_result = vm_map_copyin(kernel_map,
301             CAST_USER_ADDR_T(response), responseLength,
302             /* src_destroy */ false, (vm_map_copy_t *)responseOut);
303         if (copyin_result == KERN_SUCCESS) {
304             *responseLengthOut = responseLength;
305         } else {
306             OSKextLog(/* kext */ NULL,
307                 kOSKextLogErrorLevel |
308                 kOSKextLogIPCFlag,
309                 "Failed to copy response to request from user space.");
310             *op_result = copyin_result;  // xxx - should we map to our own code?
311             *responseOut = NULL;
312             *responseLengthOut = 0;
313             goto finish;
314         }
315     }
316 
317     if (logData && logDataLength > 0) {
318         kern_return_t copyin_result;
319 
320         copyin_result = vm_map_copyin(kernel_map,
321             CAST_USER_ADDR_T(logData), logDataLength,
322             /* src_destroy */ false, (vm_map_copy_t *)logDataOut);
323         if (copyin_result == KERN_SUCCESS) {
324             *logDataLengthOut = logDataLength;
325         } else {
326             OSKextLog(/* kext */ NULL,
327                 kOSKextLogErrorLevel |
328                 kOSKextLogIPCFlag,
329                 "Failed to copy log data for request from user space.");
330             *op_result = copyin_result;  // xxx - should we map to our own code?
331             *logDataOut = NULL;
332             *logDataLengthOut = 0;
333             goto finish;
334         }
335     }
336 
337 finish:
338     if (request) {
339         (void)vm_deallocate(kernel_map, (vm_offset_t)request, requestLengthIn);
340     }
341     if (response) {
342         /* 11981737 - clear uninitialized data in last page */
343         kmem_free(kernel_map, (vm_offset_t)response, round_page(responseLength));
344     }
345     if (logData) {
346         /* 11981737 - clear uninitialized data in last page */
347         kmem_free(kernel_map, (vm_offset_t)logData, round_page(logDataLength));
348     }
349 
350     return result;
351 }
352 
353 /*********************************************************************
354 * Gets the vm_map for the current kext
355 *********************************************************************/
356 extern vm_offset_t segPRELINKB;
357 extern unsigned long segSizePRELINK;
358 extern int kth_started;
359 extern vm_map_t g_kext_map;
360 
361 vm_map_t
362 kext_get_vm_map(kmod_info_t *info)
363 {
364     vm_map_t kext_map = NULL;
365 
366     /* Set the vm map */
367     if ((info->address >= segPRELINKB) &&
368             (info->address < (segPRELINKB + segSizePRELINK)))
369     {
370         kext_map = kernel_map;
371     } else {
372         kext_map = g_kext_map;
373     }
374 
375     return kext_map;
376 }
377 
378 
379 #if PRAGMA_MARK
380 /********************************************************************/
381 #pragma mark Weak linking support
382 /********************************************************************/
383 #endif
384 void
385 kext_weak_symbol_referenced(void)
386 {
387     panic("A kext referenced an unresolved weak symbol\n");
388 }
389 
390 const void *gOSKextUnresolved = (const void *)&kext_weak_symbol_referenced;
391 
392 #if PRAGMA_MARK
393 #pragma mark Kernel-Internal C Functions
394 #endif
395 /*********************************************************************
396 * Called from startup.c.
397 *********************************************************************/
398 void OSKextRemoveKextBootstrap(void)
399 {
400     OSKext::removeKextBootstrap();
401     return;
402 }
403 
404 #if CONFIG_DTRACE
405 /*********************************************************************
406 *********************************************************************/
407 void OSKextRegisterKextsWithDTrace(void)
408 {
409     OSKext::registerKextsWithDTrace();
410     return;
411 }
412 #endif /* CONFIG_DTRACE */
413 
414 /*********************************************************************
415 *********************************************************************/
416 void kext_dump_panic_lists(int (*printf_func)(const char * fmt, ...))
417 {
418     OSKext::printKextPanicLists(printf_func);
419     return;
420 }
421 
422 #if PRAGMA_MARK
423 #pragma mark Kmod Compatibility Functions
424 #endif
425 /*********************************************************************
426 **********************************************************************
427 *                    KMOD COMPATIBILITY FUNCTIONS                    *
428 *              (formerly in kmod.c, or C++ bridges from)             *
429 **********************************************************************
430 **********************************************************************
431 * These two functions are used in various places in the kernel, but
432 * are not exported. We might rename them at some point to start with
433 * kext_ or OSKext.
434 *
435 * kmod_panic_dump() must not be called outside of a panic context.
436 * kmod_dump_log() must not be called in a panic context.
437 *********************************************************************/
438 void
439 kmod_panic_dump(vm_offset_t * addr, unsigned int cnt)
440 {
441     extern int kdb_printf(const char *format, ...) __printflike(1,2);
442 
443     OSKext::printKextsInBacktrace(addr, cnt, &kdb_printf,
444         /* takeLock? */ false, false);
445     return;
446 }
447 
448 /********************************************************************/
449 void kmod_dump_log(vm_offset_t *addr, unsigned int cnt, boolean_t doUnslide);
450 
451 void
452 kmod_dump_log(
453     vm_offset_t * addr,
454     unsigned int cnt,
455     boolean_t doUnslide)
456 {
457     OSKext::printKextsInBacktrace(addr, cnt, &printf, /* lock? */ true, doUnslide);
458 }
459 
460 /*********************************************************************
461 * Compatibility implementation for kmod_get_info() host_priv routine.
462 * Only supported on old 32-bit architectures.
463 *********************************************************************/
464 
465 #if PRAGMA_MARK
466 #pragma mark Loaded Kext Summary
467 #endif
468 
469 void
470 OSKextLoadedKextSummariesUpdated(void)
471 {
472     // Do nothing.
473 }
474 
475 };
476