xref: /xnu-11215/libkern/OSKextLib.cpp (revision 855239e5)
1 /*
2  * Copyright (c) 2008 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         kmem_free(kernel_map, (vm_offset_t)response, responseLength);
343     }
344     if (logData) {
345         kmem_free(kernel_map, (vm_offset_t)logData, logDataLength);
346     }
347 
348     return result;
349 }
350 
351 /*********************************************************************
352 * Gets the vm_map for the current kext
353 *********************************************************************/
354 extern vm_offset_t sectPRELINKB;
355 extern int sectSizePRELINK;
356 extern int kth_started;
357 extern vm_map_t g_kext_map;
358 
359 vm_map_t
360 kext_get_vm_map(kmod_info_t *info)
361 {
362     vm_map_t kext_map = NULL;
363 
364     /* Set the vm map */
365     if ((info->address >= sectPRELINKB) &&
366             (info->address < (sectPRELINKB + sectSizePRELINK)))
367     {
368         kext_map = kernel_map;
369     } else {
370         kext_map = g_kext_map;
371     }
372 
373     return kext_map;
374 }
375 
376 
377 #if PRAGMA_MARK
378 /********************************************************************/
379 #pragma mark Weak linking support
380 /********************************************************************/
381 #endif
382 void
383 kext_weak_symbol_referenced(void)
384 {
385     panic("A kext referenced an unresolved weak symbol\n");
386 }
387 
388 const void *gOSKextUnresolved = (const void *)&kext_weak_symbol_referenced;
389 
390 #if PRAGMA_MARK
391 #pragma mark Kernel-Internal C Functions
392 #endif
393 /*********************************************************************
394 * Called from startup.c.
395 *********************************************************************/
396 void OSKextRemoveKextBootstrap(void)
397 {
398     OSKext::removeKextBootstrap();
399     return;
400 }
401 
402 #if CONFIG_DTRACE
403 /*********************************************************************
404 *********************************************************************/
405 void OSKextRegisterKextsWithDTrace(void)
406 {
407     OSKext::registerKextsWithDTrace();
408     return;
409 }
410 #endif /* CONFIG_DTRACE */
411 
412 /*********************************************************************
413 *********************************************************************/
414 void kext_dump_panic_lists(int (*printf_func)(const char * fmt, ...))
415 {
416     OSKext::printKextPanicLists(printf_func);
417     return;
418 }
419 
420 #if PRAGMA_MARK
421 #pragma mark Kmod Compatibility Functions
422 #endif
423 /*********************************************************************
424 **********************************************************************
425 *                    KMOD COMPATIBILITY FUNCTIONS                    *
426 *              (formerly in kmod.c, or C++ bridges from)             *
427 **********************************************************************
428 **********************************************************************
429 * These two functions are used in various places in the kernel, but
430 * are not exported. We might rename them at some point to start with
431 * kext_ or OSKext.
432 *
433 * kmod_panic_dump() must not be called outside of a panic context.
434 * kmod_dump_log() must not be called in a panic context.
435 *********************************************************************/
436 void
437 kmod_panic_dump(vm_offset_t * addr, unsigned int cnt)
438 {
439     extern int kdb_printf(const char *format, ...) __printflike(1,2);
440 
441     OSKext::printKextsInBacktrace(addr, cnt, &kdb_printf,
442         /* takeLock? */ false);
443     return;
444 }
445 
446 /********************************************************************/
447 void kmod_dump_log(vm_offset_t *addr, unsigned int cnt);
448 
449 void
450 kmod_dump_log(
451     vm_offset_t * addr,
452     unsigned int cnt)
453 {
454     OSKext::printKextsInBacktrace(addr, cnt, &printf, /* lock? */ true);
455 }
456 
457 /*********************************************************************
458 * Compatibility implementation for kmod_get_info() host_priv routine.
459 * Only supported on old 32-bit architectures.
460 *********************************************************************/
461 #if __i386__
462 kern_return_t
463 kext_get_kmod_info(
464     kmod_info_array_t      * kmod_list,
465     mach_msg_type_number_t * kmodCount)
466 {
467     return OSKext::getKmodInfo(kmod_list, kmodCount);
468 }
469 #endif /* __i386__ */
470 
471 #if PRAGMA_MARK
472 #pragma mark Loaded Kext Summary
473 #endif
474 
475 void
476 OSKextLoadedKextSummariesUpdated(void)
477 {
478     // Do nothing.
479 }
480 
481 };
482