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