/*
- * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ * Copyright (c) 2008-2016 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
*/
extern "C" {
+#include <string.h>
#include <kern/clock.h>
#include <kern/host.h>
#include <kern/kext_alloc.h>
+#include <firehose/tracepoint_private.h>
+#include <firehose/chunk_private.h>
+#include <os/firehose_buffer_private.h>
+#include <vm/vm_kern.h>
#include <kextd/kextd_mach.h>
#include <libkern/kernel_mach_header.h>
#include <libkern/kext_panic_report.h>
#include <libkern/prelink.h>
#include <libkern/version.h>
#include <libkern/zlib.h>
+#include <mach/host_special_ports.h>
#include <mach/mach_vm.h>
+#include <mach/mach_time.h>
#include <sys/sysctl.h>
+#include <uuid/uuid.h>
+// 04/18/11 - gab: <rdar://problem/9236163>
+#include <sys/random.h>
+
+#include <sys/pgo.h>
+
+#if CONFIG_MACF
+#include <sys/kauth.h>
+#include <security/mac_framework.h>
+#endif
};
#include <libkern/OSKextLibPrivate.h>
#include <IOKit/IORegistryEntry.h>
#include <IOKit/IOService.h>
+#include <IOKit/IOStatisticsPrivate.h>
+#include <IOKit/IOBSD.h>
+
+#include <san/kasan.h>
+
#if PRAGMA_MARK
#pragma mark External & Internal Function Protos
#endif
/*********************************************************************
*********************************************************************/
extern "C" {
-// in libkern/OSKextLib.cpp, not in header for a reason.
-extern kern_return_t OSKextPingKextd(void);
-
extern int IODTGetLoaderInfo(const char * key, void ** infoAddr, int * infoSize);
extern void IODTFreeLoaderInfo(const char * key, void * infoAddr, int infoSize);
extern void OSRuntimeUnloadCPPForSegment(kernel_segment_command_t * segment);
OSDictionary * dict,
const char * key,
const char * value);
-#if CONFIG_MACF_KEXT
-static void * MACFCopyModuleDataForKext(
- OSKext * theKext,
- mach_msg_type_number_t * datalen);
-#endif /* CONFIG_MACF_KEXT */
+static bool _OSKextInPrelinkRebuildWindow(void);
+static bool _OSKextInUnloadedPrelinkedKexts(const OSSymbol * theBundleID);
+
+// We really should add containsObject() & containsCString to OSCollection & subclasses.
+// So few pad slots, though....
+static bool _OSArrayContainsCString(OSArray * array, const char * cString);
+
+#if CONFIG_KEC_FIPS
+static void * GetAppleTEXTHashForKext(OSKext * theKext, OSDictionary *theInfoDict);
+#endif // CONFIG_KEC_FIPS
+
+/* Prelinked arm kexts do not have VM entries because the method we use to
+ * fake an entry (see libsa/bootstrap.cpp:readPrelinkedExtensions()) does
+ * not work on ARM. To get around that, we must free prelinked kext
+ * executables with ml_static_mfree() instead of kext_free().
+ */
+#if __i386__ || __x86_64__
+#define VM_MAPPED_KEXTS 1
+#define KASLR_KEXT_DEBUG 0
+#define KASLR_IOREG_DEBUG 0
+#elif __arm__ || __arm64__
+#define VM_MAPPED_KEXTS 0
+#define KASLR_KEXT_DEBUG 0
+#else
+#error Unsupported architecture
+#endif
#if PRAGMA_MARK
#pragma mark Constants & Macros
* Constants & Macros
*********************************************************************/
-/* A typical Snow Leopard system has a bit under 120 kexts loaded.
- * Use this number to create containers.
+/* Use this number to create containers.
*/
-#define kOSKextTypicalLoadCount (120)
+#define kOSKextTypicalLoadCount (150)
/* Any kext will have at least 1 retain for the internal lookup-by-ID dict.
* A loaded kext will no dependents or external retains will have 2 retains.
#define STRING_HAS_PREFIX(s, p) (strncmp((s), (p), strlen(p)) == 0)
+#define REBUILD_MAX_TIME (60 * 5) // 5 minutes
+#define MINIMUM_WAKEUP_SECONDS (30)
+
/*********************************************************************
* infoDict keys for internally-stored data. Saves on ivar slots for
* objects we don't keep around past boot time or during active load.
*/
#define _kOSKextExecutableExternalDataKey "_OSKextExecutableExternalData"
+#define OS_LOG_HDR_VERSION 1
+#define NUM_OS_LOG_SECTIONS 2
+
+#define OS_LOG_SECT_IDX 0
+#define CSTRING_SECT_IDX 1
+
#if PRAGMA_MARK
#pragma mark Typedefs
#endif
* Typedefs
*********************************************************************/
+/*********************************************************************
+* osLogDataHeaderRef describes the header information of an OSData
+* object that is returned when querying for kOSBundleLogStringsKey.
+* We currently return information regarding 2 sections - os_log and
+* cstring. In the case that the os_log section doesn't exist, we just
+* return an offset and length of 0 for that section.
+*********************************************************************/
+typedef struct osLogDataHeader {
+ uint32_t version;
+ uint32_t sect_count;
+ struct {
+ uint32_t sect_offset;
+ uint32_t sect_size;
+ } sections[0];
+} osLogDataHeaderRef;
+
/*********************************************************************
* MkextEntryRef describes the contents of an OSData object
* referencing a file entry from an mkext so that we can uncompress
static bool sPrelinkBoot = false;
static bool sSafeBoot = false;
+static bool sKeepSymbols = false;
-/******
-* sKextLock is the principal lock for OSKext. Below, there is also an
-* sKextInnerLock used to guard access to data accessed on in-calls from
-* IOService. This 2nd lock is required to prevent a deadlock
-* with IOService calling back into OSKext::considerUnloads()
-* on a separate thread during a kext load operation.
+/*********************************************************************
+* sKextLock is the principal lock for OSKext, and guards all static
+* and global variables not owned by other locks (declared further
+* below). It must be taken by any entry-point method or function,
+* including internal functions called on scheduled threads.
+*
+* sKextLock and sKextInnerLock are recursive due to multiple functions
+* that are called both externally and internally. The other locks are
+* nonrecursive.
+*
+* Which locks are taken depends on what they protect, but if more than
+* one must be taken, they must always be locked in this order
+* (and unlocked in reverse order) to prevent deadlocks:
+*
+* 1. sKextLock
+* 2. sKextInnerLock
+* 3. sKextSummariesLock
+* 4. sKextLoggingLock
*/
static IORecursiveLock * sKextLock = NULL;
static OSDictionary * sKextsByID = NULL;
+static OSDictionary * sExcludeListByID = NULL;
static OSArray * sLoadedKexts = NULL;
-
-static OSArray * sPrelinkedPersonalities = NULL;
+static OSArray * sUnloadedPrelinkedKexts = NULL;
// Requests to kextd waiting to be picked up.
static OSArray * sKernelRequests = NULL;
static bool sDeferredLoadSucceeded = false;
static bool sConsiderUnloadsExecuted = false;
+#if NO_KEXTD
+static bool sKernelRequestsEnabled = false;
+#else
static bool sKernelRequestsEnabled = true;
+#endif
static bool sLoadEnabled = true;
static bool sUnloadEnabled = true;
/* version */ "0", // filled in in OSKext::initialize()
/* reference_count */ -1, // never adjusted; kernel never unloads
/* reference_list */ NULL,
- /* address */ (vm_address_t)&_mh_execute_header,
+ /* address */ 0,
/* size */ 0, // filled in in OSKext::initialize()
/* hdr_size */ 0,
/* start */ 0,
#define KEXT_PANICLIST_SIZE (2 * PAGE_SIZE)
-static char * unloaded_kext_paniclist = NULL;
-static uint32_t unloaded_kext_paniclist_size = 0;
-static uint32_t unloaded_kext_paniclist_length = 0;
+
+static char * loaded_kext_paniclist = NULL;
+static uint32_t loaded_kext_paniclist_size = 0;
+
AbsoluteTime last_loaded_timestamp;
+static char last_loaded_str_buf[2*KMOD_MAX_NAME];
+static u_long last_loaded_strlen = 0;
+static void * last_loaded_address = NULL;
+static u_long last_loaded_size = 0;
-static char * loaded_kext_paniclist = NULL;
-static uint32_t loaded_kext_paniclist_size = 0;
-static uint32_t loaded_kext_paniclist_length = 0;
AbsoluteTime last_unloaded_timestamp;
-static void * last_unloaded_address = NULL;
-#if __LP64__
-static uint64_t last_unloaded_size = 0;
-#else
-static uint32_t last_unloaded_size = 0;
-#endif /* __LP64__ */
-
-};
+static char last_unloaded_str_buf[2*KMOD_MAX_NAME];
+static u_long last_unloaded_strlen = 0;
+static void * last_unloaded_address = NULL;
+static u_long last_unloaded_size = 0;
/*********************************************************************
-* Because we can start IOService matching from OSKext (via IOCatalogue)
-* and IOService can call into OSKext, there is potential for cross-lock
-* contention, so OSKext needs two locks. The regular sKextLock above
-* guards most OSKext class/static variables, and sKextInnerLock guards
-* variables that can be accessed on in-calls from IOService, currently:
-*
-* * OSKext::considerUnloads()
+* sKextInnerLock protects against cross-calls with IOService and
+* IOCatalogue, and owns the variables declared immediately below.
*
* Note that sConsiderUnloadsExecuted above belongs to sKextLock!
*
* locks in an entry point to OSKext; if you need to do so, you must
* spawn an independent thread to avoid potential deadlocks for threads
* calling into OSKext.
-*
-* All static variables from here to the closing comment block fall
-* under sKextInnerLock.
**********/
static IORecursiveLock * sKextInnerLock = NULL;
static thread_call_t sUnloadCallout = 0;
static thread_call_t sDestroyLinkContextThread = 0; // one-shot, one-at-a-time thread
static bool sSystemSleep = false; // true when system going to sleep
+static AbsoluteTime sLastWakeTime; // last time we woke up
+
+/*********************************************************************
+* Backtraces can be printed at various times so we need a tight lock
+* on data used for that. sKextSummariesLock protects the variables
+* declared immediately below.
+*
+* gLoadedKextSummaries is accessed by other modules, but only during
+* a panic so the lock isn't needed then.
+*
+* gLoadedKextSummaries has the "used" attribute in order to ensure
+* that it remains visible even when we are performing extremely
+* aggressive optimizations, as it is needed to allow the debugger
+* to automatically parse the list of loaded kexts.
+**********/
+static IOLock * sKextSummariesLock = NULL;
+extern "C" lck_spin_t vm_allocation_sites_lock;
+static IOSimpleLock * sKextAccountsLock = &vm_allocation_sites_lock;
+
+void (*sLoadedKextSummariesUpdated)(void) = OSKextLoadedKextSummariesUpdated;
+OSKextLoadedKextSummaryHeader * gLoadedKextSummaries __attribute__((used)) = NULL;
+uint64_t gLoadedKextSummariesTimestamp __attribute__((used)) = 0;
+static size_t sLoadedKextSummariesAllocSize = 0;
+
+static OSKextActiveAccount * sKextAccounts;
+static uint32_t sKextAccountsCount;
+};
+
+/*********************************************************************
+* sKextLoggingLock protects the logging variables declared immediately below.
+**********/
+static IOLock * sKextLoggingLock = NULL;
static const OSKextLogSpec kDefaultKernelLogFilter = kOSKextLogBasicLevel |
kOSKextLogVerboseFlagsMask;
static OSKextLogSpec sKernelLogFilter = kDefaultKernelLogFilter;
static bool sBootArgLogFilterFound = false;
-SYSCTL_INT(_debug, OID_AUTO, kextlog, CTLFLAG_RW, &sKernelLogFilter,
- sKernelLogFilter, "kernel kext logging");
+SYSCTL_UINT(_debug, OID_AUTO, kextlog, CTLFLAG_RW | CTLFLAG_LOCKED, &sKernelLogFilter,
+ 0, "kernel kext logging");
static OSKextLogSpec sUserSpaceKextLogFilter = kOSKextLogSilentFilter;
static OSArray * sUserSpaceLogSpecArray = NULL;
* End scope for sKextInnerLock-protected variables.
*********************************************************************/
+
+/*********************************************************************
+ helper function used for collecting PGO data upon unload of a kext
+ */
+
+static int OSKextGrabPgoDataLocked(OSKext *kext,
+ bool metadata,
+ uuid_t instance_uuid,
+ uint64_t *pSize,
+ char *pBuffer,
+ uint64_t bufferSize);
+
+/**********************************************************************/
+
+
+
#if PRAGMA_MARK
#pragma mark OSData callbacks (need to move to OSData)
#endif
(void)vm_deallocate(kernel_map, (vm_offset_t)ptr, length);
return;
}
+
+void osdata_kext_free(void * ptr, unsigned int length)
+{
+ (void)kext_free((vm_offset_t)ptr, length);
+}
+
};
#if PRAGMA_MARK
}
/* Create an OSData wrapper for the allocated buffer.
- * Note that we do not set a dealloc function on it here.
- * We have to call vm_map_unwire() on it in OSKext::unload()
- * and an OSData dealloc function can't take all those parameters.
*/
linkBuffer = OSData::withBytesNoCopy((void *)result, roundSize);
if (!linkBuffer) {
theKext->getIdentifierCString());
goto finish;
}
-
+ linkBuffer->setDeallocFunction(osdata_kext_free);
OSKextLog(theKext,
kOSKextLogProgressLevel |
kOSKextLogLoadFlag | kOSKextLogLinkFlag,
result = 0;
}
- OSSafeRelease(linkBuffer);
+ OSSafeReleaseNULL(linkBuffer);
return (kxld_addr_t)result;
}
OSKextVLog(theKext, logSpec, format, argList);
}
+#if PRAGMA_MARK
+#pragma mark IOStatistics defines
+#endif
+
+#if IOKITSTATS
+
+#define notifyKextLoadObservers(kext, kmod_info) \
+do { \
+ IOStatistics::onKextLoad(kext, kmod_info); \
+} while (0)
+
+#define notifyKextUnloadObservers(kext) \
+do { \
+ IOStatistics::onKextUnload(kext); \
+} while (0)
+
+#define notifyAddClassObservers(kext, addedClass, flags) \
+do { \
+ IOStatistics::onClassAdded(kext, addedClass); \
+} while (0)
+
+#define notifyRemoveClassObservers(kext, removedClass, flags) \
+do { \
+ IOStatistics::onClassRemoved(kext, removedClass); \
+} while (0)
+
+#else
+
+#define notifyKextLoadObservers(kext, kmod_info)
+#define notifyKextUnloadObservers(kext)
+#define notifyAddClassObservers(kext, addedClass, flags)
+#define notifyRemoveClassObservers(kext, removedClass, flags)
+
+#endif /* IOKITSTATS */
+
#if PRAGMA_MARK
#pragma mark Module Config (Startup & Shutdown)
#endif
*/
sKextLock = IORecursiveLockAlloc();
sKextInnerLock = IORecursiveLockAlloc();
+ sKextSummariesLock = IOLockAlloc();
+ sKextLoggingLock = IOLockAlloc();
assert(sKextLock);
assert(sKextInnerLock);
+ assert(sKextSummariesLock);
+ assert(sKextLoggingLock);
sKextsByID = OSDictionary::withCapacity(kOSKextTypicalLoadCount);
sLoadedKexts = OSArray::withCapacity(kOSKextTypicalLoadCount);
+ sUnloadedPrelinkedKexts = OSArray::withCapacity(kOSKextTypicalLoadCount / 10);
sKernelRequests = OSArray::withCapacity(0);
sPostedKextLoadIdentifiers = OSSet::withCapacity(0);
sAllKextLoadIdentifiers = OSSet::withCapacity(kOSKextTypicalLoadCount);
sRequestCallbackRecords = OSArray::withCapacity(0);
assert(sKextsByID && sLoadedKexts && sKernelRequests &&
sPostedKextLoadIdentifiers && sAllKextLoadIdentifiers &&
- sRequestCallbackRecords);
+ sRequestCallbackRecords && sUnloadedPrelinkedKexts);
/* Read the log flag boot-args and set the log flags.
*/
- if (PE_parse_boot_argn("kextlog", &bootLogFilter, sizeof("kextlog=0x00000000 "))) {
+ if (PE_parse_boot_argn("kextlog", &bootLogFilter, sizeof(bootLogFilter))) {
sBootArgLogFilterFound = true;
sKernelLogFilter = bootLogFilter;
// log this if any flags are set
"only valid OSBundleRequired kexts will be loaded.");
}
+ PE_parse_boot_argn("keepsyms", &sKeepSymbols, sizeof(sKeepSymbols));
+#if KASAN_DYNAMIC_BLACKLIST
+ /* needed for function lookup */
+ sKeepSymbols = true;
+#endif
+
/* Set up an OSKext instance to represent the kernel itself.
*/
sKernelKext = new OSKext;
kernelStart, kernelLength);
assert(kernelExecutable);
+#if KASLR_KEXT_DEBUG
+ IOLog("kaslr: kernel start 0x%lx end 0x%lx length %lu vm_kernel_slide %llu (0x%016lx) \n",
+ (unsigned long)kernelStart,
+ (unsigned long)getlastaddr(),
+ kernelLength,
+ vm_kernel_slide, vm_kernel_slide);
+#endif
+
sKernelKext->loadTag = sNextLoadTag++; // the kernel is load tag 0
sKernelKext->bundleID = OSSymbol::withCString(kOSKextKernelIdentifier);
sKernelKext->version = OSKextParseVersionString(osrelease);
sKernelKext->compatibleVersion = sKernelKext->version;
sKernelKext->linkedExecutable = kernelExecutable;
- // linkState will be set first time we do a link
sKernelKext->flags.hasAllDependencies = 1;
sKernelKext->flags.kernelComponent = 1;
sKernelKext->flags.loaded = 1;
sKernelKext->flags.started = 1;
sKernelKext->flags.CPPInitialized = 0;
+ sKernelKext->flags.jettisonLinkeditSeg = 0;
sKernelKext->kmod_info = &g_kernel_kmod_info;
strlcpy(g_kernel_kmod_info.version, osrelease,
registryRoot->setProperty(kOSKernelCPUTypeKey, kernelCPUType);
registryRoot->setProperty(kOSKernelCPUSubtypeKey, kernelCPUSubtype);
- OSSafeRelease(kernelCPUType);
- OSSafeRelease(kernelCPUSubtype);
+ OSSafeReleaseNULL(kernelCPUType);
+ OSSafeReleaseNULL(kernelCPUSubtype);
timestamp = __OSAbsoluteTimePtr(&last_loaded_timestamp);
*timestamp = 0;
timestamp = __OSAbsoluteTimePtr(&last_unloaded_timestamp);
*timestamp = 0;
+ timestamp = __OSAbsoluteTimePtr(&sLastWakeTime);
+ *timestamp = 0;
OSKextLog(/* kext */ NULL,
kOSKextLogProgressLevel |
kOSKextLogGeneralFlag,
"Kext system initialized.");
+ notifyKextLoadObservers(sKernelKext, sKernelKext->kmod_info);
+
return;
}
OSReturn result = kOSReturnError;
static bool alreadyDone = false;
- boolean_t keepsyms = FALSE;
const char * dt_kernel_header_name = "Kernel-__HEADER";
const char * dt_kernel_symtab_name = "Kernel-__SYMTAB";
int dt_result = 0;
kernel_segment_command_t * seg_to_remove = NULL;
-#if __ppc__ || __arm__
+
+#if __arm__ || __arm64__
const char * dt_segment_name = NULL;
void * segment_paddress = NULL;
int segment_size = 0;
kOSKextLogGeneralFlag,
"Jettisoning kext bootstrap segments.");
- PE_parse_boot_argn("keepsyms", &keepsyms, sizeof(keepsyms));
-
/*****
* Dispose of unnecessary stuff that the booter didn't need to load.
*/
OSRuntimeUnloadCPPForSegment(seg_to_remove);
}
-#if __ppc__ || __arm__
+#if __arm__ || __arm64__
+#if !(defined(KERNEL_INTEGRITY_KTRR))
/* Free the memory that was set up by bootx.
*/
dt_segment_name = "Kernel-__KLD";
if (0 == IODTGetLoaderInfo(dt_segment_name, &segment_paddress, &segment_size)) {
+ /* We cannot free this with KTRR enabled, as we cannot
+ * update the permissions on the KLD range this late
+ * in the boot process.
+ */
IODTFreeLoaderInfo(dt_segment_name, (void *)segment_paddress,
(int)segment_size);
}
+#endif /* !(defined(KERNEL_INTEGRITY_KTRR)) */
#elif __i386__ || __x86_64__
/* On x86, use the mapping data from the segment load command to
* unload KLD directly.
* defining the lower bound for valid physical addresses.
*/
if (seg_to_remove && seg_to_remove->vmaddr && seg_to_remove->vmsize) {
+ // 04/18/11 - gab: <rdar://problem/9236163>
+ // overwrite memory occupied by KLD segment with random data before
+ // releasing it.
+ read_frandom((void *) seg_to_remove->vmaddr, seg_to_remove->vmsize);
ml_static_mfree(seg_to_remove->vmaddr, seg_to_remove->vmsize);
}
#else
seg_to_remove = NULL;
- /*****
+ /*****
* Prelinked kernel's symtab (if there is one).
*/
kernel_section_t * sect;
ml_static_mfree(sect->addr, sect->size);
}
- /*****
- * Dump the LINKEDIT segment, unless keepsyms is set.
- */
- if (!keepsyms) {
- seg_to_remove = (kernel_segment_command_t *)getsegbyname("__LINKEDIT");
- if (seg_to_remove) {
- OSRuntimeUnloadCPPForSegment(seg_to_remove);
+ seg_to_remove = (kernel_segment_command_t *)getsegbyname("__LINKEDIT");
+
+ /* kxld always needs the kernel's __LINKEDIT segment, but we can make it
+ * pageable, unless keepsyms is set. To do that, we have to copy it from
+ * its booter-allocated memory, free the booter memory, reallocate proper
+ * managed memory, then copy the segment back in.
+ */
+#if CONFIG_KXLD
+#if (__arm__ || __arm64__)
+#error CONFIG_KXLD not expected for this arch
+#endif
+ if (!sKeepSymbols) {
+ kern_return_t mem_result;
+ void *seg_copy = NULL;
+ void *seg_data = NULL;
+ vm_map_offset_t seg_offset = 0;
+ vm_map_offset_t seg_copy_offset = 0;
+ vm_map_size_t seg_length = 0;
+
+ seg_data = (void *) seg_to_remove->vmaddr;
+ seg_offset = (vm_map_offset_t) seg_to_remove->vmaddr;
+ seg_length = (vm_map_size_t) seg_to_remove->vmsize;
+
+ /* Allocate space for the LINKEDIT copy.
+ */
+ mem_result = kmem_alloc(kernel_map, (vm_offset_t *) &seg_copy,
+ seg_length, VM_KERN_MEMORY_KEXT);
+ if (mem_result != KERN_SUCCESS) {
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogErrorLevel |
+ kOSKextLogGeneralFlag | kOSKextLogArchiveFlag,
+ "Can't copy __LINKEDIT segment for VM reassign.");
+ goto finish;
+ }
+ seg_copy_offset = (vm_map_offset_t) seg_copy;
+
+ /* Copy it out.
+ */
+ memcpy(seg_copy, seg_data, seg_length);
+
+ /* Dump the booter memory.
+ */
+ ml_static_mfree(seg_offset, seg_length);
+
+ /* Set up the VM region.
+ */
+ mem_result = vm_map_enter_mem_object(
+ kernel_map,
+ &seg_offset,
+ seg_length, /* mask */ 0,
+ VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE,
+ VM_MAP_KERNEL_FLAGS_NONE,
+ VM_KERN_MEMORY_NONE,
+ (ipc_port_t)NULL,
+ (vm_object_offset_t) 0,
+ /* copy */ FALSE,
+ /* cur_protection */ VM_PROT_READ | VM_PROT_WRITE,
+ /* max_protection */ VM_PROT_ALL,
+ /* inheritance */ VM_INHERIT_DEFAULT);
+ if ((mem_result != KERN_SUCCESS) ||
+ (seg_offset != (vm_map_offset_t) seg_data))
+ {
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogErrorLevel |
+ kOSKextLogGeneralFlag | kOSKextLogArchiveFlag,
+ "Can't create __LINKEDIT VM entry at %p, length 0x%llx (error 0x%x).",
+ seg_data, seg_length, mem_result);
+ goto finish;
}
-#if __ppc__ || __arm__
+ /* And copy it back.
+ */
+ memcpy(seg_data, seg_copy, seg_length);
+
+ /* Free the copy.
+ */
+ kmem_free(kernel_map, seg_copy_offset, seg_length);
+ }
+#else /* we are not CONFIG_KXLD */
+#if !(__arm__ || __arm64__)
+#error CONFIG_KXLD is expected for this arch
+#endif
+
+ /*****
+ * Dump the LINKEDIT segment, unless keepsyms is set.
+ */
+ if (!sKeepSymbols) {
dt_segment_name = "Kernel-__LINKEDIT";
if (0 == IODTGetLoaderInfo(dt_segment_name,
&segment_paddress, &segment_size)) {
-
+#ifdef SECURE_KERNEL
+ vm_offset_t vmaddr = ml_static_ptovirt((vm_offset_t)segment_paddress);
+ bzero((void*)vmaddr, segment_size);
+#endif
IODTFreeLoaderInfo(dt_segment_name, (void *)segment_paddress,
(int)segment_size);
}
-#elif __i386__ || __x86_64__
- if (seg_to_remove && seg_to_remove->vmaddr && seg_to_remove->vmsize) {
- ml_static_mfree(seg_to_remove->vmaddr, seg_to_remove->vmsize);
- }
-#else
-#error arch
-#endif
} else {
OSKextLog(/* kext */ NULL,
- kOSKextLogBasicLevel |
- kOSKextLogGeneralFlag,
- "keepsyms boot arg specified; keeping linkedit segment for symbols.");
+ kOSKextLogBasicLevel |
+ kOSKextLogGeneralFlag,
+ "keepsyms boot arg specified; keeping linkedit segment for symbols.");
}
+#endif /* CONFIG_KXLD */
seg_to_remove = NULL;
finish:
IORecursiveLockUnlock(sKextLock);
- OSSafeRelease(prelinkedKexts);
- OSSafeRelease(kextIterator);
- OSSafeRelease(prelinkIterator);
+ OSSafeReleaseNULL(prelinkedKexts);
+ OSSafeReleaseNULL(kextIterator);
+ OSSafeReleaseNULL(prelinkIterator);
return;
}
{
IORecursiveLockLock(sKextLock);
sKextdActive = active;
- if (sPrelinkedPersonalities) {
- gIOCatalogue->removePersonalities(sPrelinkedPersonalities);
- OSSafeReleaseNULL(sPrelinkedPersonalities);
+ if (sKernelRequests->getCount()) {
+ OSKext::pingKextd();
}
IORecursiveLockUnlock(sKextLock);
return;
}
+/*********************************************************************
+* OSKextLib.cpp might need access to this someday but for now it's
+* private.
+*********************************************************************/
+extern "C" {
+extern void ipc_port_release_send(ipc_port_t);
+};
+
+/* static */
+OSReturn
+OSKext::pingKextd(void)
+{
+ OSReturn result = kOSReturnError;
+#if !NO_KEXTD
+ mach_port_t kextd_port = IPC_PORT_NULL;
+
+ if (!sKextdActive) {
+ result = kOSKextReturnDisabled; // basically unavailable
+ goto finish;
+ }
+
+ result = host_get_kextd_port(host_priv_self(), &kextd_port);
+ if (result != KERN_SUCCESS || !IPC_PORT_VALID(kextd_port)) {
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogErrorLevel |
+ kOSKextLogIPCFlag,
+ "Can't get kextd port.");
+ goto finish;
+ }
+
+ result = kextd_ping(kextd_port);
+ if (result != KERN_SUCCESS) {
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogErrorLevel |
+ kOSKextLogIPCFlag,
+ "kextd ping failed (0x%x).", (int)result);
+ goto finish;
+ }
+
+finish:
+ if (IPC_PORT_VALID(kextd_port)) {
+ ipc_port_release_send(kextd_port);
+ }
+#endif
+
+ return result;
+}
+
/*********************************************************************
*********************************************************************/
/* static */
void
OSKext::willShutdown(void)
{
+#if !NO_KEXTD
OSReturn checkResult = kOSReturnError;
+#endif
OSDictionary * exitRequest = NULL; // must release
IORecursiveLockLock(sKextLock);
OSKext::setAutounloadsEnabled(false);
OSKext::setKernelRequestsEnabled(false);
+#if !NO_KEXTD
OSKextLog(/* kext */ NULL,
kOSKextLogProgressLevel |
kOSKextLogGeneralFlag,
goto finish;
}
- OSKextPingKextd();
+ OSKext::pingKextd();
finish:
+#endif
+
IORecursiveLockUnlock(sKextLock);
- OSSafeRelease(exitRequest);
+ OSSafeReleaseNULL(exitRequest);
return;
}
*********************************************************************/
OSKext *
OSKext::withPrelinkedInfoDict(
- OSDictionary * anInfoDict)
+ OSDictionary * anInfoDict,
+ bool doCoalesedSlides)
{
OSKext * newKext = new OSKext;
- if (newKext && !newKext->initWithPrelinkedInfoDict(anInfoDict)) {
+ if (newKext && !newKext->initWithPrelinkedInfoDict(anInfoDict, doCoalesedSlides)) {
newKext->release();
return NULL;
}
*********************************************************************/
bool
OSKext::initWithPrelinkedInfoDict(
- OSDictionary * anInfoDict)
+ OSDictionary * anInfoDict,
+ bool doCoalesedSlides)
{
bool result = false;
- kern_return_t alloc_result = KERN_SUCCESS;
OSString * kextPath = NULL; // do not release
OSNumber * addressNum = NULL; // reused; do not release
OSNumber * lengthNum = NULL; // reused; do not release
void * data = NULL; // do not free
void * srcData = NULL; // do not free
OSData * prelinkedExecutable = NULL; // must release
- void * linkStateCopy = NULL; // kmem_free on error
- uint32_t linkStateLength = 0;
uint32_t length = 0; // reused
if (!super::init()) {
if (!setInfoDictionaryAndPath(anInfoDict, kextPath)) {
goto finish;
}
+#if KASLR_KEXT_DEBUG
+ IOLog("kaslr: doCoalesedSlides %d kext %s \n", doCoalesedSlides, getIdentifierCString());
+#endif
- /* Don't need the path to be in the info dictionary any more.
+ /* Also get the executable's bundle-relative path if present.
+ * Don't look for an arch-specific path property.
*/
- anInfoDict->removeObject(kPrelinkBundlePathKey);
+ executableRelPath = OSDynamicCast(OSString,
+ anInfoDict->getObject(kPrelinkExecutableRelativePathKey));
+ if (executableRelPath) {
+ executableRelPath->retain();
+ }
- /* If we have a link state, create an OSData wrapper for it.
+ /* Don't need the paths to be in the info dictionary any more.
*/
- addressNum = OSDynamicCast(OSNumber,
- anInfoDict->getObject(kPrelinkLinkStateKey));
- if (addressNum) {
- lengthNum = OSDynamicCast(OSNumber,
- anInfoDict->getObject(kPrelinkLinkStateSizeKey));
- if (!lengthNum) {
- OSKextLog(this,
- kOSKextLogErrorLevel |
- kOSKextLogArchiveFlag,
- "Kext %s can't find prelinked kext link state size.",
- getIdentifierCString());
- goto finish;
- }
-
- data = (void *) (intptr_t) (addressNum->unsigned64BitValue());
- linkStateLength = (uint32_t) (lengthNum->unsigned32BitValue());
-
- anInfoDict->removeObject(kPrelinkLinkStateKey);
- anInfoDict->removeObject(kPrelinkLinkStateSizeKey);
-
- /* Copy the link state out of the booter-provided memory so it is in
- * the VM system and we can page it out.
- */
- alloc_result = kmem_alloc_pageable(kernel_map,
- (vm_offset_t *)&linkStateCopy, linkStateLength);
- if (alloc_result != KERN_SUCCESS) {
- OSKextLog(this,
- kOSKextLogErrorLevel |
- kOSKextLogArchiveFlag,
- "Kext %s failed to copy prelinked link state.",
- getIdentifierCString());
- goto finish;
- }
- memcpy(linkStateCopy, data, linkStateLength);
-
- linkState = OSData::withBytesNoCopy(linkStateCopy, linkStateLength);
- if (!linkState) {
- OSKextLog(this,
- kOSKextLogErrorLevel |
- kOSKextLogArchiveFlag,
- "Kext %s failed to create link state wrapper.",
- getIdentifierCString());
- goto finish;
- }
- linkState->setDeallocFunction(osdata_kmem_free);
-
- /* Clear linkStateCopy; the OSData owns it now so we mustn't free it.
- */
- linkStateCopy = NULL;
- }
+ anInfoDict->removeObject(kPrelinkBundlePathKey);
+ anInfoDict->removeObject(kPrelinkExecutableRelativePathKey);
/* Create an OSData wrapper around the linked executable.
*/
goto finish;
}
- data = (void *) (intptr_t) (addressNum->unsigned64BitValue());
+ data = (void *) ((intptr_t) (addressNum->unsigned64BitValue()) + vm_kernel_slide);
length = (uint32_t) (lengthNum->unsigned32BitValue());
+#if KASLR_KEXT_DEBUG
+ IOLog("kaslr: unslid 0x%lx slid 0x%lx length %u - prelink executable \n",
+ (unsigned long)VM_KERNEL_UNSLIDE(data),
+ (unsigned long)data,
+ length);
+#endif
+
anInfoDict->removeObject(kPrelinkExecutableLoadKey);
anInfoDict->removeObject(kPrelinkExecutableSizeKey);
- /* If the kext's load address differs from its source address, allocate
- * space in the kext map at the load address and copy the kext over.
- */
+ /* If the kext's load address differs from its source address, allocate
+ * space in the kext map at the load address and copy the kext over.
+ */
addressNum = OSDynamicCast(OSNumber, anInfoDict->getObject(kPrelinkExecutableSourceKey));
if (addressNum) {
- srcData = (void *) (intptr_t) (addressNum->unsigned64BitValue());
-
+ srcData = (void *) ((intptr_t) (addressNum->unsigned64BitValue()) + vm_kernel_slide);
+
+#if KASLR_KEXT_DEBUG
+ IOLog("kaslr: unslid 0x%lx slid 0x%lx - prelink executable source \n",
+ (unsigned long)VM_KERNEL_UNSLIDE(srcData),
+ (unsigned long)srcData);
+#endif
+
if (data != srcData) {
#if __LP64__
+ kern_return_t alloc_result;
+
alloc_result = kext_alloc((vm_offset_t *)&data, length, /* fixed */ TRUE);
if (alloc_result != KERN_SUCCESS) {
OSKextLog(this,
- kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
- "Failed to allocate space for prelinked kext %s.",
- getIdentifierCString());
+ kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
+ "Failed to allocate space for prelinked kext %s.",
+ getIdentifierCString());
goto finish;
}
memcpy(data, srcData, length);
#else
OSKextLog(this,
- kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
- "Error: prelinked kext %s - source and load addresses "
- "differ on ILP32 architecture.",
- getIdentifierCString());
+ kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
+ "Error: prelinked kext %s - source and load addresses "
+ "differ on ILP32 architecture.",
+ getIdentifierCString());
goto finish;
#endif /* __LP64__ */
}
-
+
anInfoDict->removeObject(kPrelinkExecutableSourceKey);
}
- /* We don't need to set a dealloc function for the linked executable
- * because it is freed separately in OSKext::unload(), which must unwire
- * part of the memory.
- * xxx - do we *have* to do it that way?
- */
prelinkedExecutable = OSData::withBytesNoCopy(data, length);
if (!prelinkedExecutable) {
OSKextLog(this,
getIdentifierCString());
goto finish;
}
- setLinkedExecutable(prelinkedExecutable);
+#if VM_MAPPED_KEXTS
+ prelinkedExecutable->setDeallocFunction(osdata_kext_free);
+#else
+ prelinkedExecutable->setDeallocFunction(osdata_phys_free);
+#endif
+ setLinkedExecutable(prelinkedExecutable);
addressNum = OSDynamicCast(OSNumber,
anInfoDict->getObject(kPrelinkKmodInfoKey));
if (!addressNum) {
goto finish;
}
- kmod_info = (kmod_info_t *) (intptr_t) (addressNum->unsigned64BitValue());
+ if (addressNum->unsigned64BitValue() != 0) {
+ kmod_info = (kmod_info_t *) (intptr_t) (addressNum->unsigned64BitValue() + vm_kernel_slide);
+ kmod_info->address += vm_kernel_slide;
+#if KASLR_KEXT_DEBUG
+ IOLog("kaslr: unslid 0x%lx slid 0x%lx - kmod_info \n",
+ (unsigned long)VM_KERNEL_UNSLIDE(kmod_info),
+ (unsigned long)kmod_info);
+ IOLog("kaslr: unslid 0x%lx slid 0x%lx - kmod_info->address \n",
+ (unsigned long)VM_KERNEL_UNSLIDE(kmod_info->address),
+ (unsigned long)kmod_info->address);
+ #endif
+ }
anInfoDict->removeObject(kPrelinkKmodInfoKey);
}
}
}
+ result = slidePrelinkedExecutable(doCoalesedSlides);
+ if (result != kOSReturnSuccess) {
+ goto finish;
+ }
+
+ if (doCoalesedSlides == false) {
+ /* set VM protections now, wire later at kext load */
+ result = setVMAttributes(true, false);
+ if (result != KERN_SUCCESS) {
+ goto finish;
+ }
+ }
+
flags.prelinked = true;
/* If we created a kext from prelink info,
result = registerIdentifier();
finish:
+ OSSafeReleaseNULL(prelinkedExecutable);
- /* If we didn't hand linkStateCopy off to an OSData, free it.
- */
- if (linkStateCopy) {
- kmem_free(kernel_map, (vm_offset_t)linkStateCopy, linkStateLength);
+ return result;
+}
+
+/*********************************************************************
+ *********************************************************************/
+/* static */
+void OSKext::setAllVMAttributes(void)
+{
+ OSCollectionIterator * kextIterator = NULL; // must release
+ const OSSymbol * thisID = NULL; // do not release
+
+ IORecursiveLockLock(sKextLock);
+
+ kextIterator = OSCollectionIterator::withCollection(sKextsByID);
+ if (!kextIterator) {
+ goto finish;
+ }
+
+ while ((thisID = OSDynamicCast(OSSymbol, kextIterator->getNextObject()))) {
+ OSKext * thisKext; // do not release
+
+ thisKext = OSDynamicCast(OSKext, sKextsByID->getObject(thisID));
+ if (!thisKext || thisKext->isInterface() || !thisKext->declaresExecutable()) {
+ continue;
+ }
+
+ /* set VM protections now, wire later at kext load */
+ thisKext->setVMAttributes(true, false);
}
- OSSafeRelease(prelinkedExecutable);
+finish:
+ IORecursiveLockUnlock(sKextLock);
+ OSSafeReleaseNULL(kextIterator);
- return result;
+ return;
}
/*********************************************************************
void * executableAddr = NULL; // do not free
char * bundlePathAddr = NULL; // do not free
- OSObject * parsedXML = NULL; // must release
+ OSObject * parsedXML = NULL; // must release
OSDictionary * theInfoDict = NULL; // do not release
OSString * kextPath = NULL; // must release
OSString * errorString = NULL; // must release
result = registerIdentifier();
finish:
- OSSafeRelease(parsedXML);
- OSSafeRelease(kextPath);
- OSSafeRelease(errorString);
- OSSafeRelease(executable);
+ OSSafeReleaseNULL(parsedXML);
+ OSSafeReleaseNULL(kextPath);
+ OSSafeReleaseNULL(errorString);
+ OSSafeReleaseNULL(executable);
return result;
}
OSData * newUUID = NULL; // must release
OSData * existingUUID = NULL; // must release
+ IORecursiveLockLock(sKextLock);
+
/* Get the new kext's version for checks & log messages.
*/
newVersion = getVersion();
finish:
+ IORecursiveLockUnlock(sKextLock);
+
if (result) {
OSKextLog(this,
kOSKextLogStepLevel |
getIdentifierCString(), newVersionCString);
}
- OSSafeRelease(newUUID);
- OSSafeRelease(existingUUID);
+ OSSafeReleaseNULL(newUUID);
+ OSSafeReleaseNULL(existingUUID);
return result;
}
/*********************************************************************
* Does the bare minimum validation to look up a kext.
* All other validation is done on the spot as needed.
-*
-* No need for lock, only called from init
**********************************************************************/
bool
OSKext::setInfoDictionaryAndPath(
OSDictionary * aDictionary,
OSString * aPath)
{
- bool result = false;
- OSString * bundleIDString = NULL; // do not release
- OSString * versionString = NULL; // do not release
- OSString * compatibleVersionString = NULL; // do not release
- const char * versionCString = NULL; // do not free
- const char * compatibleVersionCString = NULL; // do not free
- OSBoolean * scratchBool = NULL; // do not release
+ bool result = false;
+ OSString * bundleIDString = NULL; // do not release
+ OSString * versionString = NULL; // do not release
+ OSString * compatibleVersionString = NULL; // do not release
+ const char * versionCString = NULL; // do not free
+ const char * compatibleVersionCString = NULL; // do not free
+ OSBoolean * scratchBool = NULL; // do not release
+ OSDictionary * scratchDict = NULL; // do not release
if (infoDict) {
panic("Attempt to set info dictionary on a kext "
goto finish;
}
}
+
+ /* Check to see if this kext is in exclude list */
+ if ( isInExcludeList() ) {
+ OSKextLog(this,
+ kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
+ "Kext %s is in exclude list, not loadable",
+ getIdentifierCString());
+ goto finish;
+ }
/* Set flags for later use if the infoDict gets flushed. We only
* check for true values, not false ones(!)
*/
scratchBool = OSDynamicCast(OSBoolean,
getPropertyForHostArch(kOSBundleIsInterfaceKey));
- if (scratchBool && scratchBool->isTrue()) {
+ if (scratchBool == kOSBooleanTrue) {
flags.interface = 1;
}
scratchBool = OSDynamicCast(OSBoolean,
getPropertyForHostArch(kOSKernelResourceKey));
- if (scratchBool && scratchBool->isTrue()) {
+ if (scratchBool == kOSBooleanTrue) {
flags.kernelComponent = 1;
flags.interface = 1; // xxx - hm. the kernel itself isn't an interface...
flags.started = 1;
flags.hasAllDependencies = 1;
}
+ /* Make sure common string values in personalities are uniqued to OSSymbols.
+ */
+ scratchDict = OSDynamicCast(OSDictionary,
+ getPropertyForHostArch(kIOKitPersonalitiesKey));
+ if (scratchDict) {
+ uniquePersonalityProperties(scratchDict);
+ }
+
result = true;
finish:
result = true;
goto finish;
}
-
+
if (infoDict->getObject(_kOSKextExecutableKey) ||
infoDict->getObject(_kOSKextMkextExecutableReferenceKey)) {
/*********************************************************************
*********************************************************************/
-void
-OSKext::free(void)
+static void
+uniqueStringPlistProperty(OSDictionary * dict, const char * key)
+{
+ OSString * stringValue = NULL; // do not release
+ const OSSymbol * symbolValue = NULL; // must release
+
+ stringValue = OSDynamicCast(OSString, dict->getObject(key));
+ if (!stringValue) {
+ goto finish;
+ }
+
+ symbolValue = OSSymbol::withString(stringValue);
+ if (!symbolValue) {
+ goto finish;
+ }
+
+ dict->setObject(key, symbolValue);
+
+finish:
+ if (symbolValue) symbolValue->release();
+
+ return;
+}
+
+/*********************************************************************
+*********************************************************************/
+static void
+uniqueStringPlistProperty(OSDictionary * dict, const OSString * key)
+{
+ OSString * stringValue = NULL; // do not release
+ const OSSymbol * symbolValue = NULL; // must release
+
+ stringValue = OSDynamicCast(OSString, dict->getObject(key));
+ if (!stringValue) {
+ goto finish;
+ }
+
+ symbolValue = OSSymbol::withString(stringValue);
+ if (!symbolValue) {
+ goto finish;
+ }
+
+ dict->setObject(key, symbolValue);
+
+finish:
+ if (symbolValue) symbolValue->release();
+
+ return;
+}
+
+/*********************************************************************
+* Replace common personality property values with uniqued instances
+* to save on wired memory.
+*********************************************************************/
+/* static */
+void
+OSKext::uniquePersonalityProperties(OSDictionary * personalityDict)
+{
+ /* Properties every personality has.
+ */
+ uniqueStringPlistProperty(personalityDict, kCFBundleIdentifierKey);
+ uniqueStringPlistProperty(personalityDict, kIOProviderClassKey);
+ uniqueStringPlistProperty(personalityDict, gIOClassKey);
+
+ /* Other commonly used properties.
+ */
+ uniqueStringPlistProperty(personalityDict, gIOMatchCategoryKey);
+ uniqueStringPlistProperty(personalityDict, gIOResourceMatchKey);
+ uniqueStringPlistProperty(personalityDict, gIOUserClientClassKey);
+
+ uniqueStringPlistProperty(personalityDict, "HIDDefaultBehavior");
+ uniqueStringPlistProperty(personalityDict, "HIDPointerAccelerationType");
+ uniqueStringPlistProperty(personalityDict, "HIDRemoteControlType");
+ uniqueStringPlistProperty(personalityDict, "HIDScrollAccelerationType");
+ uniqueStringPlistProperty(personalityDict, "IOPersonalityPublisher");
+ uniqueStringPlistProperty(personalityDict, "Physical Interconnect");
+ uniqueStringPlistProperty(personalityDict, "Physical Interconnect Location");
+ uniqueStringPlistProperty(personalityDict, "Vendor");
+ uniqueStringPlistProperty(personalityDict, "Vendor Identification");
+ uniqueStringPlistProperty(personalityDict, "Vendor Name");
+ uniqueStringPlistProperty(personalityDict, "bConfigurationValue");
+ uniqueStringPlistProperty(personalityDict, "bInterfaceNumber");
+ uniqueStringPlistProperty(personalityDict, "idProduct");
+
+ return;
+}
+
+/*********************************************************************
+*********************************************************************/
+void
+OSKext::free(void)
{
if (isLoaded()) {
panic("Attempt to free loaded kext %s.", getIdentifierCString());
}
- OSSafeRelease(infoDict);
- OSSafeRelease(bundleID);
- OSSafeRelease(path);
- OSSafeRelease(dependencies);
- OSSafeRelease(linkState);
- OSSafeRelease(linkedExecutable);
- OSSafeRelease(metaClasses);
- OSSafeRelease(interfaceUUID);
+ OSSafeReleaseNULL(infoDict);
+ OSSafeReleaseNULL(bundleID);
+ OSSafeReleaseNULL(path);
+ OSSafeReleaseNULL(executableRelPath);
+ OSSafeReleaseNULL(dependencies);
+ OSSafeReleaseNULL(linkedExecutable);
+ OSSafeReleaseNULL(metaClasses);
+ OSSafeReleaseNULL(interfaceUUID);
if (isInterface() && kmod_info) {
kfree(kmod_info, sizeof(kmod_info_t));
"Mkext archive too small to be valid.");
goto finish;
}
-
+
mkextHeader = (mkext_header *)mkextData->getBytesNoCopy();
if (MKEXT_GET_MAGIC(mkextHeader) != MKEXT_MAGIC ||
if (mkextVersion == MKEXT_VERS_2) {
result = OSKext::readMkext2Archive(mkextData, NULL, checksumPtr);
- } else if (mkextVersion == MKEXT_VERS_1) {
- result = OSKext::readMkext1Archive(mkextData, checksumPtr);
} else {
OSKextLog(/* kext */ NULL,
kOSKextLogErrorLevel |
return result;
}
-/*********************************************************************
-* Assumes magic, signature, version, length have been checked.
-*
-* Doesn't do as much bounds-checking as it should, but we're dropping
-* mkext1 support from the kernel for SnowLeopard soon.
-*
-* Should keep track of all kexts created so far, and if we hit a
-* fatal error halfway through, remove those kexts. If we've dropped
-* an older version that had already been read, whoops! Might want to
-* add a level of buffering?
-*********************************************************************/
-/* static */
-OSReturn
-OSKext::readMkext1Archive(
- OSData * mkextData,
- uint32_t * checksumPtr)
-{
- OSReturn result = kOSReturnError;
- uint32_t mkextLength;
- mkext1_header * mkextHeader = 0; // do not free
- void * mkextEnd = 0; // do not free
- uint32_t mkextVersion;
- uint8_t * crc_address = 0;
- uint32_t checksum;
- uint32_t numKexts = 0;
-
- OSData * infoDictDataObject = NULL; // must release
- OSObject * parsedXML = NULL; // must release
- OSDictionary * infoDict = NULL; // do not release
- OSString * errorString = NULL; // must release
- OSData * mkextExecutableInfo = NULL; // must release
- OSKext * theKext = NULL; // must release
-
- mkextLength = mkextData->getLength();
- mkextHeader = (mkext1_header *)mkextData->getBytesNoCopy();
- mkextEnd = (char *)mkextHeader + mkextLength;
- mkextVersion = OSSwapBigToHostInt32(mkextHeader->version);
-
- crc_address = (u_int8_t *)&mkextHeader->version;
- checksum = mkext_adler32(crc_address,
- (uintptr_t)mkextHeader +
- OSSwapBigToHostInt32(mkextHeader->length) - (uintptr_t)crc_address);
-
- if (OSSwapBigToHostInt32(mkextHeader->adler32) != checksum) {
- OSKextLog(/* kext */ NULL,
- kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
- "Kext archive has a bad checksum.");
- result = kOSKextReturnBadData;
- goto finish;
- }
-
- if (checksumPtr) {
- *checksumPtr = checksum;
- }
-
- /* Check that the CPU type & subtype match that of the running kernel. */
- if (OSSwapBigToHostInt32(mkextHeader->cputype) != (UInt32)CPU_TYPE_ANY) {
- if ((UInt32)_mh_execute_header.cputype !=
- OSSwapBigToHostInt32(mkextHeader->cputype)) {
-
- OSKextLog(/* kext */ NULL,
- kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
- "Kext archive doesn't contain software "
- "for this computer's CPU type.");
- result = kOSKextReturnArchNotFound;
- goto finish;
- }
- }
-
- numKexts = OSSwapBigToHostInt32(mkextHeader->numkexts);
-
- for (uint32_t i = 0; i < numKexts; i++) {
-
- OSSafeReleaseNULL(infoDictDataObject);
- OSSafeReleaseNULL(infoDict);
- OSSafeReleaseNULL(mkextExecutableInfo);
- OSSafeReleaseNULL(errorString);
- OSSafeReleaseNULL(theKext);
-
- mkext_kext * kextEntry = &mkextHeader->kext[i];
- mkext_file * infoDictPtr = &kextEntry->plist;
- mkext_file * executablePtr = &kextEntry->module;
- if (kextEntry >= mkextEnd) {
- OSKextLog(/* kext */ NULL,
- kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
- "Mkext file overrun.");
- result = kOSKextReturnBadData;
- goto finish;
- }
-
- /* Note that we're pretty tolerant of errors in individual entries.
- * As long as we can keep processing, we do.
- */
- infoDictDataObject = OSKext::extractMkext1Entry(
- mkextHeader, infoDictPtr);
- if (!infoDictDataObject) {
- OSKextLog(/* kext */ NULL,
- kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
- "Can't uncompress info dictionary "
- "from mkext archive entry %d.", i);
- continue;
- }
-
- parsedXML = OSUnserializeXML(
- (const char *)infoDictDataObject->getBytesNoCopy(),
- &errorString);
- if (parsedXML) {
- infoDict = OSDynamicCast(OSDictionary, parsedXML);
- }
- if (!infoDict) {
- const char * errorCString = "(unknown error)";
-
- if (errorString && errorString->getCStringNoCopy()) {
- errorCString = errorString->getCStringNoCopy();
- } else if (parsedXML) {
- errorCString = "not a dictionary";
- }
- OSKextLog(/* kext */ NULL,
- kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
- "Error: Can't read XML property list "
- "for mkext archive entry %d: %s.", i, errorCString);
- continue;
- }
-
- theKext = new OSKext;
- if (!theKext) {
- OSKextLog(/* kext */ NULL,
- kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
- "Kext allocation failure.");
- continue;
- }
-
- /*****
- * Prepare an entry to hold the mkext entry info for the
- * compressed binary module, if there is one. If all four fields
- * of the module entry are zero, there isn't one.
- */
- if ((OSSwapBigToHostInt32(executablePtr->offset) ||
- OSSwapBigToHostInt32(executablePtr->compsize) ||
- OSSwapBigToHostInt32(executablePtr->realsize) ||
- OSSwapBigToHostInt32(executablePtr->modifiedsecs))) {
-
- MkextEntryRef entryRef;
-
- mkextExecutableInfo = OSData::withCapacity(sizeof(entryRef));
- if (!mkextExecutableInfo) {
- panic("Error: Couldn't allocate data object "
- "for mkext archive entry %d.\n", i);
- }
-
- entryRef.mkext = (mkext_basic_header *)mkextHeader;
- entryRef.fileinfo = (uint8_t *)executablePtr;
- if (!mkextExecutableInfo->appendBytes(&entryRef,
- sizeof(entryRef))) {
-
- OSKextLog(/* kext */ NULL,
- kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
- "Couldn't record executable info "
- "for mkext archive entry %d.", i);
- // we might hit a load error later but oh well
- // xxx - should probably remove theKext
- continue;
- }
-
- }
-
- /* Init can fail because of a data/runtime error, or because the
- * kext is a dup. Either way, we don't care here.
- */
- if (!theKext->initWithMkext1Info(infoDict, mkextExecutableInfo,
- mkextData)) {
-
- // theKext is released at the top of the loop or in the finish block
- continue;
- }
-
- /* If we got even one kext out of the mkext archive,
- * we have successfully read the archive, in that we
- * have data references into its mapped memory.
- */
- result = kOSReturnSuccess;
- }
-
-finish:
-
- OSSafeRelease(infoDictDataObject);
- OSSafeRelease(parsedXML);
- OSSafeRelease(errorString);
- OSSafeRelease(mkextExecutableInfo);
- OSSafeRelease(theKext);
-
- return result;
-}
-
-/*********************************************************************
-*********************************************************************/
-bool
-OSKext::initWithMkext1Info(
- OSDictionary * anInfoDict,
- OSData * executableWrapper,
- OSData * mkextData)
-{
- bool result = false;
-
- // mkext1 doesn't allow for path (might stuff in info dict)
- if (!setInfoDictionaryAndPath(anInfoDict, /* path */ NULL)) {
- goto finish;
- }
-
- if (!registerIdentifier()) {
- goto finish;
- }
-
- if (!setExecutable(executableWrapper, mkextData, true)) {
- goto finish;
- }
-
- result = true;
-
-finish:
-
- /* If we can't init, remove the kext from the lookup dictionary.
- * This is safe to call in init because there's an implicit retain.
- */
- if (!result) {
- OSKext::removeKext(this, /* removePersonalities? */ false);
- }
-
- return result;
-}
-
-/*********************************************************************
-* xxx - this should take the input data length
-*********************************************************************/
-/* static */
-OSData *
-OSKext::extractMkext1Entry(
- const void * mkextFileBase,
- const void * entry)
-{
- OSData * result = NULL;
- OSData * uncompressedData = NULL; // release on error
- const char * errmsg = NULL;
-
- mkext_file * fileinfo;
- uint8_t * uncompressedDataBuffer = 0; // do not free (panic on alloc. fail)
- size_t uncompressed_size = 0;
- kern_return_t kern_result;
-
- fileinfo = (mkext_file *)entry;
-
- size_t offset = OSSwapBigToHostInt32(fileinfo->offset);
- size_t compressed_size = OSSwapBigToHostInt32(fileinfo->compsize);
- size_t expected_size = OSSwapBigToHostInt32(fileinfo->realsize);
-
- // Add 1 for '\0' to terminate XML string (for plists)
- // (we really should have the archive format include that).
- size_t alloc_size = expected_size + 1;
- time_t modifiedsecs = OSSwapBigToHostInt32(fileinfo->modifiedsecs);
-
- /* If these four fields are zero there's no file, but it's up to
- * the calling context to decide if that's an error.
- */
- if (offset == 0 && compressed_size == 0 &&
- expected_size == 0 && modifiedsecs == 0) {
- goto finish;
- }
-
- kern_result = kmem_alloc(kernel_map,
- (vm_offset_t *)&uncompressedDataBuffer,
- alloc_size);
- if (kern_result != KERN_SUCCESS) {
- panic(ALLOC_FAIL);
- goto finish;
- }
-
- uncompressedData = OSData::withBytesNoCopy(uncompressedDataBuffer,
- alloc_size);
- if (uncompressedData == NULL) {
- /* No need to free uncompressedDataBuffer here, either. */
- panic(ALLOC_FAIL);
- goto finish;
- }
- uncompressedData->setDeallocFunction(&osdata_kmem_free);
-
- /* Do the decompression if necessary. Note that even if the file isn't
- * compressed, we want to make a copy so that we don't have the tie to
- * the larger mkext file buffer any more.
- * xxx - need to detect decompression overflow too
- */
- if (compressed_size != 0) {
- errmsg = "OSKext::uncompressMkext - "
- "uncompressed file shorter than expected";
- uncompressed_size = decompress_lzss(uncompressedDataBuffer,
- expected_size,
- ((uint8_t *)mkextFileBase) + offset,
- compressed_size);
- if (uncompressed_size != expected_size) {
- goto finish;
- }
- } else {
- memcpy(uncompressedDataBuffer,
- ((uint8_t *)mkextFileBase) + offset,
- expected_size);
- }
-
- // Add a terminating nul character in case the data is XML.
- // (we really should have the archive format include that).
- uncompressedDataBuffer[expected_size] = '\0';
-
- result = uncompressedData;
- errmsg = NULL;
-
-finish:
- if (!result) {
- OSKextLog(/* kext */ NULL,
- kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
- "%s", errmsg);
-
- if (uncompressedData) {
- uncompressedData->release();
- }
- }
- return result;
-}
-
/*********************************************************************
* Assumes magic, signature, version, length have been checked.
* xxx - need to add further bounds checking for each file entry
OSString * errorString = NULL; // must release
OSData * mkextPlistUncompressedData = NULL; // must release
const char * mkextPlistDataBuffer = NULL; // do not free
- OSObject * parsedXML = NULL; // must release
+ OSObject * parsedXML = NULL; // must release
OSDictionary * mkextPlist = NULL; // do not release
OSArray * mkextInfoDictArray = NULL; // do not release
uint32_t count, i;
infoDict = OSDynamicCast(OSDictionary,
- mkextInfoDictArray->getObject(i));
-
+ mkextInfoDictArray->getObject(i));
+
/* Create the kext for the entry, then release it, because the
* kext system keeps them around until explicitly removed.
* Any creation/registration failures are already logged for us.
*/
- OSKext * newKext = OSKext::withMkext2Info(infoDict, mkextData);
- OSSafeRelease(newKext);
+ if (infoDict) {
+ OSKext * newKext = OSKext::withMkext2Info(infoDict, mkextData);
+ OSSafeReleaseNULL(newKext);
+ }
}
/* Even if we didn't keep any kexts from the mkext, we may have a load
finish:
- OSSafeRelease(parsedXML);
- OSSafeRelease(mkextPlistUncompressedData);
- OSSafeRelease(errorString);
+ OSSafeReleaseNULL(parsedXML);
+ OSSafeReleaseNULL(mkextPlistUncompressedData);
+ OSSafeReleaseNULL(errorString);
return result;
}
OSCollectionIterator * iterator = NULL; // must release
OSData * executable = NULL; // must release
- if (!super::init()) {
+ if (anInfoDict == NULL || !super::init()) {
goto finish;
}
/* Get the path. Don't look for an arch-specific path property.
*/
kextPath = OSDynamicCast(OSString,
- anInfoDict->getObject(kMKEXTBundlePathKey));
+ anInfoDict->getObject(kMKEXTBundlePathKey));
if (!setInfoDictionaryAndPath(anInfoDict, kextPath)) {
goto finish;
}
- /* Don't need the path to be in the info dictionary any more.
+ /* If we have a path to the executable, save it.
+ */
+ executableRelPath = OSDynamicCast(OSString,
+ anInfoDict->getObject(kMKEXTExecutableRelativePathKey));
+ if (executableRelPath) {
+ executableRelPath->retain();
+ }
+
+ /* Don't need the paths to be in the info dictionary any more.
*/
anInfoDict->removeObject(kMKEXTBundlePathKey);
+ anInfoDict->removeObject(kMKEXTExecutableRelativePathKey);
executableOffsetNum = OSDynamicCast(OSNumber,
infoDict->getObject(kMKEXTExecutableKey));
finish:
- OSSafeRelease(executable);
- OSSafeRelease(iterator);
+ OSSafeReleaseNULL(executable);
+ OSSafeReleaseNULL(iterator);
return result;
}
{
void * result = NULL;
z_mem * zmem = NULL;
- uint32_t total = num_items * size;
- uint32_t allocSize = total + sizeof(zmem);
+
+ uint64_t total = ((uint64_t)num_items) * ((uint64_t)size);
+ //Check for overflow due to multiplication
+ if (total > UINT32_MAX){
+ panic("z_alloc(%p, %x, %x): overflow caused by %x * %x\n",
+ notused, num_items, size, num_items, size);
+ }
- zmem = (z_mem *)kalloc(allocSize);
+ uint64_t allocSize64 = total + ((uint64_t)sizeof(zmem));
+ //Check for overflow due to addition
+ if (allocSize64 > UINT32_MAX){
+ panic("z_alloc(%p, %x, %x): overflow caused by %x + %lx\n",
+ notused, num_items, size, (uint32_t)total, sizeof(zmem));
+ }
+ uint32_t allocSize = (uint32_t)allocSize64;
+
+ zmem = (z_mem *)kalloc_tag(allocSize, VM_KERN_MEMORY_OSKEXT);
if (!zmem) {
goto finish;
}
}
if (KERN_SUCCESS != kmem_alloc(kernel_map,
- (vm_offset_t*)&uncompressedDataBuffer, fullSize)) {
+ (vm_offset_t*)&uncompressedDataBuffer, fullSize, VM_KERN_MEMORY_OSKEXT)) {
/* How's this for cheesy? The kernel is only asked to extract
* kext plists so we tailor the log messages.
*/
- if (this == sKernelKext) {
+ if (isKernel()) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
}
uncompressedData = OSData::withBytesNoCopy(uncompressedDataBuffer, fullSize);
if (!uncompressedData) {
- if (this == sKernelKext) {
+ if (isKernel()) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
}
uncompressedData->setDeallocFunction(&osdata_kmem_free);
- if (this == sKernelKext) {
+ if (isKernel()) {
OSKextLog(this,
kOSKextLogDetailLevel |
kOSKextLogArchiveFlag,
zlib_result = inflateInit(&zstream);
if (Z_OK != zlib_result) {
- if (this == sKernelKext) {
+ if (isKernel()) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
if (zlib_result == Z_STREAM_END || zlib_result == Z_OK) {
uncompressedSize = zstream.total_out;
} else {
- if (this == sKernelKext) {
+ if (isKernel()) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
}
if (uncompressedSize != fullSize) {
- if (this == sKernelKext) {
+ if (isKernel()) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
if (zstream_inited) inflateEnd(&zstream);
if (!result) {
- OSSafeRelease(uncompressedData);
+ OSSafeReleaseNULL(uncompressedData);
}
return result;
Boolean delayAutounload = false;
OSKextExcludeLevel startKextExcludeLevel = kOSKextExcludeNone;
OSKextExcludeLevel startMatchingExcludeLevel = kOSKextExcludeAll;
-
+
IORecursiveLockLock(sKextLock);
if (logInfoOut) {
}
startKextExcludeNum = OSDynamicCast(OSNumber,
- requestArgs->getObject(kKextKextRequestArgumentStartExcludeKey));
+ requestArgs->getObject(kKextRequestArgumentStartExcludeKey));
startMatchingExcludeNum = OSDynamicCast(OSNumber,
requestArgs->getObject(kKextRequestArgumentStartMatchingExcludeKey));
delayAutounloadBool = OSDynamicCast(OSBoolean,
IORecursiveLockUnlock(sKextLock);
- OSSafeRelease(mkextData);
- OSSafeRelease(mkextPlist);
- OSSafeRelease(serializer);
- OSSafeRelease(logInfoArray);
+ OSSafeReleaseNULL(mkextData);
+ OSSafeReleaseNULL(mkextPlist);
+ OSSafeReleaseNULL(serializer);
+ OSSafeReleaseNULL(logInfoArray);
return result;
}
logInfo = serializer->text();
logInfoLength = serializer->getLength();
- kmem_result = kmem_alloc(kernel_map, (vm_offset_t *)&buffer, logInfoLength);
+ kmem_result = kmem_alloc(kernel_map, (vm_offset_t *)&buffer, round_page(logInfoLength), VM_KERN_MEMORY_OSKEXT);
if (kmem_result != KERN_SUCCESS) {
OSKextLog(/* kext */ NULL,
kOSKextLogErrorLevel |
/* Incidental error; we're going to (try to) allow the request
* to succeed. */
} else {
+ /* 11981737 - clear uninitialized data in last page */
+ bzero((void *)(buffer + logInfoLength),
+ (round_page(logInfoLength) - logInfoLength));
memcpy(buffer, logInfo, logInfoLength);
*logInfoOut = buffer;
*logInfoLengthOut = logInfoLength;
result = kOSReturnSuccess;
finish:
- OSSafeRelease(serializer);
+ OSSafeReleaseNULL(serializer);
return result;
}
(vm_address_t)thisKext->linkedExecutable->getBytesNoCopy();
vm_address_t kext_end = kext_start +
thisKext->linkedExecutable->getLength();
-
if ((kext_start <= address) && (address < kext_end)) {
foundKext = thisKext;
foundKext->retain();
return foundKext;
}
+OSData *
+OSKext::copyKextUUIDForAddress(OSNumber *address)
+{
+ OSData *uuid = NULL;
+
+ if (!address) {
+ return NULL;
+ }
+
+ uintptr_t addr = (uintptr_t)address->unsigned64BitValue() + vm_kernel_slide;
+
+#if CONFIG_MACF
+ /* Is the calling process allowed to query kext info? */
+ if (current_task() != kernel_task) {
+ int macCheckResult = 0;
+ kauth_cred_t cred = NULL;
+
+ cred = kauth_cred_get_with_ref();
+ macCheckResult = mac_kext_check_query(cred);
+ kauth_cred_unref(&cred);
+
+ if (macCheckResult != 0) {
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogErrorLevel | kOSKextLogLoadFlag,
+ "Failed to query kext UUID (MAC policy error 0x%x).",
+ macCheckResult);
+ return NULL;
+ }
+ }
+#endif
+
+ if (((vm_offset_t)addr >= vm_kernel_stext) && ((vm_offset_t)addr < vm_kernel_etext)) {
+ /* address in xnu proper */
+ unsigned long uuid_len = 0;
+ uuid = OSData::withBytes(getuuidfromheader(&_mh_execute_header, &uuid_len), uuid_len);
+ } else {
+ IOLockLock(sKextSummariesLock);
+ OSKextLoadedKextSummary *summary = OSKext::summaryForAddress(addr);
+ if (summary) {
+ uuid = OSData::withBytes(summary->uuid, sizeof(uuid_t));
+ }
+ IOLockUnlock(sKextSummariesLock);
+ }
+
+ return uuid;
+}
+
/*********************************************************************
*********************************************************************/
-/* static */
-bool OSKext::isKextWithIdentifierLoaded(const char * kextIdentifier)
+OSKext *
+OSKext::lookupKextWithUUID(uuid_t wanted)
{
- bool result = false;
OSKext * foundKext = NULL; // returned
+ uint32_t count, i;
IORecursiveLockLock(sKextLock);
- foundKext = OSDynamicCast(OSKext, sKextsByID->getObject(kextIdentifier));
- if (foundKext && foundKext->isLoaded()) {
- result = true;
+ count = sLoadedKexts->getCount();
+
+ for (i = 0; i < count; i++) {
+ OSKext * thisKext = NULL;
+
+ thisKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i));
+ if (!thisKext) {
+ continue;
+ }
+
+ OSData *uuid_data = thisKext->copyUUID();
+ if (!uuid_data) {
+ continue;
+ }
+
+ uuid_t uuid;
+ memcpy(&uuid, uuid_data->getBytesNoCopy(), sizeof(uuid));
+ uuid_data->release();
+
+ if (0 == uuid_compare(wanted, uuid)) {
+ foundKext = thisKext;
+ foundKext->retain();
+ goto finish;
+ }
+
}
+finish:
IORecursiveLockUnlock(sKextLock);
-
+
+ return foundKext;
+}
+
+
+
+
+/*********************************************************************
+*********************************************************************/
+/* static */
+bool OSKext::isKextWithIdentifierLoaded(const char * kextIdentifier)
+{
+ bool result = false;
+ OSKext * foundKext = NULL; // returned
+
+ IORecursiveLockLock(sKextLock);
+
+ foundKext = OSDynamicCast(OSKext, sKextsByID->getObject(kextIdentifier));
+ if (foundKext && foundKext->isLoaded()) {
+ result = true;
+ }
+
+ IORecursiveLockUnlock(sKextLock);
+
return result;
}
OSReturn
OSKext::removeKext(
OSKext * aKext,
+#if CONFIG_EMBEDDED
+ __unused
+#endif
bool terminateServicesAndRemovePersonalitiesFlag)
{
+#if CONFIG_EMBEDDED
+ OSKextLog(aKext,
+ kOSKextLogErrorLevel |
+ kOSKextLogKextBookkeepingFlag,
+ "removeKext() called for %s, not supported on embedded",
+ aKext->getIdentifier() ? aKext->getIdentifierCString() : "unknown kext");
+
+ return kOSReturnSuccess;
+#else /* CONFIG_EMBEDDED */
+
OSReturn result = kOSKextReturnInUse;
OSKext * checkKext = NULL; // do not release
+#if CONFIG_MACF
+ int macCheckResult = 0;
+ kauth_cred_t cred = NULL;
+#endif
IORecursiveLockLock(sKextLock);
}
if (aKext->isLoaded()) {
+#if CONFIG_MACF
+ if (current_task() != kernel_task) {
+ cred = kauth_cred_get_with_ref();
+ macCheckResult = mac_kext_check_unload(cred, aKext->getIdentifierCString());
+ kauth_cred_unref(&cred);
+ }
+
+ if (macCheckResult != 0) {
+ result = kOSReturnError;
+ OSKextLog(aKext,
+ kOSKextLogErrorLevel |
+ kOSKextLogKextBookkeepingFlag,
+ "Failed to remove kext %s (MAC policy error 0x%x).",
+ aKext->getIdentifierCString(), macCheckResult);
+ goto finish;
+ }
+#endif
+
+ /* make sure there are no resource requests in flight - 17187548 */
+ if (aKext->countRequestCallbacks()) {
+ goto finish;
+ }
+
/* If we are terminating, send the request to the IOCatalogue
* (which will actually call us right back but that's ok we have
* a recursive lock don't you know) but do not ask the IOCatalogue
aKext->getIdentifierCString(), /* unload */ false);
if (result != kOSReturnSuccess) {
OSKextLog(aKext,
- kOSKextLogProgressLevel |
+ kOSKextLogErrorLevel |
kOSKextLogKextBookkeepingFlag,
"Can't remove kext %s; services failed to terminate - 0x%x.",
aKext->getIdentifierCString(), result);
}
OSKextLog(aKext,
- kOSKextLogProgressLevel |
- kOSKextLogKextBookkeepingFlag,
- "Removing kext %s.",
- aKext->getIdentifierCString());
-
+ kOSKextLogProgressLevel |
+ kOSKextLogKextBookkeepingFlag,
+ "Removing kext %s.",
+ aKext->getIdentifierCString());
+
sKextsByID->removeObject(aKext->getIdentifier());
result = kOSReturnSuccess;
finish:
IORecursiveLockUnlock(sKextLock);
return result;
+#endif /* CONFIG_EMBEDDED */
}
/*********************************************************************
return result;
}
-
+
+/*********************************************************************
+ *********************************************************************/
+#define BOOTER_KEXT_PREFIX "Driver-"
+
+typedef struct _DeviceTreeBuffer {
+ uint32_t paddr;
+ uint32_t length;
+} _DeviceTreeBuffer;
+
+/*********************************************************************
+ * Create a dictionary of excluded kexts from the given booter data.
+ *********************************************************************/
+/* static */
+void
+OSKext::createExcludeListFromBooterData(
+ OSDictionary * theDictionary,
+ OSCollectionIterator * theIterator )
+{
+ OSString * deviceTreeName = NULL; // do not release
+ const _DeviceTreeBuffer * deviceTreeBuffer = NULL; // do not release
+ char * booterDataPtr = NULL; // do not release
+ _BooterKextFileInfo * kextFileInfo = NULL; // do not release
+ char * infoDictAddr = NULL; // do not release
+ OSObject * parsedXML = NULL; // must release
+ OSDictionary * theInfoDict = NULL; // do not release
+
+ theIterator->reset();
+
+ /* look for AppleKextExcludeList.kext */
+ while ( (deviceTreeName =
+ OSDynamicCast(OSString, theIterator->getNextObject())) ) {
+
+ const char * devTreeNameCString;
+ OSData * deviceTreeEntry;
+ OSString * myBundleID; // do not release
+
+ OSSafeReleaseNULL(parsedXML);
+
+ deviceTreeEntry =
+ OSDynamicCast(OSData, theDictionary->getObject(deviceTreeName));
+ if (!deviceTreeEntry) {
+ continue;
+ }
+
+ /* Make sure it is a kext */
+ devTreeNameCString = deviceTreeName->getCStringNoCopy();
+ if (strncmp(devTreeNameCString, BOOTER_KEXT_PREFIX,
+ (sizeof(BOOTER_KEXT_PREFIX) - 1)) != 0) {
+ OSKextLog(NULL,
+ kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
+ "\"%s\" not a kext",
+ devTreeNameCString);
+ continue;
+ }
+
+ deviceTreeBuffer = (const _DeviceTreeBuffer *)
+ deviceTreeEntry->getBytesNoCopy(0, sizeof(deviceTreeBuffer));
+ if (!deviceTreeBuffer) {
+ continue;
+ }
+
+ booterDataPtr = (char *)ml_static_ptovirt(deviceTreeBuffer->paddr);
+ if (!booterDataPtr) {
+ continue;
+ }
+
+ kextFileInfo = (_BooterKextFileInfo *) booterDataPtr;
+ if (!kextFileInfo->infoDictPhysAddr ||
+ !kextFileInfo->infoDictLength) {
+ continue;
+ }
+
+ infoDictAddr = (char *)
+ ml_static_ptovirt(kextFileInfo->infoDictPhysAddr);
+ if (!infoDictAddr) {
+ continue;
+ }
+
+ parsedXML = OSUnserializeXML(infoDictAddr);
+ if (!parsedXML) {
+ continue;
+ }
+
+ theInfoDict = OSDynamicCast(OSDictionary, parsedXML);
+ if (!theInfoDict) {
+ continue;
+ }
+
+ myBundleID =
+ OSDynamicCast(OSString,
+ theInfoDict->getObject(kCFBundleIdentifierKey));
+ if ( myBundleID &&
+ strcmp( myBundleID->getCStringNoCopy(), "com.apple.driver.KextExcludeList" ) == 0 ) {
+
+ /* get copy of exclusion list dictionary */
+ OSDictionary * myTempDict; // do not free
+
+ myTempDict = OSDynamicCast(
+ OSDictionary,
+ theInfoDict->getObject("OSKextExcludeList"));
+ if ( NULL == myTempDict ) {
+ /* 25322874 */
+ panic("Missing OSKextExcludeList dictionary\n");
+ }
+
+ IORecursiveLockLock(sKextLock);
+
+ /* get rid of old exclusion list */
+ if (sExcludeListByID) {
+ OSSafeReleaseNULL(sExcludeListByID);
+ }
+ sExcludeListByID = OSDictionary::withDictionary(myTempDict, 0);
+ IORecursiveLockUnlock(sKextLock);
+
+ break;
+ }
+
+ } // while ( (deviceTreeName = ...) )
+
+ OSSafeReleaseNULL(parsedXML);
+ return;
+}
+
+/*********************************************************************
+ * Create a dictionary of excluded kexts from the given prelink
+ * info (kernelcache).
+ *********************************************************************/
+/* static */
+void
+OSKext::createExcludeListFromPrelinkInfo( OSArray * theInfoArray )
+{
+ OSDictionary * myInfoDict = NULL; // do not release
+ OSString * myBundleID; // do not release
+ u_int i;
+
+ /* Find com.apple.driver.KextExcludeList. */
+ for (i = 0; i < theInfoArray->getCount(); i++) {
+ myInfoDict = OSDynamicCast(OSDictionary, theInfoArray->getObject(i));
+ if (!myInfoDict) {
+ continue;
+ }
+ myBundleID =
+ OSDynamicCast(OSString,
+ myInfoDict->getObject(kCFBundleIdentifierKey));
+ if ( myBundleID &&
+ strcmp( myBundleID->getCStringNoCopy(), "com.apple.driver.KextExcludeList" ) == 0 ) {
+ // get copy of exclude list dictionary
+ OSDictionary * myTempDict; // do not free
+ myTempDict = OSDynamicCast(OSDictionary,
+ myInfoDict->getObject("OSKextExcludeList"));
+ if ( NULL == myTempDict ) {
+ /* 25322874 */
+ panic("Missing OSKextExcludeList dictionary\n");
+ }
+
+ IORecursiveLockLock(sKextLock);
+ // get rid of old exclude list
+ if (sExcludeListByID) {
+ OSSafeReleaseNULL(sExcludeListByID);
+ }
+
+ sExcludeListByID = OSDictionary::withDictionary(myTempDict, 0);
+ IORecursiveLockUnlock(sKextLock);
+ break;
+ }
+ } // for (i = 0; i < theInfoArray->getCount()...
+
+ return;
+}
+
#if PRAGMA_MARK
#pragma mark Accessors
#endif
return compatibleVersion;
}
+/*********************************************************************
+*********************************************************************/
+bool
+OSKext::isLibrary(void)
+{
+ return (getCompatibleVersion() > 0);
+}
+
/*********************************************************************
*********************************************************************/
bool
bool
OSKext::declaresExecutable(void)
{
- if (getPropertyForHostArch(kCFBundleExecutableKey)) {
- return true;
- }
- return false;
+ return (getPropertyForHostArch(kCFBundleExecutableKey) != NULL);
}
/*********************************************************************
extractedExecutable = extractMkext2FileData(
MKEXT2_GET_ENTRY_DATA(fileinfo), "executable",
compressedSize, fullSize);
- } else if (mkextVersion == MKEXT_VERS_1) {
- extractedExecutable = extractMkext1Entry(
- mkextEntryRef->mkext, mkextEntryRef->fileinfo);
} else {
OSKextLog(this, kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
finish:
- OSSafeRelease(extractedExecutable);
+ OSSafeReleaseNULL(extractedExecutable);
return result;
}
return flags.interface;
}
+/*********************************************************************
+*********************************************************************/
+bool
+OSKext::isKernel(void)
+{
+ return (this == sKernelKext);
+}
+
/*********************************************************************
*********************************************************************/
bool
return flags.kernelComponent ? true : false;
}
+/*********************************************************************
+*********************************************************************/
+bool
+OSKext::isExecutable(void)
+{
+ return (!isKernel() && !isInterface() && declaresExecutable());
+}
+
/*********************************************************************
* We might want to check this recursively for all dependencies,
* since a subtree of dependencies could get loaded before we hit
bool result = false;
OSString * required = NULL; // do not release
+ if (isKernel()) {
+ result = true;
+ goto finish;
+ }
required = OSDynamicCast(OSString,
getPropertyForHostArch(kOSBundleRequiredKey));
return loadTag;
}
+/*********************************************************************
+ *********************************************************************/
+void OSKext::getSizeInfo(uint32_t *loadSize, uint32_t *wiredSize)
+{
+ if (linkedExecutable) {
+ *loadSize = linkedExecutable->getLength();
+
+ /* If we have a kmod_info struct, calculated the wired size
+ * from that. Otherwise it's the full load size.
+ */
+ if (kmod_info) {
+ *wiredSize = *loadSize - kmod_info->hdr_size;
+ } else {
+ *wiredSize = *loadSize;
+ }
+ }
+ else {
+ *wiredSize = 0;
+ *loadSize = 0;
+ }
+}
+
/*********************************************************************
*********************************************************************/
OSData *
header = (const kernel_mach_header_t *)theExecutable->getBytesNoCopy();
load_cmd = (const struct load_command *)&header[1];
-
+
+ if (header->magic != MH_MAGIC_KERNEL) {
+ OSKextLog(NULL,
+ kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
+ "%s: bad header %p",
+ __func__,
+ header);
+ goto finish;
+ }
+
for (i = 0; i < header->ncmds; i++) {
if (load_cmd->cmd == LC_UUID) {
uuid_cmd = (struct uuid_command *)load_cmd;
/*********************************************************************
*********************************************************************/
-#if defined (__ppc__)
-#define ARCHNAME "ppc"
-#elif defined (__i386__)
-#define ARCHNAME "i386"
-#elif defined (__x86_64__)
+#if defined (__arm__)
+#include <arm/arch.h>
+#endif
+
+#if defined (__x86_64__)
#define ARCHNAME "x86_64"
+#elif defined (__arm64__)
+#define ARCHNAME "arm64"
+#elif defined (__arm__)
+
+#if defined (__ARM_ARCH_7S__)
+#define ARCHNAME "armv7s"
+#elif defined (__ARM_ARCH_7F__)
+#define ARCHNAME "armv7f"
+#elif defined (__ARM_ARCH_7K__)
+#define ARCHNAME "armv7k"
+#elif defined (_ARM_ARCH_7) /* umbrella for all remaining */
+#define ARCHNAME "armv7"
+#elif defined (_ARM_ARCH_6) /* umbrella for all armv6 */
+#define ARCHNAME "armv6"
+#endif
+
+#elif defined (__arm64__)
+#define ARCHNAME "arm64"
#else
#error architecture not supported
#endif
/* Add 1 for the ARCH_SEPARATOR_CHAR, and 1 for the '\0'.
*/
keySize = 1 + 1 + strlen(key) + strlen(ARCHNAME);
- result = (char *)kalloc(keySize);
+ result = (char *)kalloc_tag(keySize, VM_KERN_MEMORY_OSKEXT);
if (!result) {
goto finish;
}
#if PRAGMA_MARK
#pragma mark Load/Start/Stop/Unload
#endif
+
+#define isWhiteSpace(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == ',' || (c) == '\n')
+
+/*********************************************************************
+ * sExcludeListByID is a dictionary with keys / values of:
+ * key = bundleID string of kext we will not allow to load
+ * value = version string(s) of the kext that is to be denied loading.
+ * The version strings can be comma delimited. For example if kext
+ * com.foocompany.fookext has two versions that we want to deny
+ * loading then the version strings might look like:
+ * 1.0.0, 1.0.1
+ * If the current fookext has a version of 1.0.0 OR 1.0.1 we will
+ * not load the kext.
+ *
+ * Value may also be in the form of "LE 2.0.0" (version numbers
+ * less than or equal to 2.0.0 will not load) or "LT 2.0.0" (version
+ * number less than 2.0.0 will not load)
+ *
+ * NOTE - we cannot use the characters "<=" or "<" because we have code
+ * that serializes plists and treats '<' as a special character.
+ *********************************************************************/
+bool
+OSKext::isInExcludeList(void)
+{
+ OSString * versionString = NULL; // do not release
+ char * versionCString = NULL; // do not free
+ size_t i;
+ boolean_t wantLessThan = false;
+ boolean_t wantLessThanEqualTo = false;
+ char myBuffer[32];
+
+ if (!sExcludeListByID) {
+ return(false);
+ }
+ /* look up by bundleID in our exclude list and if found get version
+ * string (or strings) that we will not allow to load
+ */
+ versionString = OSDynamicCast(OSString, sExcludeListByID->getObject(bundleID));
+ if (versionString == NULL || versionString->getLength() > (sizeof(myBuffer) - 1)) {
+ return(false);
+ }
+
+ /* parse version strings */
+ versionCString = (char *) versionString->getCStringNoCopy();
+
+ /* look for "LT" or "LE" form of version string, must be in first two
+ * positions.
+ */
+ if (*versionCString == 'L' && *(versionCString + 1) == 'T') {
+ wantLessThan = true;
+ versionCString +=2;
+ }
+ else if (*versionCString == 'L' && *(versionCString + 1) == 'E') {
+ wantLessThanEqualTo = true;
+ versionCString +=2;
+ }
+
+ for (i = 0; *versionCString != 0x00; versionCString++) {
+ /* skip whitespace */
+ if (isWhiteSpace(*versionCString)) {
+ continue;
+ }
+
+ /* peek ahead for version string separator or null terminator */
+ if (*(versionCString + 1) == ',' || *(versionCString + 1) == 0x00) {
+
+ /* OK, we have a version string */
+ myBuffer[i++] = *versionCString;
+ myBuffer[i] = 0x00;
+
+ OSKextVersion excludeVers;
+ excludeVers = OSKextParseVersionString(myBuffer);
+
+ if (wantLessThanEqualTo) {
+ if (version <= excludeVers) {
+ return(true);
+ }
+ }
+ else if (wantLessThan) {
+ if (version < excludeVers) {
+ return(true);
+ }
+ }
+ else if ( version == excludeVers ) {
+ return(true);
+ }
+
+ /* reset for the next (if any) version string */
+ i = 0;
+ wantLessThan = false;
+ wantLessThanEqualTo = false;
+ }
+ else {
+ /* save valid version character */
+ myBuffer[i++] = *versionCString;
+
+ /* make sure bogus version string doesn't overrun local buffer */
+ if ( i >= sizeof(myBuffer) ) {
+ break;
+ }
+ }
+ }
+
+ return(false);
+}
+
/*********************************************************************
*********************************************************************/
+/* static */
OSReturn
OSKext::loadKextWithIdentifier(
const char * kextIdentifierCString,
startOpt, startMatchingOpt, personalityNames);
finish:
- OSSafeRelease(kextIdentifier);
+ OSSafeReleaseNULL(kextIdentifier);
return result;
}
-
/*********************************************************************
*********************************************************************/
OSReturn
OSArray * personalityNames)
{
OSReturn result = kOSReturnError;
+ OSReturn pingResult = kOSReturnError;
OSKext * theKext = NULL; // do not release
OSDictionary * loadRequest = NULL; // must release
const OSSymbol * kextIdentifierSymbol = NULL; // must release
kextIdentifier->getCStringNoCopy());
}
- if (sKextdActive) {
- OSKextPingKextd();
- } else {
+ pingResult = OSKext::pingKextd();
+ if (pingResult == kOSKextReturnDisabled) {
OSKextLog(/* kext */ NULL,
((sPrelinkBoot) ? kOSKextLogDebugLevel : kOSKextLogErrorLevel) |
kOSKextLogLoadFlag,
- "Not loading kext %s - not found and kextd not available in early boot.",
+ "Kext %s might not load - kextd is currently unavailable.",
kextIdentifier->getCStringNoCopy());
}
}
finish:
- OSSafeRelease(loadRequest);
- OSSafeRelease(kextIdentifierSymbol);
+ OSSafeReleaseNULL(loadRequest);
+ OSSafeReleaseNULL(kextIdentifierSymbol);
IORecursiveLockUnlock(sKextLock);
goto finish;
}
+ IORecursiveLockLock(sKextLock);
if (!sAllKextLoadIdentifiers->containsObject(kextIdentifierSymbol)) {
if (!sAllKextLoadIdentifiers->setObject(kextIdentifierSymbol)) {
fail = true;
kextIdentifier->getCStringNoCopy());
}
}
+ IORecursiveLockUnlock(sKextLock);
+
finish:
if (fail) {
"Failed to record kext %s as a candidate for inclusion in prelinked kernel.",
kextIdentifier->getCStringNoCopy());
}
- OSSafeRelease(kextIdentifierSymbol);
+ OSSafeReleaseNULL(kextIdentifierSymbol);
return;
}
Boolean alreadyLoaded = false;
OSKext * lastLoadedKext = NULL;
- if (!sLoadEnabled) {
- if (!isLoaded() || (!isStarted() && startOpt != kOSKextExcludeNone) ||
- (startMatchingOpt != kOSKextExcludeNone)) {
-
- OSKextLog(this,
- kOSKextLogErrorLevel |
- kOSKextLogLoadFlag,
- "Kext loading is disabled "
- "(attempt to load/start/start matching for kext %s).",
- getIdentifierCString());
- }
- result = kOSKextReturnDisabled;
+ if (isInExcludeList()) {
+ OSKextLog(this,
+ kOSKextLogErrorLevel | kOSKextLogGeneralFlag |
+ kOSKextLogLoadFlag,
+ "Kext %s is in exclude list, not loadable",
+ getIdentifierCString());
+
+ result = kOSKextReturnNotLoadable;
goto finish;
}
getIdentifierCString());
goto loaded;
}
+
+#if CONFIG_MACF
+ if (current_task() != kernel_task) {
+ int macCheckResult = 0;
+ kauth_cred_t cred = NULL;
+
+ cred = kauth_cred_get_with_ref();
+ macCheckResult = mac_kext_check_load(cred, getIdentifierCString());
+ kauth_cred_unref(&cred);
+
+ if (macCheckResult != 0) {
+ result = kOSReturnError;
+ OSKextLog(this,
+ kOSKextLogErrorLevel | kOSKextLogLoadFlag,
+ "Failed to load kext %s (MAC policy error 0x%x).",
+ getIdentifierCString(), macCheckResult);
+ goto finish;
+ }
+ }
+#endif
+
+ if (!sLoadEnabled) {
+ OSKextLog(this,
+ kOSKextLogErrorLevel |
+ kOSKextLogLoadFlag,
+ "Kext loading is disabled (attempt to load kext %s).",
+ getIdentifierCString());
+ result = kOSKextReturnDisabled;
+ goto finish;
+ }
/* If we've pushed the next available load tag to the invalid value,
* we can't load any more kexts.
kOSKextLogProgressLevel | kOSKextLogLoadFlag,
"Loading kext %s.",
getIdentifierCString());
-
-
+
if (!sKxldContext) {
kxldResult = kxld_create_context(&sKxldContext, &kern_allocate,
&kxld_log_callback, /* Flags */ (KXLDFlags) 0,
- /* cputype */ 0, /* cpusubtype */ 0);
+ /* cputype */ 0, /* cpusubtype */ 0, /* page size */ 0);
if (kxldResult) {
OSKextLog(this,
kOSKextLogErrorLevel |
goto finish;
}
+ pendingPgoHead.next = &pendingPgoHead;
+ pendingPgoHead.prev = &pendingPgoHead;
+
+ uuid_generate(instance_uuid);
+ account = IONew(OSKextAccount, 1);
+ if (!account) {
+ result = KERN_MEMORY_ERROR;
+ goto finish;
+ }
+ bzero(account, sizeof(*account));
+ account->loadTag = kmod_info->id;
+ account->site.refcount = 0;
+ account->site.flags = VM_TAG_KMOD;
+ account->kext = this;
+
flags.loaded = true;
/* Add the kext to the list of loaded kexts and update the kmod_info
/* Keep the kernel itself out of the kmod list.
*/
- if (lastLoadedKext == sKernelKext) {
+ if (lastLoadedKext->isKernel()) {
lastLoadedKext = NULL;
}
kmod_info->next = lastLoadedKext->kmod_info;
}
+ notifyKextLoadObservers(this, kmod_info);
+
/* Make the global kmod list point at the just-loaded kext. Note that the
* __kernel__ kext isn't in this list, as it wasn't before SnowLeopard,
* although we do report it in kextstat these days by using the newer
/* Save the list of loaded kexts in case we panic.
*/
- clock_get_uptime(&last_loaded_timestamp);
OSKext::saveLoadedKextPanicList();
+ if (isExecutable()) {
+ OSKext::updateLoadedKextSummaries();
+ savePanicString(/* isLoading */ true);
+
+#if CONFIG_DTRACE
+ registerWithDTrace();
+#else
+ jettisonLinkeditSegment();
+#endif /* CONFIG_DTRACE */
+
+#if !VM_MAPPED_KEXTS
+ /* If there is a page (or more) worth of padding after the end
+ * of the last data section but before the end of the data segment
+ * then free it in the same manner the LinkeditSegment is freed
+ */
+ jettisonDATASegmentPadding();
+#endif
+ }
+
loaded:
- /* This is a bit of a hack, because we shouldn't be handling
- * personalities within the load function.
- */
- if (declaresExecutable() && (startOpt == kOSKextExcludeNone)) {
- result = start();
- if (result != kOSReturnSuccess) {
- OSKextLog(this,
- kOSKextLogErrorLevel | kOSKextLogLoadFlag,
- "Kext %s start failed (result 0x%x).",
- getIdentifierCString(), result);
- result = kOSKextReturnStartStopError;
+ if (isExecutable() && !flags.started) {
+ if (startOpt == kOSKextExcludeNone) {
+ result = start();
+ if (result != kOSReturnSuccess) {
+ OSKextLog(this,
+ kOSKextLogErrorLevel | kOSKextLogLoadFlag,
+ "Kext %s start failed (result 0x%x).",
+ getIdentifierCString(), result);
+ result = kOSKextReturnStartStopError;
+ }
}
}
/* If not excluding matching, send the personalities to the kernel.
* This never affects the result of the load operation.
+ * This is a bit of a hack, because we shouldn't be handling
+ * personalities within the load function.
*/
if (result == kOSReturnSuccess && startMatchingOpt == kOSKextExcludeNone) {
- sendPersonalitiesToCatalog(true, personalityNames);
+ result = sendPersonalitiesToCatalog(true, personalityNames);
}
-
+
finish:
+
+ /* More hack! If the kext doesn't declare an executable, even if we
+ * "loaded" it, we have to remove any personalities naming it, or we'll
+ * never see the registry go quiet. Errors here do not count for the
+ * load operation itself.
+ *
+ * Note that in every other regard it's perfectly ok for a kext to
+ * not declare an executable and serve only as a package for personalities
+ * naming another kext, so we do have to allow such kexts to be "loaded"
+ * so that those other personalities get added & matched.
+ */
+ if (!declaresExecutable()) {
+ OSKextLog(this,
+ kOSKextLogStepLevel | kOSKextLogLoadFlag,
+ "Kext %s has no executable; removing any personalities naming it.",
+ getIdentifierCString());
+ removePersonalitiesFromCatalog();
+ }
+
if (result != kOSReturnSuccess) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Kext %s loaded.",
getIdentifierCString());
+
+ queueKextNotification(kKextRequestPredicateLoadNotification,
+ OSDynamicCast(OSString, bundleID));
}
return result;
}
+/*********************************************************************
+*
+*********************************************************************/
+static char * strdup(const char * string)
+{
+ char * result = NULL;
+ size_t size;
+
+ if (!string) {
+ goto finish;
+ }
+
+ size = 1 + strlen(string);
+ result = (char *)kalloc_tag(size, VM_KERN_MEMORY_OSKEXT);
+ if (!result) {
+ goto finish;
+ }
+
+ memcpy(result, string, size);
+
+finish:
+ return result;
+}
+
+/*********************************************************************
+*
+*********************************************************************/
+
+kernel_section_t *
+OSKext::lookupSection(const char *segname, const char *secname)
+{
+ kernel_section_t * found_section = NULL;
+ kernel_mach_header_t * mh = NULL;
+ kernel_segment_command_t * seg = NULL;
+ kernel_section_t * sec = NULL;
+
+ mh = (kernel_mach_header_t *)linkedExecutable->getBytesNoCopy();
+
+ for (seg = firstsegfromheader(mh); seg != NULL; seg = nextsegfromheader(mh, seg)) {
+
+ if (0 != strcmp(seg->segname, segname)) {
+ continue;
+ }
+
+ for (sec = firstsect(seg); sec != NULL; sec = nextsect(seg, sec)) {
+
+ if (0 == strcmp(sec->sectname, secname)) {
+ found_section = sec;
+ goto out;
+ }
+ }
+ }
+
+ out:
+ return found_section;
+}
+
+/*********************************************************************
+*
+*********************************************************************/
+
+OSReturn
+OSKext::slidePrelinkedExecutable(bool doCoalesedSlides)
+{
+ OSReturn result = kOSKextReturnBadData;
+ kernel_mach_header_t * mh = NULL;
+ kernel_segment_command_t * seg = NULL;
+ kernel_segment_command_t * linkeditSeg = NULL;
+ kernel_section_t * sec = NULL;
+ char * linkeditBase = NULL;
+ bool haveLinkeditBase = false;
+ char * relocBase = NULL;
+ bool haveRelocBase = false;
+ struct dysymtab_command * dysymtab = NULL;
+ struct linkedit_data_command * segmentSplitInfo = NULL;
+ struct symtab_command * symtab = NULL;
+ kernel_nlist_t * sym = NULL;
+ struct relocation_info * reloc = NULL;
+ uint32_t i = 0;
+ int reloc_size;
+ vm_offset_t new_kextsize;
+
+ if (linkedExecutable == NULL || vm_kernel_slide == 0) {
+ result = kOSReturnSuccess;
+ goto finish;
+ }
+
+ mh = (kernel_mach_header_t *)linkedExecutable->getBytesNoCopy();
+ segmentSplitInfo = (struct linkedit_data_command *) getcommandfromheader(mh, LC_SEGMENT_SPLIT_INFO);
+
+ for (seg = firstsegfromheader(mh); seg != NULL; seg = nextsegfromheader(mh, seg)) {
+ if (!seg->vmaddr) {
+ continue;
+ }
+ seg->vmaddr += vm_kernel_slide;
+
+#if KASLR_KEXT_DEBUG
+ IOLog("kaslr: segname %s unslid 0x%lx slid 0x%lx \n",
+ seg->segname,
+ (unsigned long)VM_KERNEL_UNSLIDE(seg->vmaddr),
+ (unsigned long)seg->vmaddr);
+#endif
+
+ if (!haveRelocBase) {
+ relocBase = (char *) seg->vmaddr;
+ haveRelocBase = true;
+ }
+ if (!strcmp(seg->segname, "__LINKEDIT")) {
+ linkeditBase = (char *) seg->vmaddr - seg->fileoff;
+ haveLinkeditBase = true;
+ linkeditSeg = seg;
+ }
+ for (sec = firstsect(seg); sec != NULL; sec = nextsect(seg, sec)) {
+ sec->addr += vm_kernel_slide;
+
+#if KASLR_KEXT_DEBUG
+ IOLog("kaslr: sectname %s unslid 0x%lx slid 0x%lx \n",
+ sec->sectname,
+ (unsigned long)VM_KERNEL_UNSLIDE(sec->addr),
+ (unsigned long)sec->addr);
+#endif
+ }
+ }
+
+ dysymtab = (struct dysymtab_command *) getcommandfromheader(mh, LC_DYSYMTAB);
+
+ symtab = (struct symtab_command *) getcommandfromheader(mh, LC_SYMTAB);
+
+ if (symtab != NULL && doCoalesedSlides == false) {
+ /* Some pseudo-kexts have symbol tables without segments.
+ * Ignore them. */
+ if (symtab->nsyms > 0 && haveLinkeditBase) {
+ sym = (kernel_nlist_t *) (linkeditBase + symtab->symoff);
+ for (i = 0; i < symtab->nsyms; i++) {
+ if (sym[i].n_type & N_STAB) {
+ continue;
+ }
+ sym[i].n_value += vm_kernel_slide;
+
+#if KASLR_KEXT_DEBUG
+#define MAX_SYMS_TO_LOG 5
+ if ( i < MAX_SYMS_TO_LOG ) {
+ IOLog("kaslr: LC_SYMTAB unslid 0x%lx slid 0x%lx \n",
+ (unsigned long)VM_KERNEL_UNSLIDE(sym[i].n_value),
+ (unsigned long)sym[i].n_value);
+ }
+#endif
+ }
+ }
+ }
+
+ if (dysymtab != NULL && doCoalesedSlides == false) {
+ if (dysymtab->nextrel > 0) {
+ OSKextLog(this,
+ kOSKextLogErrorLevel | kOSKextLogLoadFlag |
+ kOSKextLogLinkFlag,
+ "Sliding kext %s: External relocations found.",
+ getIdentifierCString());
+ goto finish;
+ }
+
+ if (dysymtab->nlocrel > 0) {
+ if (!haveLinkeditBase) {
+ OSKextLog(this,
+ kOSKextLogErrorLevel | kOSKextLogLoadFlag |
+ kOSKextLogLinkFlag,
+ "Sliding kext %s: No linkedit segment.",
+ getIdentifierCString());
+ goto finish;
+ }
+
+ if (!haveRelocBase) {
+ OSKextLog(this,
+ kOSKextLogErrorLevel | kOSKextLogLoadFlag |
+ kOSKextLogLinkFlag,
+#if __x86_64__
+ "Sliding kext %s: No writable segments.",
+#else
+ "Sliding kext %s: No segments.",
+#endif
+ getIdentifierCString());
+ goto finish;
+ }
+
+ reloc = (struct relocation_info *) (linkeditBase + dysymtab->locreloff);
+ reloc_size = dysymtab->nlocrel * sizeof(struct relocation_info);
+
+ for (i = 0; i < dysymtab->nlocrel; i++) {
+ if ( reloc[i].r_extern != 0
+ || reloc[i].r_type != 0
+ || reloc[i].r_length != (sizeof(void *) == 8 ? 3 : 2)
+ ) {
+ OSKextLog(this,
+ kOSKextLogErrorLevel | kOSKextLogLoadFlag |
+ kOSKextLogLinkFlag,
+ "Sliding kext %s: Unexpected relocation found.",
+ getIdentifierCString());
+ goto finish;
+ }
+ if (reloc[i].r_pcrel != 0) {
+ continue;
+ }
+ *((uintptr_t *)(relocBase + reloc[i].r_address)) += vm_kernel_slide;
+
+#if KASLR_KEXT_DEBUG
+#define MAX_DYSYMS_TO_LOG 5
+ if ( i < MAX_DYSYMS_TO_LOG ) {
+ IOLog("kaslr: LC_DYSYMTAB unslid 0x%lx slid 0x%lx \n",
+ (unsigned long)VM_KERNEL_UNSLIDE(*((uintptr_t *)(relocBase + reloc[i].r_address))),
+ (unsigned long)*((uintptr_t *)(relocBase + reloc[i].r_address)));
+ }
+#endif
+ }
+
+ /* We should free these relocations, not just delete the reference to them.
+ * <rdar://problem/10535549> Free relocations from PIE kexts.
+ *
+ * For now, we do not free LINKEDIT for kexts with split segments.
+ */
+ new_kextsize = round_page(kmod_info->size - reloc_size);
+ if (((kmod_info->size - new_kextsize) > PAGE_SIZE) && (!segmentSplitInfo)) {
+ vm_offset_t endofkext = kmod_info->address + kmod_info->size;
+ vm_offset_t new_endofkext = kmod_info->address + new_kextsize;
+ vm_offset_t endofrelocInfo = (vm_offset_t) (((uint8_t *)reloc) + reloc_size);
+ int bytes_remaining = endofkext - endofrelocInfo;
+ OSData * new_osdata = NULL;
+
+ /* fix up symbol offsets if they are after the dsymtab local relocs */
+ if (symtab) {
+ if (dysymtab->locreloff < symtab->symoff){
+ symtab->symoff -= reloc_size;
+ }
+ if (dysymtab->locreloff < symtab->stroff) {
+ symtab->stroff -= reloc_size;
+ }
+ }
+ if (dysymtab->locreloff < dysymtab->extreloff) {
+ dysymtab->extreloff -= reloc_size;
+ }
+
+ /* move data behind reloc info down to new offset */
+ if (endofrelocInfo < endofkext) {
+ memcpy(reloc, (void *)endofrelocInfo, bytes_remaining);
+ }
+
+ /* Create a new OSData for the smaller kext object and reflect
+ * new linkedit segment size.
+ */
+ linkeditSeg->vmsize = round_page(linkeditSeg->vmsize - reloc_size);
+ linkeditSeg->filesize = linkeditSeg->vmsize;
+
+ new_osdata = OSData::withBytesNoCopy((void *)kmod_info->address, new_kextsize);
+ if (new_osdata) {
+ /* Fix up kmod info and linkedExecutable.
+ */
+ kmod_info->size = new_kextsize;
+#if VM_MAPPED_KEXTS
+ new_osdata->setDeallocFunction(osdata_kext_free);
+#else
+ new_osdata->setDeallocFunction(osdata_phys_free);
+#endif
+ linkedExecutable->setDeallocFunction(NULL);
+ linkedExecutable->release();
+ linkedExecutable = new_osdata;
+
+#if VM_MAPPED_KEXTS
+ kext_free(new_endofkext, (endofkext - new_endofkext));
+#else
+ ml_static_mfree(new_endofkext, (endofkext - new_endofkext));
+#endif
+ }
+ }
+ dysymtab->nlocrel = 0;
+ dysymtab->locreloff = 0;
+ }
+ }
+
+ result = kOSReturnSuccess;
+finish:
+ return result;
+}
+
/*********************************************************************
* called only by load()
*********************************************************************/
{
OSReturn result = kOSReturnError;
kern_return_t kxldResult;
- u_char ** kxlddeps = NULL; // must kfree
+ KXLDDependency * kxlddeps = NULL; // must kfree
uint32_t num_kxlddeps = 0;
+ OSArray * linkDependencies = NULL; // must release
+ uint32_t numDirectDependencies = 0;
uint32_t num_kmod_refs = 0;
- u_char * linkStateBytes = NULL; // do not free
- u_long linkStateLength = 0;
- u_char ** linkStateBytesPtr = NULL; // do not free
- u_long * linkStateLengthPtr = NULL; // do not free
struct mach_header ** kxldHeaderPtr = NULL; // do not free
struct mach_header * kxld_header = NULL; // xxx - need to free here?
OSData * theExecutable = NULL; // do not release
if (isKernelComponent()) {
if (STRING_HAS_PREFIX(versCString, KERNEL_LIB_PREFIX)) {
+
if (strncmp(versCString, KERNEL6_VERSION, strlen(KERNEL6_VERSION))) {
OSKextLog(this,
kOSKextLogErrorLevel |
goto register_kmod;
}
+ /* <rdar://problem/21444003> all callers must be entitled */
+ if (FALSE == IOTaskHasEntitlement(current_task(), "com.apple.rootless.kext-management")) {
+ OSKextLog(this,
+ kOSKextLogErrorLevel | kOSKextLogLoadFlag,
+ "Not entitled to link kext '%s'",
+ getIdentifierCString());
+ result = kOSKextReturnNotPrivileged;
+ goto finish;
+ }
+
theExecutable = getExecutable();
if (!theExecutable) {
if (declaresExecutable()) {
goto register_kmod;
}
- if (isKernelComponent()) {
- num_kxlddeps = 1; // the kernel itself
- } else {
- num_kxlddeps = getNumDependencies();
+ if (isInterface()) {
+ OSData *executableCopy = OSData::withData(theExecutable);
+ setLinkedExecutable(executableCopy);
+ executableCopy->release();
+ goto register_kmod;
}
+
+ numDirectDependencies = getNumDependencies();
+
+ if (flags.hasBleedthrough) {
+ linkDependencies = dependencies;
+ linkDependencies->retain();
+ } else {
+ linkDependencies = OSArray::withArray(dependencies);
+ if (!linkDependencies) {
+ OSKextLog(this,
+ kOSKextLogErrorLevel |
+ kOSKextLogLoadFlag | kOSKextLogLinkFlag,
+ "Can't allocate link dependencies to load kext %s.",
+ getIdentifierCString());
+ goto finish;
+ }
+
+ for (i = 0; i < numDirectDependencies; ++i) {
+ OSKext * dependencyKext = OSDynamicCast(OSKext,
+ dependencies->getObject(i));
+ dependencyKext->addBleedthroughDependencies(linkDependencies);
+ }
+ }
+
+ num_kxlddeps = linkDependencies->getCount();
if (!num_kxlddeps) {
OSKextLog(this,
kOSKextLogErrorLevel |
getIdentifierCString());
goto finish;
}
- kxlddeps = (u_char **)kalloc(num_kxlddeps * sizeof(*kxlddeps));
+
+ kxlddeps = (KXLDDependency *)kalloc_tag(num_kxlddeps * sizeof(*kxlddeps), VM_KERN_MEMORY_OSKEXT);
if (!kxlddeps) {
OSKextLog(this,
kOSKextLogErrorLevel |
getIdentifierCString());
goto finish;
}
-
- if (isKernelComponent()) {
- OSData * kernelLinkState = OSKext::getKernelLinkState();
- kxlddeps[0] = (u_char *)kernelLinkState->getBytesNoCopy();
- } else for (i = 0; i < num_kxlddeps; i++) {
- OSKext * dependency = OSDynamicCast(OSKext, dependencies->getObject(i));
- if (!dependency->linkState) {
- // xxx - maybe we should panic here
- OSKextLog(this,
- kOSKextLogErrorLevel |
- kOSKextLogLoadFlag | kOSKextLogLinkFlag,
- "Can't load kext %s - link state missing.",
- getIdentifierCString());
- goto finish;
+ bzero(kxlddeps, num_kxlddeps * sizeof(*kxlddeps));
+
+ for (i = 0; i < num_kxlddeps; ++i ) {
+ OSKext * dependency = OSDynamicCast(OSKext, linkDependencies->getObject(i));
+
+ if (dependency->isInterface()) {
+ OSKext *interfaceTargetKext = NULL;
+ OSData * interfaceTarget = NULL;
+
+ if (dependency->isKernelComponent()) {
+ interfaceTargetKext = sKernelKext;
+ interfaceTarget = sKernelKext->linkedExecutable;
+ } else {
+ interfaceTargetKext = OSDynamicCast(OSKext,
+ dependency->dependencies->getObject(0));
+
+ interfaceTarget = interfaceTargetKext->linkedExecutable;
+ }
+
+ if (!interfaceTarget) {
+ // panic?
+ goto finish;
+ }
+
+ /* The names set here aren't actually logged yet <rdar://problem/7941514>,
+ * it will be useful to have them in the debugger.
+ * strdup() failing isn't critical right here so we don't check that.
+ */
+ kxlddeps[i].kext = (u_char *) interfaceTarget->getBytesNoCopy();
+ kxlddeps[i].kext_size = interfaceTarget->getLength();
+ kxlddeps[i].kext_name = strdup(interfaceTargetKext->getIdentifierCString());
+
+ kxlddeps[i].interface = (u_char *) dependency->linkedExecutable->getBytesNoCopy();
+ kxlddeps[i].interface_size = dependency->linkedExecutable->getLength();
+ kxlddeps[i].interface_name = strdup(dependency->getIdentifierCString());
+ } else {
+ kxlddeps[i].kext = (u_char *) dependency->linkedExecutable->getBytesNoCopy();
+ kxlddeps[i].kext_size = dependency->linkedExecutable->getLength();
+ kxlddeps[i].kext_name = strdup(dependency->getIdentifierCString());
}
- kxlddeps[i] = (u_char *)dependency->linkState->getBytesNoCopy();
- assert(kxlddeps[i]);
- }
- /* We only need link state for a library kext.
- */
- if (compatibleVersion > -1 && (declaresExecutable() || isKernelComponent())) {
- linkStateBytesPtr = &linkStateBytes;
- linkStateLengthPtr = &linkStateLength;
+ kxlddeps[i].is_direct_dependency = (i < numDirectDependencies);
}
- /* We only need the linked executable for a real kext.
- */
- if (!isInterface()) {
- kxldHeaderPtr = &kxld_header;
- }
+ kxldHeaderPtr = &kxld_header;
#if DEBUG
OSKextLog(this,
" executable: %p executable_length: %d\n"
" user_data: %p\n"
" kxld_dependencies: %p num_dependencies: %d\n"
- " kxld_header_ptr: %p kmod_info_ptr: %p\n"
- " link_state_ptr: %p link_state_length_ptr: %p",
- getIdentifierCString(), kxldContext,
+ " kxld_header_ptr: %p kmod_info_ptr: %p\n",
+ getIdentifierCString(), sKxldContext,
theExecutable->getBytesNoCopy(), theExecutable->getLength(),
this, kxlddeps, num_kxlddeps,
- kxldHeaderPtr, kernelKmodInfoPtr,
- linkStateBytesPtr, linkStateLengthPtr);
+ kxldHeaderPtr, &kmod_info);
#endif
/* After this call, the linkedExecutable instance variable
(u_char *)theExecutable->getBytesNoCopy(),
theExecutable->getLength(),
getIdentifierCString(), this, kxlddeps, num_kxlddeps,
- (u_char **)kxldHeaderPtr, (kxld_addr_t *)&kmod_info,
- linkStateBytesPtr, linkStateLengthPtr,
- /* symbolFile */ NULL, /* symbolFileSize */ NULL);
+ (u_char **)kxldHeaderPtr, (kxld_addr_t *)&kmod_info);
if (kxldResult != KERN_SUCCESS) {
// xxx - add kxldResult here?
result = kOSKextReturnLinkError;
goto finish;
}
-
- /* If we got a link state, wrap it in an OSData and keep it
- * around for later use linking other kexts that depend on this kext.
- */
- if (linkStateBytes && linkStateLength > 0) {
- linkState = OSData::withBytesNoCopy(linkStateBytes, linkStateLength);
- assert(linkState);
- linkState->setDeallocFunction(&osdata_kmem_free);
- }
- /* If this isn't an interface, We've written data & instructions into kernel
- * memory, so flush the data cache and invalidate the instruction cache.
+ /* We've written data & instructions into kernel memory, so flush the data
+ * cache and invalidate the instruction cache.
+ * I/D caches are coherent on x86
*/
- if (!isInterface()) {
- flush_dcache(kmod_info->address, kmod_info->size, false);
- invalidate_icache(kmod_info->address, kmod_info->size, false);
- }
-
+#if !defined(__i386__) && !defined(__x86_64__)
+ flush_dcache(kmod_info->address, kmod_info->size, false);
+ invalidate_icache(kmod_info->address, kmod_info->size, false);
+#endif
register_kmod:
if (isInterface()) {
/* Whip up a fake kmod_info entry for the interface kext.
*/
- kmod_info = (kmod_info_t *)kalloc(sizeof(kmod_info_t));
+ kmod_info = (kmod_info_t *)kalloc_tag(sizeof(kmod_info_t), VM_KERN_MEMORY_OSKEXT);
if (!kmod_info) {
result = KERN_MEMORY_ERROR;
goto finish;
*/
num_kmod_refs = getNumDependencies();
if (num_kmod_refs) {
- kmod_info->reference_list = (kmod_reference_t *)kalloc(
- num_kmod_refs * sizeof(kmod_reference_t));
+ kmod_info->reference_list = (kmod_reference_t *)kalloc_tag(
+ num_kmod_refs * sizeof(kmod_reference_t), VM_KERN_MEMORY_OSKEXT);
if (!kmod_info->reference_list) {
result = KERN_MEMORY_ERROR;
goto finish;
"Kext %s executable loaded; %u pages at 0x%lx (load tag %u).",
kmod_info->name,
(unsigned)kmod_info->size / PAGE_SIZE,
- (unsigned long)kmod_info->address,
+ (unsigned long)VM_KERNEL_UNSLIDE(kmod_info->address),
(unsigned)kmod_info->id);
}
- result = setVMProtections();
+ /* if prelinked, VM protections are already set */
+ result = setVMAttributes(!isPrelinked(), true);
if (result != KERN_SUCCESS) {
goto finish;
}
+#if KASAN
+ kasan_load_kext((vm_offset_t)linkedExecutable->getBytesNoCopy(),
+ linkedExecutable->getLength(), getIdentifierCString());
+#else
+ if (lookupSection(KASAN_GLOBAL_SEGNAME, KASAN_GLOBAL_SECTNAME)) {
+ OSKextLog(this,
+ kOSKextLogErrorLevel | kOSKextLogLoadFlag,
+ "KASAN: cannot load KASAN-ified kext %s on a non-KASAN kernel\n",
+ getIdentifierCString()
+ );
+ result = KERN_FAILURE;
+ goto finish;
+ }
+#endif
+
result = kOSReturnSuccess;
finish:
- if (kxlddeps) kfree(kxlddeps, (num_kxlddeps * sizeof(void *)));
+ OSSafeReleaseNULL(linkDependencies);
+
+ /* Clear up locally allocated dependency info.
+ */
+ for (i = 0; i < num_kxlddeps; ++i ) {
+ size_t size;
+
+ if (kxlddeps[i].kext_name) {
+ size = 1 + strlen(kxlddeps[i].kext_name);
+ kfree(kxlddeps[i].kext_name, size);
+ }
+ if (kxlddeps[i].interface_name) {
+ size = 1 + strlen(kxlddeps[i].interface_name);
+ kfree(kxlddeps[i].interface_name, size);
+ }
+ }
+ if (kxlddeps) kfree(kxlddeps, (num_kxlddeps * sizeof(*kxlddeps)));
/* We no longer need the unrelocated executable (which the linker
* has altered anyhow).
}
/*********************************************************************
-* xxx - initWithPrelinkedInfoDict doesn't use this
+* The linkedit segment is used by the kext linker for dependency
+* resolution, and by dtrace for probe initialization. We can free it
+* for non-library kexts, since no kexts depend on non-library kexts
+* by definition, once dtrace has been initialized.
+*********************************************************************/
+void
+OSKext::jettisonLinkeditSegment(void)
+{
+ kernel_mach_header_t * machhdr = (kernel_mach_header_t *)kmod_info->address;
+ kernel_segment_command_t * linkedit = NULL;
+ vm_offset_t start;
+ vm_size_t linkeditsize, kextsize;
+ OSData * data = NULL;
+
+#if NO_KEXTD
+ /* We can free symbol tables for all embedded kexts because we don't
+ * support runtime kext linking.
+ */
+ if (sKeepSymbols || !isExecutable() || !linkedExecutable || flags.jettisonLinkeditSeg) {
+#else
+ if (sKeepSymbols || isLibrary() || !isExecutable() || !linkedExecutable || flags.jettisonLinkeditSeg) {
+#endif
+ goto finish;
+ }
+
+ /* Find the linkedit segment. If it's not the last segment, then freeing
+ * it will fragment the kext into multiple VM regions, which OSKext is not
+ * designed to handle, so we'll have to skip it.
+ */
+ linkedit = getsegbynamefromheader(machhdr, SEG_LINKEDIT);
+ if (!linkedit) {
+ goto finish;
+ }
+
+ if (round_page(kmod_info->address + kmod_info->size) !=
+ round_page(linkedit->vmaddr + linkedit->vmsize))
+ {
+ goto finish;
+ }
+
+ /* Create a new OSData for the smaller kext object.
+ */
+ linkeditsize = round_page(linkedit->vmsize);
+ kextsize = kmod_info->size - linkeditsize;
+ start = linkedit->vmaddr;
+
+ data = OSData::withBytesNoCopy((void *)kmod_info->address, kextsize);
+ if (!data) {
+ goto finish;
+ }
+
+ /* Fix the kmod info and linkedExecutable.
+ */
+ kmod_info->size = kextsize;
+
+#if VM_MAPPED_KEXTS
+ data->setDeallocFunction(osdata_kext_free);
+#else
+ data->setDeallocFunction(osdata_phys_free);
+#endif
+ linkedExecutable->setDeallocFunction(NULL);
+ linkedExecutable->release();
+ linkedExecutable = data;
+ flags.jettisonLinkeditSeg = 1;
+
+ /* Free the linkedit segment.
+ */
+#if VM_MAPPED_KEXTS
+ kext_free(start, linkeditsize);
+#else
+ ml_static_mfree(start, linkeditsize);
+#endif
+
+finish:
+ return;
+}
+
+/*********************************************************************
+* If there are whole pages that are unused betweem the last section
+* of the DATA segment and the end of the DATA segment then we can free
+* them
+*********************************************************************/
+void
+OSKext::jettisonDATASegmentPadding(void)
+{
+ kernel_mach_header_t * mh;
+ kernel_segment_command_t * dataSeg;
+ kernel_section_t * sec, * lastSec;
+ vm_offset_t dataSegEnd, lastSecEnd;
+ vm_size_t padSize;
+
+ mh = (kernel_mach_header_t *)kmod_info->address;
+
+ dataSeg = getsegbynamefromheader(mh, SEG_DATA);
+ if (dataSeg == NULL) {
+ return;
+ }
+
+ lastSec = NULL;
+ sec = firstsect(dataSeg);
+ while (sec != NULL) {
+ lastSec = sec;
+ sec = nextsect(dataSeg, sec);
+ }
+
+ if (lastSec == NULL) {
+ return;
+ }
+
+ if ((dataSeg->vmaddr != round_page(dataSeg->vmaddr)) ||
+ (dataSeg->vmsize != round_page(dataSeg->vmsize))) {
+ return;
+ }
+
+ dataSegEnd = dataSeg->vmaddr + dataSeg->vmsize;
+ lastSecEnd = round_page(lastSec->addr + lastSec->size);
+
+ if (dataSegEnd <= lastSecEnd) {
+ return;
+ }
+
+ padSize = dataSegEnd - lastSecEnd;
+
+ if (padSize >= PAGE_SIZE) {
+#if VM_MAPPED_KEXTS
+ kext_free(lastSecEnd, padSize);
+#else
+ ml_static_mfree(lastSecEnd, padSize);
+#endif
+ }
+}
+
+/*********************************************************************
*********************************************************************/
void
OSKext::setLinkedExecutable(OSData * anExecutable)
return;
}
+#if CONFIG_DTRACE
+/*********************************************************************
+* Go through all loaded kexts and tell them to register with dtrace.
+* The instance method only registers if necessary.
+*********************************************************************/
+/* static */
+void
+OSKext::registerKextsWithDTrace(void)
+{
+ uint32_t count = sLoadedKexts->getCount();
+ uint32_t i;
+
+ IORecursiveLockLock(sKextLock);
+
+ for (i = 0; i < count; i++) {
+ OSKext * thisKext = NULL; // do not release
+
+ thisKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i));
+ if (!thisKext || !thisKext->isExecutable()) {
+ continue;
+ }
+
+ thisKext->registerWithDTrace();
+ }
+
+ IORecursiveLockUnlock(sKextLock);
+
+ return;
+}
+
+extern "C" {
+ extern int (*dtrace_modload)(struct kmod_info *, uint32_t);
+ extern int (*dtrace_modunload)(struct kmod_info *);
+};
+
+/*********************************************************************
+*********************************************************************/
+void
+OSKext::registerWithDTrace(void)
+{
+ /* Register kext with dtrace. A dtrace_modload failure should not
+ * prevent a kext from loading, so we ignore the return code.
+ */
+ if (!flags.dtraceInitialized && (dtrace_modload != NULL)) {
+ uint32_t modflag = 0;
+ OSObject * forceInit = getPropertyForHostArch("OSBundleForceDTraceInit");
+ if (forceInit == kOSBooleanTrue) {
+ modflag |= KMOD_DTRACE_FORCE_INIT;
+ }
+
+ (void)(*dtrace_modload)(kmod_info, modflag);
+ flags.dtraceInitialized = true;
+ jettisonLinkeditSegment();
+ }
+ return;
+}
+/*********************************************************************
+*********************************************************************/
+void
+OSKext::unregisterWithDTrace(void)
+{
+ /* Unregister kext with dtrace. A dtrace_modunload failure should not
+ * prevent a kext from loading, so we ignore the return code.
+ */
+ if (flags.dtraceInitialized && (dtrace_modunload != NULL)) {
+ (void)(*dtrace_modunload)(kmod_info);
+ flags.dtraceInitialized = false;
+ }
+ return;
+}
+#endif /* CONFIG_DTRACE */
+
+
/*********************************************************************
* called only by loadExecutable()
*********************************************************************/
+#if !VM_MAPPED_KEXTS
+#if defined(__arm__) || defined(__arm64__)
+static inline kern_return_t
+OSKext_protect(
+ vm_map_t map,
+ vm_map_offset_t start,
+ vm_map_offset_t end,
+ vm_prot_t new_prot,
+ boolean_t set_max)
+{
+#pragma unused(map)
+ assert(map == kernel_map); // we can handle KEXTs arising from the PRELINK segment and no others
+ assert(start <= end);
+ if (start >= end)
+ return KERN_SUCCESS; // Punt segments of length zero (e.g., headers) or less (i.e., blunders)
+ else if (set_max)
+ return KERN_SUCCESS; // Punt set_max, as there's no mechanism to record that state
+ else
+ return ml_static_protect(start, end - start, new_prot);
+}
+
+static inline kern_return_t
+OSKext_wire(
+ vm_map_t map,
+ vm_map_offset_t start,
+ vm_map_offset_t end,
+ vm_prot_t access_type,
+ boolean_t user_wire)
+{
+#pragma unused(map,start,end,access_type,user_wire)
+ return KERN_SUCCESS; // No-op as PRELINK kexts are cemented into physical memory at boot
+}
+#else
+#error Unrecognized architecture
+#endif
+#else
+static inline kern_return_t
+OSKext_protect(
+ vm_map_t map,
+ vm_map_offset_t start,
+ vm_map_offset_t end,
+ vm_prot_t new_prot,
+ boolean_t set_max)
+{
+ if (start == end) { // 10538581
+ return(KERN_SUCCESS);
+ }
+ return vm_map_protect(map, start, end, new_prot, set_max);
+}
+
+static inline kern_return_t
+OSKext_wire(
+ vm_map_t map,
+ vm_map_offset_t start,
+ vm_map_offset_t end,
+ vm_prot_t access_type,
+ boolean_t user_wire)
+{
+ return vm_map_wire_kernel(map, start, end, access_type, VM_KERN_MEMORY_KEXT, user_wire);
+}
+#endif
+
OSReturn
-OSKext::setVMProtections(void)
+OSKext::setVMAttributes(bool protect, bool wire)
{
vm_map_t kext_map = NULL;
kernel_segment_command_t * seg = NULL;
vm_map_offset_t end = 0;
OSReturn result = kOSReturnError;
- if (!kmod_info->address && !kmod_info->size) {
+ if (isInterface() || !declaresExecutable()) {
result = kOSReturnSuccess;
goto finish;
}
goto finish;
}
- /* XXX: On arm, the vme covering the prelinked kernel (really, the whole
- * range from 0xc0000000 to a little over 0xe0000000) has maxprot set to 0
- * so the vm_map_protect calls below fail
- * I believe this happens in the call to vm_map_enter in kmem_init but I
- * need to confirm.
- */
+#if !VM_MAPPED_KEXTS
+ if (getcommandfromheader((kernel_mach_header_t *)kmod_info->address, LC_SEGMENT_SPLIT_INFO)) {
+ /* This is a split kext in a prelinked kernelcache; we'll let the
+ * platform code take care of protecting it. It is already wired.
+ */
+ /* TODO: Should this still allow protections for the first segment
+ * to go through, in the event that we have a mix of split and
+ * unsplit kexts?
+ */
+ result = KERN_SUCCESS;
+ goto finish;
+ }
+#endif
+
/* Protect the headers as read-only; they do not need to be wired */
- result = vm_map_protect(kext_map, kmod_info->address,
- kmod_info->address + kmod_info->hdr_size, VM_PROT_READ, TRUE);
+ result = (protect) ? OSKext_protect(kext_map, kmod_info->address,
+ kmod_info->address + kmod_info->hdr_size, VM_PROT_READ, TRUE)
+ : KERN_SUCCESS;
if (result != KERN_SUCCESS) {
goto finish;
}
/* Set the VM protections and wire down each of the segments */
seg = firstsegfromheader((kernel_mach_header_t *)kmod_info->address);
while (seg) {
+
+#if __arm__
+ /* We build all ARM kexts, so we can ensure they are aligned */
+ assert((seg->vmaddr & PAGE_MASK) == 0);
+ assert((seg->vmsize & PAGE_MASK) == 0);
+#endif
+
start = round_page(seg->vmaddr);
end = trunc_page(seg->vmaddr + seg->vmsize);
- result = vm_map_protect(kext_map, start, end, seg->maxprot, TRUE);
- if (result != KERN_SUCCESS) {
- OSKextLog(this,
- kOSKextLogErrorLevel |
- kOSKextLogLoadFlag,
- "Kext %s failed to set maximum VM protections "
- "for segment %s - 0x%x.",
- getIdentifierCString(), seg->segname, (int)result);
- goto finish;
- }
+ if (protect) {
+ result = OSKext_protect(kext_map, start, end, seg->maxprot, TRUE);
+ if (result != KERN_SUCCESS) {
+ OSKextLog(this,
+ kOSKextLogErrorLevel |
+ kOSKextLogLoadFlag,
+ "Kext %s failed to set maximum VM protections "
+ "for segment %s - 0x%x.",
+ getIdentifierCString(), seg->segname, (int)result);
+ goto finish;
+ }
- result = vm_map_protect(kext_map, start, end, seg->initprot, FALSE);
- if (result != KERN_SUCCESS) {
- OSKextLog(this,
- kOSKextLogErrorLevel |
- kOSKextLogLoadFlag,
- "Kext %s failed to set initial VM protections "
- "for segment %s - 0x%x.",
- getIdentifierCString(), seg->segname, (int)result);
- goto finish;
+ result = OSKext_protect(kext_map, start, end, seg->initprot, FALSE);
+ if (result != KERN_SUCCESS) {
+ OSKextLog(this,
+ kOSKextLogErrorLevel |
+ kOSKextLogLoadFlag,
+ "Kext %s failed to set initial VM protections "
+ "for segment %s - 0x%x.",
+ getIdentifierCString(), seg->segname, (int)result);
+ goto finish;
+ }
}
- result = vm_map_wire(kext_map, start, end, seg->initprot, FALSE);
- if (result != KERN_SUCCESS) {
- goto finish;
+ if (segmentShouldBeWired(seg) && wire) {
+ result = OSKext_wire(kext_map, start, end, seg->initprot, FALSE);
+ if (result != KERN_SUCCESS) {
+ goto finish;
+ }
}
seg = nextsegfromheader((kernel_mach_header_t *) kmod_info->address, seg);
return result;
}
+/*********************************************************************
+*********************************************************************/
+boolean_t
+OSKext::segmentShouldBeWired(kernel_segment_command_t *seg)
+{
+ return (sKeepSymbols || strncmp(seg->segname, SEG_LINKEDIT, sizeof(seg->segname)));
+}
+
/*********************************************************************
*********************************************************************/
OSReturn
const char * whichOp = startFlag ? "start" : "stop";
kern_return_t kern_result = 0;
vm_map_t kext_map = NULL;
+ kernel_segment_command_t * seg = NULL;
mach_vm_address_t address = 0;
mach_vm_size_t size = 0;
uint32_t depth = 0;
/* Verify that the start/stop function lies within the kext's address range.
*/
- if (address < kmod_info->address + kmod_info->hdr_size ||
- kmod_info->address + kmod_info->size <= address)
- {
- OSKextLog(this,
- kOSKextLogErrorLevel |
- kOSKextLogLoadFlag,
- "Kext %s module %s pointer is outside of kext range "
- "(%s %p - kext at %p-%p)..",
- getIdentifierCString(),
- whichOp,
- whichOp,
- (void *)address,
- (void *)kmod_info->address,
- (void *)(kmod_info->address + kmod_info->size));
- result = kOSKextReturnBadData;
- goto finish;
+ if (getcommandfromheader((kernel_mach_header_t *)kmod_info->address, LC_SEGMENT_SPLIT_INFO)) {
+ /* This will likely be how we deal with split kexts; walk the segments to
+ * check that the function lies inside one of the segments of this kext.
+ */
+ for (seg = firstsegfromheader((kernel_mach_header_t *)kmod_info->address);
+ seg != NULL;
+ seg = nextsegfromheader((kernel_mach_header_t *)kmod_info->address, seg)) {
+ if ((address >= seg->vmaddr) && address < (seg->vmaddr + seg->vmsize)) {
+ break;
+ }
+ }
+
+ if (!seg) {
+ OSKextLog(this,
+ kOSKextLogErrorLevel |
+ kOSKextLogLoadFlag,
+ "Kext %s module %s pointer is outside of kext range "
+ "(%s %p - kext starts at %p).",
+ getIdentifierCString(),
+ whichOp,
+ whichOp,
+ (void *)VM_KERNEL_UNSLIDE(address),
+ (void *)VM_KERNEL_UNSLIDE(kmod_info->address));
+ result = kOSKextReturnBadData;
+ goto finish;
+ }
+
+ seg = NULL;
+ } else {
+ if (address < kmod_info->address + kmod_info->hdr_size ||
+ kmod_info->address + kmod_info->size <= address)
+ {
+ OSKextLog(this,
+ kOSKextLogErrorLevel |
+ kOSKextLogLoadFlag,
+ "Kext %s module %s pointer is outside of kext range "
+ "(%s %p - kext at %p-%p).",
+ getIdentifierCString(),
+ whichOp,
+ whichOp,
+ (void *)VM_KERNEL_UNSLIDE(address),
+ (void *)VM_KERNEL_UNSLIDE(kmod_info->address),
+ (void *)(VM_KERNEL_UNSLIDE(kmod_info->address) + kmod_info->size));
+ result = kOSKextReturnBadData;
+ goto finish;
+ }
}
/* Only do these checks before calling the start function;
kOSKextLogLoadFlag,
"Kext %s - bad %s pointer %p.",
getIdentifierCString(),
- whichOp, (void *)address);
+ whichOp, (void *)VM_KERNEL_UNSLIDE(address));
result = kOSKextReturnBadData;
goto finish;
}
+#if VM_MAPPED_KEXTS
if (!(info.protection & VM_PROT_EXECUTE)) {
OSKextLog(this,
kOSKextLogErrorLevel |
result = kOSKextReturnBadData;
goto finish;
}
+#endif
- /* Verify that the kext is backed by physical memory.
+ /* Verify that the kext's segments are backed by physical memory.
*/
- for (address = kmod_info->address;
- address < round_page(kmod_info->address + kmod_info->size);
- address += PAGE_SIZE)
- {
- if (!pmap_find_phys(kernel_pmap, (vm_offset_t)address)) {
- OSKextLog(this,
- kOSKextLogErrorLevel |
- kOSKextLogLoadFlag,
- "Kext %s - page %p is not backed by physical memory.",
- getIdentifierCString(),
- (void *)address);
+ seg = firstsegfromheader((kernel_mach_header_t *)kmod_info->address);
+ while (seg) {
+ if (!verifySegmentMapping(seg)) {
result = kOSKextReturnBadData;
goto finish;
}
+
+ seg = nextsegfromheader((kernel_mach_header_t *) kmod_info->address, seg);
}
+
}
result = kOSReturnSuccess;
return result;
}
+/*********************************************************************
+*********************************************************************/
+boolean_t
+OSKext::verifySegmentMapping(kernel_segment_command_t *seg)
+{
+ mach_vm_address_t address = 0;
+
+ if (!segmentShouldBeWired(seg)) return true;
+
+ for (address = seg->vmaddr;
+ address < round_page(seg->vmaddr + seg->vmsize);
+ address += PAGE_SIZE)
+ {
+ if (!pmap_find_phys(kernel_pmap, (vm_offset_t)address)) {
+ OSKextLog(this,
+ kOSKextLogErrorLevel |
+ kOSKextLogLoadFlag,
+ "Kext %s - page %p is not backed by physical memory.",
+ getIdentifierCString(),
+ (void *)address);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*********************************************************************
+*********************************************************************/
+static void
+OSKextLogKextInfo(OSKext *aKext, uint64_t address, uint64_t size, firehose_tracepoint_code_t code)
+{
+
+ uint64_t stamp = 0;
+ firehose_tracepoint_id_u trace_id;
+ struct firehose_trace_uuid_info_s uuid_info_s;
+ firehose_trace_uuid_info_t uuid_info = &uuid_info_s;
+ size_t uuid_info_len = sizeof(struct firehose_trace_uuid_info_s);
+ OSData *uuid_data;
+
+ stamp = firehose_tracepoint_time(firehose_activity_flags_default);
+ trace_id.ftid_value = FIREHOSE_TRACE_ID_MAKE(firehose_tracepoint_namespace_metadata, _firehose_tracepoint_type_metadata_kext, (firehose_tracepoint_flags_t)0, code);
+
+ uuid_data = aKext->copyUUID();
+ if (uuid_data) {
+ memcpy(uuid_info->ftui_uuid, uuid_data->getBytesNoCopy(), sizeof(uuid_info->ftui_uuid));
+ OSSafeReleaseNULL(uuid_data);
+ }
+
+ uuid_info->ftui_size = size;
+ uuid_info->ftui_address = VM_KERNEL_UNSLIDE(address);
+
+ firehose_trace_metadata(firehose_stream_metadata, trace_id, stamp, uuid_info, uuid_info_len);
+ return;
+}
+
/*********************************************************************
*********************************************************************/
OSReturn
OSReturn result = kOSReturnError;
kern_return_t (* startfunc)(kmod_info_t *, void *);
unsigned int i, count;
- void * kmodStartData = NULL; // special handling needed
-#if CONFIG_MACF_KEXT
- mach_msg_type_number_t kmodStartDataCount = 0;
-#endif /* CONFIG_MACF_KEXT */
+ void * kmodStartData = NULL;
if (isStarted() || isInterface() || isKernelComponent()) {
result = kOSReturnSuccess;
goto finish;
}
+ if (!sLoadEnabled) {
+ OSKextLog(this,
+ kOSKextLogErrorLevel |
+ kOSKextLogLoadFlag,
+ "Kext loading is disabled (attempt to start kext %s).",
+ getIdentifierCString());
+ result = kOSKextReturnDisabled;
+ goto finish;
+ }
+
result = validateKextMapping(/* start? */ true);
if (result != kOSReturnSuccess) {
goto finish;
}
}
-#if CONFIG_MACF_KEXT
- /* See if the kext has any MAC framework module data in its plist.
- * This is passed in as arg #2 of the kext's start routine,
- * which is otherwise reserved for any other kext.
- */
- kmodStartData = MACFCopyModuleDataForKext(this, &kmodStartDataCount);
-#endif /* CONFIG_MACF_KEXT */
-
OSKextLog(this,
kOSKextLogDetailLevel |
kOSKextLogLoadFlag,
flags.starting = 1;
-#if !__i386__ && !__ppc__
+ // Drop a log message so logd can grab the needed information to decode this kext
+ OSKextLogKextInfo(this, kmod_info->address, kmod_info->size, firehose_tracepoint_code_load);
+
+#if !CONFIG_STATIC_CPPINIT
result = OSRuntimeInitializeCPP(kmod_info, NULL);
if (result == KERN_SUCCESS) {
#endif
+#if CONFIG_KEC_FIPS
+ kmodStartData = GetAppleTEXTHashForKext(this, this->infoDict);
+
+#if 0
+ if (kmodStartData) {
+ OSKextLog(this,
+ kOSKextLogErrorLevel |
+ kOSKextLogGeneralFlag,
+ "Kext %s calling module start function. kmodStartData %p. arch %s",
+ getIdentifierCString(), kmodStartData, ARCHNAME);
+ }
+#endif
+#endif // CONFIG_KEC_FIPS
result = startfunc(kmod_info, kmodStartData);
-#if !__i386__ && !__ppc__
+#if !CONFIG_STATIC_CPPINIT
if (result != KERN_SUCCESS) {
(void) OSRuntimeFinalizeCPP(kmod_info, NULL);
}
}
finish:
-#if CONFIG_MACF_KEXT
- /* Free the module data for a MAC framework kext. When we start using
- * param #2 we'll have to distinguish and free/release appropriately.
- *
- * xxx - I'm pretty sure the old codepath freed the data and that it's
- * xxx - up to the kext to copy it.
- */
- if (kmodStartData) {
- kmem_free(kernel_map, (vm_offset_t)kmodStartData, kmodStartDataCount);
- }
-#endif /* CONFIG_MACF_KEXT */
-
return result;
}
{
OSReturn result = kOSReturnError;
kern_return_t (*stopfunc)(kmod_info_t *, void *);
-
+
if (!isStarted() || isInterface()) {
result = kOSReturnSuccess;
goto finish;
goto finish;
}
- /* Save the list of loaded kexts in case we panic.
- */
- OSKext::saveUnloadedKextPanicList(this);
-
stopfunc = kmod_info->stop;
if (stopfunc) {
OSKextLog(this,
flags.stopping = 1;
result = stopfunc(kmod_info, /* userData */ NULL);
-#if !__i386__ && !__ppc__
+#if !CONFIG_STATIC_CPPINIT
if (result == KERN_SUCCESS) {
result = OSRuntimeFinalizeCPP(kmod_info, NULL);
}
}
finish:
+ // Drop a log message so logd can update this kext's metadata
+ OSKextLogKextInfo(this, kmod_info->address, kmod_info->size, firehose_tracepoint_code_unload);
return result;
}
OSReturn
OSKext::unload(void)
{
- OSReturn result = kOSReturnError;
- unsigned int index;
- uint32_t num_kmod_refs = 0;
+ OSReturn result = kOSReturnError;
+ unsigned int index;
+ uint32_t num_kmod_refs = 0;
+ OSKextAccount * freeAccount;
if (!sUnloadEnabled) {
OSKextLog(this,
goto finish;
}
+ if (!isLoaded()) {
+ result = kOSReturnSuccess;
+ goto finish;
+ }
+
+ if (isKernelComponent()) {
+ result = kOSKextReturnInvalidArgument;
+ goto finish;
+ }
- if (hasOSMetaClassInstances()) {
+ if (metaClasses && !OSMetaClass::removeClasses(metaClasses)) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag | kOSKextLogKextBookkeepingFlag,
result = kOSKextReturnInUse;
goto finish;
}
-
- if (!isLoaded()) {
- result = kOSReturnSuccess;
- goto finish;
- }
-
- if (isKernelComponent()) {
- result = kOSKextReturnInvalidArgument;
- goto finish;
- }
/* Note that the kext is unloading before running any code that
* might be in the kext (request callbacks, module stop function).
* of unloading.
*/
flags.unloading = 1;
+
+ /* Update the string describing the last kext to unload in case we panic.
+ */
+ savePanicString(/* isLoading */ false);
if (isStarted()) {
result = stop();
"Kext %s unloading.",
getIdentifierCString());
+ {
+ struct list_head *p;
+ struct list_head *prev;
+ struct list_head *next;
+ for (p = pendingPgoHead.next; p != &pendingPgoHead; p = next) {
+ OSKextGrabPgoStruct *s = container_of(p, OSKextGrabPgoStruct, list_head);
+ s->err = OSKextGrabPgoDataLocked(this, s->metadata, instance_uuid, s->pSize, s->pBuffer, s->bufferSize);
+ prev = p->prev;
+ next = p->next;
+ prev->next = next;
+ next->prev = prev;
+ p->prev = p;
+ p->next = p;
+ IORecursiveLockWakeup(sKextLock, s, false);
+ }
+ }
+
+
/* Even if we don't call the stop function, we want to be sure we
* have no OSMetaClass references before unloading the kext executable
* from memory. OSMetaClasses may have pointers into the kext executable
}
OSKext * lastKext = OSDynamicCast(OSKext, sLoadedKexts->getLastObject());
- if (lastKext && lastKext != sKernelKext) {
+ if (lastKext && !lastKext->isKernel()) {
kmod = lastKext->kmod_info;
} else {
kmod = NULL; // clear the global kmod variable
num_kmod_refs * sizeof(kmod_reference_t));
}
- /* If we have a linked executable, release & clear it, and then
- * unwire & deallocate the buffer the OSData wrapped.
- */
+#if CONFIG_DTRACE
+ unregisterWithDTrace();
+#endif /* CONFIG_DTRACE */
+
+ notifyKextUnloadObservers(this);
+
+ freeAccount = NULL;
+ IOSimpleLockLock(sKextAccountsLock);
+ account->kext = NULL;
+ if (account->site.tag) account->site.flags |= VM_TAG_UNLOAD;
+ else freeAccount = account;
+ IOSimpleLockUnlock(sKextAccountsLock);
+ if (freeAccount) IODelete(freeAccount, OSKextAccount, 1);
+
+ /* Unwire and free the linked executable.
+ */
if (linkedExecutable) {
- vm_map_t kext_map;
+#if KASAN
+ kasan_unload_kext((vm_offset_t)linkedExecutable->getBytesNoCopy(), linkedExecutable->getLength());
+#endif
- /* linkedExecutable is just a wrapper for the executable and doesn't
- * free it.
- */
- linkedExecutable->release();
- linkedExecutable = NULL;
+#if VM_MAPPED_KEXTS
+ if (!isInterface()) {
+ kernel_segment_command_t *seg = NULL;
+ vm_map_t kext_map = kext_get_vm_map(kmod_info);
- OSKextLog(this,
- kOSKextLogProgressLevel |
- kOSKextLogLoadFlag,
- "Kext %s unwiring and unmapping linked executable.",
- getIdentifierCString());
+ if (!kext_map) {
+ OSKextLog(this,
+ kOSKextLogErrorLevel |
+ kOSKextLogLoadFlag,
+ "Failed to free kext %s; couldn't find the kext map.",
+ getIdentifierCString());
+ result = kOSKextReturnInternalError;
+ goto finish;
+ }
+
+ OSKextLog(this,
+ kOSKextLogProgressLevel |
+ kOSKextLogLoadFlag,
+ "Kext %s unwiring and unmapping linked executable.",
+ getIdentifierCString());
+
+ seg = firstsegfromheader((kernel_mach_header_t *)kmod_info->address);
+ while (seg) {
+ if (segmentShouldBeWired(seg)) {
+ result = vm_map_unwire(kext_map, seg->vmaddr,
+ seg->vmaddr + seg->vmsize, FALSE);
+ if (result != KERN_SUCCESS) {
+ OSKextLog(this,
+ kOSKextLogErrorLevel |
+ kOSKextLogLoadFlag,
+ "Failed to unwire kext %s.",
+ getIdentifierCString());
+ result = kOSKextReturnInternalError;
+ goto finish;
+ }
+ }
- kext_map = kext_get_vm_map(kmod_info);
- if (kext_map) {
- // xxx - do we have to do this before freeing? Why can't we just free it?
- // xxx - we should be able to set a dealloc func on the linkedExecutable
- result = vm_map_unwire(kext_map,
- kmod_info->address + kmod_info->hdr_size,
- kmod_info->address + kmod_info->size, FALSE);
- if (result == KERN_SUCCESS) {
- kext_free(kmod_info->address, kmod_info->size);
+ seg = nextsegfromheader((kernel_mach_header_t *) kmod_info->address, seg);
}
}
+#endif
+ OSSafeReleaseNULL(linkedExecutable);
}
/* An interface kext has a fake kmod_info that was allocated,
flags.loaded = false;
flushDependencies();
+ /* save a copy of the bundle ID for us to check when deciding to
+ * rebuild the kernel cache file. If a kext was already in the kernel
+ * cache and unloaded then later loaded we do not need to rebuild the
+ * kernel cache. 9055303
+ */
+ if (isPrelinked()) {
+ if (!_OSKextInUnloadedPrelinkedKexts(bundleID)) {
+ IORecursiveLockLock(sKextLock);
+ if (sUnloadedPrelinkedKexts) {
+ sUnloadedPrelinkedKexts->setObject(bundleID);
+ }
+ IORecursiveLockUnlock(sKextLock);
+ }
+ }
+
OSKextLog(this,
kOSKextLogProgressLevel | kOSKextLogLoadFlag,
"Kext %s unloaded.", getIdentifierCString());
+ queueKextNotification(kKextRequestPredicateUnloadNotification,
+ OSDynamicCast(OSString, bundleID));
+
finish:
OSKext::saveLoadedKextPanicList();
+ OSKext::updateLoadedKextSummaries();
flags.unloading = 0;
return result;
}
+/*********************************************************************
+* Assumes sKextLock is held.
+*********************************************************************/
+/* static */
+OSReturn
+OSKext::queueKextNotification(
+ const char * notificationName,
+ OSString * kextIdentifier)
+{
+ OSReturn result = kOSReturnError;
+ OSDictionary * loadRequest = NULL; // must release
+
+ if (!kextIdentifier) {
+ result = kOSKextReturnInvalidArgument;
+ goto finish;
+ }
+
+ /* Create a new request unless one is already sitting
+ * in sKernelRequests for this bundle identifier
+ */
+ result = _OSKextCreateRequest(notificationName, &loadRequest);
+ if (result != kOSReturnSuccess) {
+ goto finish;
+ }
+ if (!_OSKextSetRequestArgument(loadRequest,
+ kKextRequestArgumentBundleIdentifierKey, kextIdentifier)) {
+
+ result = kOSKextReturnNoMemory;
+ goto finish;
+ }
+ if (!sKernelRequests->setObject(loadRequest)) {
+ result = kOSKextReturnNoMemory;
+ goto finish;
+ }
+
+ /* We might want to only queue the notification if kextd is active,
+ * but that wouldn't work for embedded. Note that we don't care if
+ * the ping immediately succeeds here so don't do anything with the
+ * result of this call.
+ */
+ OSKext::pingKextd();
+
+ result = kOSReturnSuccess;
+
+finish:
+ OSSafeReleaseNULL(loadRequest);
+
+ return result;
+}
+
/*********************************************************************
*********************************************************************/
static void
__unused thread_call_param_t p0,
__unused thread_call_param_t p1)
{
- /* Once both recursive locks are taken in correct order, we shouldn't
- * have to worry about further recursive lock takes.
+ /* Take multiple locks in the correct order.
*/
IORecursiveLockLock(sKextLock);
IORecursiveLockLock(sKextInnerLock);
* to avoid deadlocks with IOService, with which OSKext has a reciprocal
* call relationship.
*
-* Do not call any function that takes sKextLock here! This function
-* can be invoked with sKextInnerLock, and the two must always
-* be taken in the order: sKextLock -> sKextInnerLock.
+* This function must be invoked with sKextInnerLock held.
+* Do not call any function that takes sKextLock here!
*********************************************************************/
/* static */
void
return;
}
-/*********************************************************************
-*********************************************************************/
-OSData *
-OSKext::getKernelLinkState()
-{
- kern_return_t kxldResult;
- u_char * kernel = NULL;
- size_t kernelLength;
- u_char * linkStateBytes = NULL;
- u_long linkStateLength;
- OSData * linkState = NULL;
-
- if (sKernelKext && sKernelKext->linkState) {
- goto finish;
- }
-
- kernel = (u_char *)&_mh_execute_header;
- kernelLength = getlastaddr() - (vm_offset_t)kernel;
-
- kxldResult = kxld_link_file(sKxldContext,
- kernel,
- kernelLength,
- kOSKextKernelIdentifier,
- /* callbackData */ NULL,
- /* dependencies */ NULL,
- /* numDependencies */ 0,
- /* linkedObjectOut */ NULL,
- /* kmod_info_kern out */ NULL,
- &linkStateBytes,
- &linkStateLength,
- /* symbolFile */ NULL,
- /* symbolFileSize */ NULL);
- if (kxldResult) {
- panic("Can't generate kernel link state; no kexts can be loaded.");
- goto finish;
- }
-
- linkState = OSData::withBytesNoCopy(linkStateBytes, linkStateLength);
- linkState->setDeallocFunction(&osdata_kmem_free);
- sKernelKext->linkState = linkState;
-
-finish:
- return sKernelKext->linkState;
-}
-
#if PRAGMA_MARK
#pragma mark Autounload
#endif
* This is a static method because the kext will be deallocated if it
* does unload!
*********************************************************************/
+/* static */
OSReturn
OSKext::autounloadKext(OSKext * aKext)
{
result = OSKext::removeKext(aKext);
finish:
-
return result;
}
-
+
/*********************************************************************
*********************************************************************/
void
bool didUnload = false;
unsigned int count, i;
- /* Once both recursive locks are taken in correct order, we shouldn't
- * have to worry about further recursive lock takes.
+ /* Take multiple locks in the correct order
+ * (note also sKextSummaries lock further down).
*/
IORecursiveLockLock(sKextLock);
IORecursiveLockLock(sKextInnerLock);
OSKext::flushNonloadedKexts(/* flushPrelinkedKexts */ true);
-
+
/* If the system is powering down, don't try to unload anything.
*/
if (sSystemSleep) {
}
OSKextLog(/* kext */ NULL,
- kOSKextLogProgressLevel |
- kOSKextLogLoadFlag,
- "Checking for unused kexts to autounload.");
+ kOSKextLogProgressLevel | kOSKextLogLoadFlag,
+ "Checking for unused kexts to autounload.");
/*****
* Remove any request callbacks marked as stale,
OSBoolean * stale = OSDynamicCast(OSBoolean,
callbackRecord->getObject(kKextRequestStaleKey));
- if (stale && stale->isTrue()) {
+ if (stale == kOSBooleanTrue) {
OSKext::invokeRequestCallback(callbackRecord,
kOSKextReturnTimeout);
} else {
i = count - 1;
do {
OSKext * thisKext = OSDynamicCast(OSKext,
- sLoadedKexts->getObject(i));
- didUnload = (kOSReturnSuccess == OSKext::autounloadKext(thisKext));
+ sLoadedKexts->getObject(i));
+ didUnload |= (kOSReturnSuccess == OSKext::autounloadKext(thisKext));
} while (i--);
}
} while (didUnload);
sConsiderUnloadsExecuted = true;
(void) OSKext::considerRebuildOfPrelinkedKernel();
-
+
IORecursiveLockUnlock(sKextInnerLock);
IORecursiveLockUnlock(sKextLock);
sUnloadCallout = thread_call_allocate(&_OSKextConsiderUnloads, 0);
}
+ /* we only reset delay value for unloading if we already have something
+ * pending. rescheduleOnlyFlag should not start the count down.
+ */
if (rescheduleOnlyFlag && !sConsiderUnloadsPending) {
goto finish;
}
*********************************************************************/
extern "C" {
+IOReturn OSKextSystemSleepOrWake(UInt32 messageType);
IOReturn OSKextSystemSleepOrWake(UInt32 messageType)
{
IORecursiveLockLock(sKextInnerLock);
thread_call_cancel(sUnloadCallout);
}
sSystemSleep = true;
+ AbsoluteTime_to_scalar(&sLastWakeTime) = 0;
} else if (messageType == kIOMessageSystemHasPoweredOn) {
sSystemSleep = false;
- }
+ clock_get_uptime(&sLastWakeTime);
+ }
IORecursiveLockUnlock(sKextInnerLock);
return kIOReturnSuccess;
void
OSKext::considerRebuildOfPrelinkedKernel(void)
{
- OSReturn checkResult = kOSReturnError;
- static bool requestedPrelink = false;
- OSDictionary * prelinkRequest = NULL; // must release
-
+ static bool requestedPrelink = false;
+ OSReturn checkResult = kOSReturnError;
+ OSDictionary * prelinkRequest = NULL; // must release
+ OSCollectionIterator * kextIterator = NULL; // must release
+ const OSSymbol * thisID = NULL; // do not release
+ bool doRebuild = false;
+ AbsoluteTime my_abstime;
+ UInt64 my_ns;
+ SInt32 delta_secs;
+
+ /* Only one auto rebuild per boot and only on boot from prelinked kernel */
+ if (requestedPrelink || !sPrelinkBoot) {
+ return;
+ }
+
+ /* no direct return from this point */
IORecursiveLockLock(sKextLock);
-
- if (!sDeferredLoadSucceeded || !sConsiderUnloadsExecuted ||
- sSafeBoot || requestedPrelink)
- {
+
+ /* We need to wait for kextd to get up and running with unloads already done
+ * and any new startup kexts loaded.
+ */
+ if (!sConsiderUnloadsExecuted ||
+ !sDeferredLoadSucceeded) {
goto finish;
}
-
- OSKextLog(/* kext */ NULL,
- kOSKextLogProgressLevel |
- kOSKextLogArchiveFlag,
- "Requesting build of prelinked kernel.");
-
+
+ /* we really only care about boot / system start up related kexts so bail
+ * if we're here after REBUILD_MAX_TIME.
+ */
+ if (!_OSKextInPrelinkRebuildWindow()) {
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogArchiveFlag,
+ "%s prebuild rebuild has expired",
+ __FUNCTION__);
+ requestedPrelink = true;
+ goto finish;
+ }
+
+ /* we do not want to trigger a rebuild if we get here too close to waking
+ * up. (see radar 10233768)
+ */
+ IORecursiveLockLock(sKextInnerLock);
+
+ clock_get_uptime(&my_abstime);
+ delta_secs = MINIMUM_WAKEUP_SECONDS + 1;
+ if (AbsoluteTime_to_scalar(&sLastWakeTime) != 0) {
+ SUB_ABSOLUTETIME(&my_abstime, &sLastWakeTime);
+ absolutetime_to_nanoseconds(my_abstime, &my_ns);
+ delta_secs = (SInt32)(my_ns / NSEC_PER_SEC);
+ }
+ IORecursiveLockUnlock(sKextInnerLock);
+
+ if (delta_secs < MINIMUM_WAKEUP_SECONDS) {
+ /* too close to time of last wake from sleep */
+ goto finish;
+ }
+ requestedPrelink = true;
+
+ /* Now it's time to see if we have a reason to rebuild. We may have done
+ * some loads and unloads but the kernel cache didn't actually change.
+ * We will rebuild if any kext is not marked prelinked AND is not in our
+ * list of prelinked kexts that got unloaded. (see radar 9055303)
+ */
+ kextIterator = OSCollectionIterator::withCollection(sKextsByID);
+ if (!kextIterator) {
+ goto finish;
+ }
+
+ while ((thisID = OSDynamicCast(OSSymbol, kextIterator->getNextObject()))) {
+ OSKext * thisKext; // do not release
+
+ thisKext = OSDynamicCast(OSKext, sKextsByID->getObject(thisID));
+ if (!thisKext || thisKext->isPrelinked() || thisKext->isKernel()) {
+ continue;
+ }
+
+ if (_OSKextInUnloadedPrelinkedKexts(thisKext->bundleID)) {
+ continue;
+ }
+ /* kext is loaded and was not in current kernel cache so let's rebuild
+ */
+ doRebuild = true;
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogArchiveFlag,
+ "considerRebuildOfPrelinkedKernel %s triggered rebuild",
+ thisKext->bundleID->getCStringNoCopy());
+ break;
+ }
+ sUnloadedPrelinkedKexts->flushCollection();
+
+ if (!doRebuild) {
+ goto finish;
+ }
+
checkResult = _OSKextCreateRequest(kKextRequestPredicateRequestPrelink,
- &prelinkRequest);
+ &prelinkRequest);
if (checkResult != kOSReturnSuccess) {
goto finish;
}
-
+
if (!sKernelRequests->setObject(prelinkRequest)) {
goto finish;
}
-
- OSKextPingKextd();
- requestedPrelink = true;
-
+
+ OSKext::pingKextd();
+
finish:
IORecursiveLockUnlock(sKextLock);
- OSSafeRelease(prelinkRequest);
+ OSSafeReleaseNULL(prelinkRequest);
+ OSSafeReleaseNULL(kextIterator);
+
return;
}
libraryVersion->getCStringNoCopy());
goto finish;
}
-
+
+ /* If a nonprelinked library somehow got into the mix for a
+ * prelinked kext, at any point in the chain, we must fail
+ * because the prelinked relocs for the library will be all wrong.
+ */
+ if (this->isPrelinked() &&
+ libraryKext->declaresExecutable() &&
+ !libraryKext->isPrelinked()) {
+
+ OSKextLog(this,
+ kOSKextLogErrorLevel |
+ kOSKextLogDependenciesFlag,
+ "Kext %s (prelinked) - library kext %s (v%s) not prelinked.",
+ getIdentifierCString(), library_id,
+ libraryVersion->getCStringNoCopy());
+ goto finish;
+ }
+
if (!libraryKext->resolveDependencies(loopStack)) {
goto finish;
}
}
}
+ if (hasRawKernelDependency) {
+ OSKextLog(this,
+ kOSKextLogErrorLevel |
+ kOSKextLogValidationFlag | kOSKextLogDependenciesFlag,
+ "Error - kext %s declares a dependency on %s, which is not permitted.",
+ getIdentifierCString(), KERNEL_LIB);
+ goto finish;
+ }
#if __LP64__
- if (hasRawKernelDependency || hasKernelDependency) {
+ if (hasKernelDependency) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogValidationFlag | kOSKextLogDependenciesFlag,
// xxx - is it invalid to do both "com.apple.kernel" and any
// xxx - "com.apple.kernel.*"?
- if (hasRawKernelDependency && hasKernelDependency) {
- OSKextLog(this,
- kOSKextLogErrorLevel |
- kOSKextLogValidationFlag | kOSKextLogDependenciesFlag,
- "Error - kext %s declares dependencies on both "
- "%s and %s.",
- getIdentifierCString(), KERNEL_LIB, KERNEL6_LIB);
- goto finish;
- }
-
- if ((hasRawKernelDependency || hasKernelDependency) && hasKPIDependency) {
+ if (hasKernelDependency && hasKPIDependency) {
OSKextLog(this,
kOSKextLogWarningLevel |
kOSKextLogDependenciesFlag,
getIdentifierCString(), KERNEL_LIB, KPI_LIB_PREFIX);
}
- if (!hasRawKernelDependency && !hasKernelDependency && !hasKPIDependency) {
+ if (!hasKernelDependency && !hasKPIDependency) {
// xxx - do we want to use validation flag for these too?
OSKextLog(this,
kOSKextLogWarningLevel |
* its indirect dependencies to simulate old-style linking. XXX - Should
* check for duplicates.
*/
- if (!hasRawKernelDependency && !hasKPIDependency) {
+ if (!hasKPIDependency) {
unsigned int i;
+ flags.hasBleedthrough = true;
+
count = getNumDependencies();
/* We add to the dependencies array in this loop, but do not iterate
getIdentifierCString());
}
- OSSafeRelease(localLoopStack);
- OSSafeRelease(libraryIterator);
+ OSSafeReleaseNULL(localLoopStack);
+ OSSafeReleaseNULL(libraryIterator);
return result;
}
}
}
+ notifyAddClassObservers(this, aClass, flags);
+
result = kOSReturnSuccess;
finish:
metaClasses->removeObject(aClass);
+ notifyRemoveClassObservers(this, aClass, flags);
+
result = kOSReturnSuccess;
finish:
finish:
- OSSafeRelease(classIterator);
+ OSSafeReleaseNULL(classIterator);
return result;
}
theKext->reportOSMetaClassInstances(msgLogSpec);
finish:
- OSSafeRelease(theKext);
+ OSSafeReleaseNULL(theKext);
return;
}
}
finish:
- OSSafeRelease(classIterator);
+ OSSafeReleaseNULL(classIterator);
return;
}
char * response = NULL; // returned by reference
uint32_t responseLength = 0;
- OSObject * parsedXML = NULL; // must release
+ OSObject * parsedXML = NULL; // must release
OSDictionary * requestDict = NULL; // do not release
OSString * errorString = NULL; // must release
- OSData * responseData = NULL; // must release
- OSObject * responseObject = NULL; // must release
+ OSObject * responseObject = NULL; // must release
OSSerialize * serializer = NULL; // must release
kOSKextLogIPCFlag,
"Received '%s' request from user space.",
predicate->getCStringNoCopy());
-
+
result = kOSKextReturnNotPrivileged;
if (hostPriv == HOST_PRIV_NULL) {
- if (!predicate->isEqualTo(kKextRequestPredicateGetLoaded) &&
- !predicate->isEqualTo(kKextRequestPredicateGetKernelLinkState) &&
- !predicate->isEqualTo(kKextRequestPredicateGetKernelLoadAddress)) {
-
- goto finish;
+ /* must be root to use these kext requests */
+ if (predicate->isEqualTo(kKextRequestPredicateUnload) ||
+ predicate->isEqualTo(kKextRequestPredicateStart) ||
+ predicate->isEqualTo(kKextRequestPredicateStop) ||
+ predicate->isEqualTo(kKextRequestPredicateGetKernelRequests) ||
+ predicate->isEqualTo(kKextRequestPredicateSendResource) ) {
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogErrorLevel |
+ kOSKextLogIPCFlag,
+ "Access Failure - must be root user.");
+ goto finish;
}
}
} else if (predicate->isEqualTo(kKextRequestPredicateSendResource)) {
result = OSKext::dispatchResource(requestDict);
- } else if (predicate->isEqualTo(kKextRequestPredicateGetLoaded)) {
- OSBoolean * delayAutounloadBool = NULL;
+ } else if (predicate->isEqualTo(kKextRequestPredicateGetUUIDByAddress)) {
+
+ OSNumber *lookupNum = NULL;
+ lookupNum = OSDynamicCast(OSNumber,
+ _OSKextGetRequestArgument(requestDict,
+ kKextRequestArgumentLookupAddressKey));
+
+ responseObject = OSKext::copyKextUUIDForAddress(lookupNum);
+ if (responseObject) {
+ result = kOSReturnSuccess;
+ } else {
+ goto finish;
+ }
+
+ } else if (predicate->isEqualTo(kKextRequestPredicateGetLoaded) ||
+ predicate->isEqualTo(kKextRequestPredicateGetLoadedByUUID)) {
+ OSBoolean * delayAutounloadBool = NULL;
+ OSObject * infoKeysRaw = NULL;
+ OSArray * infoKeys = NULL;
+ uint32_t infoKeysCount = 0;
delayAutounloadBool = OSDynamicCast(OSBoolean,
_OSKextGetRequestArgument(requestDict,
OSKext::considerUnloads(/* rescheduleOnly? */ true);
}
- responseObject = OSDynamicCast(OSObject,
- OSKext::copyLoadedKextInfo(kextIdentifiers));
+ infoKeysRaw = _OSKextGetRequestArgument(requestDict,
+ kKextRequestArgumentInfoKeysKey);
+ infoKeys = OSDynamicCast(OSArray, infoKeysRaw);
+ if (infoKeysRaw && !infoKeys) {
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogErrorLevel |
+ kOSKextLogIPCFlag,
+ "Invalid arguments to kext info request.");
+ goto finish;
+ }
+
+ if (infoKeys) {
+ infoKeysCount = infoKeys->getCount();
+ for (uint32_t i = 0; i < infoKeysCount; i++) {
+ if (!OSDynamicCast(OSString, infoKeys->getObject(i))) {
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogErrorLevel |
+ kOSKextLogIPCFlag,
+ "Invalid arguments to kext info request.");
+ goto finish;
+ }
+ }
+ }
+
+ if (predicate->isEqualTo(kKextRequestPredicateGetLoaded)) {
+ responseObject = OSKext::copyLoadedKextInfo(kextIdentifiers, infoKeys);
+ }
+ else if (predicate->isEqualTo(kKextRequestPredicateGetLoadedByUUID)) {
+ responseObject = OSKext::copyLoadedKextInfoByUUID(kextIdentifiers, infoKeys);
+ }
if (!responseObject) {
result = kOSKextReturnInternalError;
} else {
"Returning loaded kext info.");
result = kOSReturnSuccess;
}
-
- } else if (predicate->isEqualTo(kKextRequestPredicateGetKernelLoadAddress)) {
- OSNumber * addressNum = NULL; // released as responseObject
- kernel_segment_command_t * textseg = getsegbyname("__TEXT");
-
- if (!textseg) {
- OSKextLog(/* kext */ NULL,
- kOSKextLogErrorLevel |
- kOSKextLogGeneralFlag | kOSKextLogIPCFlag,
- "Can't find text segment for kernel load address.");
- result = kOSReturnError;
- goto finish;
- }
-
- OSKextLog(/* kext */ NULL,
- kOSKextLogDebugLevel |
- kOSKextLogIPCFlag,
- "Returning kernel load address 0x%llx.",
- (unsigned long long)textseg->vmaddr);
- addressNum = OSNumber::withNumber((long long unsigned int)textseg->vmaddr,
- 8 * sizeof(long long unsigned int));
- responseObject = OSDynamicCast(OSObject, addressNum);
- result = kOSReturnSuccess;
-
- } else if (predicate->isEqualTo(kKextRequestPredicateGetKernelLinkState)) {
- OSKextLog(/* kext */ NULL,
- kOSKextLogDebugLevel |
- kOSKextLogIPCFlag,
- "Returning kernel link state.");
- responseData = sKernelKext->linkState;
- responseData->retain();
- result = kOSReturnSuccess;
-
} else if (predicate->isEqualTo(kKextRequestPredicateGetKernelRequests)) {
/* Hand the current sKernelRequests array to the caller
* (who must release it), and make a new one.
*/
- responseObject = OSDynamicCast(OSObject, sKernelRequests);
+ responseObject = sKernelRequests;
sKernelRequests = OSArray::withCapacity(0);
sPostedKextLoadIdentifiers->flushCollection();
OSKextLog(/* kext */ NULL,
} else if (predicate->isEqualTo(kKextRequestPredicateGetAllLoadRequests)) {
/* Return the set of all requested bundle identifiers */
- responseObject = OSDynamicCast(OSObject, sAllKextLoadIdentifiers);
+ responseObject = sAllKextLoadIdentifiers;
responseObject->retain();
OSKextLog(/* kext */ NULL,
kOSKextLogDebugLevel |
"Returning load requests.");
result = kOSReturnSuccess;
}
+ else {
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogDebugLevel |
+ kOSKextLogIPCFlag,
+ "Received '%s' invalid request from user space.",
+ predicate->getCStringNoCopy());
+ goto finish;
+ }
/**********
* Now we have handle the request, or not. Gather up the response & logging
"probable memory leak.");
}
- if (responseData && responseObject) {
- OSKextLog(/* kext */ NULL,
- kOSKextLogErrorLevel |
- kOSKextLogIPCFlag,
- "Mistakenly generated both data & plist responses to user request "
- "(returning only data).");
- }
-
- if (responseData && responseData->getLength() && responseOut) {
-
- response = (char *)responseData->getBytesNoCopy();
- responseLength = responseData->getLength();
- } else if (responseOut && responseObject) {
+ if (responseOut && responseObject) {
serializer = OSSerialize::withCapacity(0);
if (!serializer) {
result = kOSKextReturnNoMemory;
goto finish;
}
- if (!responseObject->serialize(serializer)) {
- OSKextLog(/* kext */ NULL,
- kOSKextLogErrorLevel |
- kOSKextLogIPCFlag,
- "Failed to serialize response to request from user space.");
- result = kOSKextReturnSerialization;
- goto finish;
+ if (!responseObject->serialize(serializer)) {
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogGeneralFlag | kOSKextLogErrorLevel,
+ "Failed to serialize response to request from user space.");
+ result = kOSKextReturnSerialization;
+ goto finish;
+ }
+
+ response = (char *)serializer->text();
+ responseLength = serializer->getLength();
+ }
+
+ if (responseOut && response) {
+ char * buffer;
+
+ /* This kmem_alloc sets the return value of the function.
+ */
+ kmem_result = kmem_alloc(kernel_map, (vm_offset_t *)&buffer,
+ round_page(responseLength), VM_KERN_MEMORY_OSKEXT);
+ if (kmem_result != KERN_SUCCESS) {
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogErrorLevel |
+ kOSKextLogIPCFlag,
+ "Failed to copy response to request from user space.");
+ result = kmem_result;
+ goto finish;
+ } else {
+ /* 11981737 - clear uninitialized data in last page */
+ bzero((void *)(buffer + responseLength),
+ (round_page(responseLength) - responseLength));
+ memcpy(buffer, response, responseLength);
+ *responseOut = buffer;
+ *responseLengthOut = responseLength;
+ }
+ }
+
+finish:
+
+ /* Gather up the collected log messages for user space. Any messages
+ * messages past this call will not make it up as log messages but
+ * will be in the system log. Note that we ignore the return of the
+ * serialize; it has no bearing on the operation at hand even if we
+ * fail to get the log messages.
+ */
+ logInfoArray = OSKext::clearUserSpaceLogFilter();
+
+ if (logInfoArray && logInfoOut && logInfoLengthOut) {
+ (void)OSKext::serializeLogInfo(logInfoArray,
+ logInfoOut, logInfoLengthOut);
+ }
+
+ IORecursiveLockUnlock(sKextLock);
+
+ OSSafeReleaseNULL(parsedXML);
+ OSSafeReleaseNULL(errorString);
+ OSSafeReleaseNULL(responseObject);
+ OSSafeReleaseNULL(serializer);
+ OSSafeReleaseNULL(logInfoArray);
+
+ return result;
+}
+
+
+// #include <InstrProfiling.h>
+extern "C" {
+
+ uint64_t __llvm_profile_get_size_for_buffer_internal(const char *DataBegin,
+ const char *DataEnd,
+ const char *CountersBegin,
+ const char *CountersEnd ,
+ const char *NamesBegin,
+ const char *NamesEnd);
+ int __llvm_profile_write_buffer_internal(char *Buffer,
+ const char *DataBegin,
+ const char *DataEnd,
+ const char *CountersBegin,
+ const char *CountersEnd ,
+ const char *NamesBegin,
+ const char *NamesEnd);
+}
+
+
+static
+void OSKextPgoMetadataPut(char *pBuffer,
+ size_t *position,
+ size_t bufferSize,
+ uint32_t *num_pairs,
+ const char *key,
+ const char *value)
+{
+ size_t strlen_key = strlen(key);
+ size_t strlen_value = strlen(value);
+ size_t len = strlen(key) + 1 + strlen(value) + 1;
+ char *pos = pBuffer + *position;
+ *position += len;
+ if (pBuffer && bufferSize && *position <= bufferSize) {
+ memcpy(pos, key, strlen_key); pos += strlen_key;
+ *(pos++) = '=';
+ memcpy(pos, value, strlen_value); pos += strlen_value;
+ *(pos++) = 0;
+ if (num_pairs) {
+ (*num_pairs)++;
+ }
+ }
+}
+
+
+static
+void OSKextPgoMetadataPutMax(size_t *position, const char *key, size_t value_max)
+{
+ *position += strlen(key) + 1 + value_max + 1;
+}
+
+
+static
+void OSKextPgoMetadataPutAll(OSKext *kext,
+ uuid_t instance_uuid,
+ char *pBuffer,
+ size_t *position,
+ size_t bufferSize,
+ uint32_t *num_pairs)
+{
+ _static_assert_1_arg(sizeof(clock_sec_t) % 2 == 0);
+ //log_10 2^16 ≈ 4.82
+ const size_t max_secs_string_size = 5 * sizeof(clock_sec_t)/2;
+ const size_t max_timestamp_string_size = max_secs_string_size + 1 + 6;
+
+ if (!pBuffer) {
+ OSKextPgoMetadataPutMax(position, "INSTANCE", 36);
+ OSKextPgoMetadataPutMax(position, "UUID", 36);
+ OSKextPgoMetadataPutMax(position, "TIMESTAMP", max_timestamp_string_size);
+ } else {
+ uuid_string_t instance_uuid_string;
+ uuid_unparse(instance_uuid, instance_uuid_string);
+ OSKextPgoMetadataPut(pBuffer, position, bufferSize, num_pairs,
+ "INSTANCE", instance_uuid_string);
+
+ OSData *uuid_data;
+ uuid_t uuid;
+ uuid_string_t uuid_string;
+ uuid_data = kext->copyUUID();
+ if (uuid_data) {
+ memcpy(uuid, uuid_data->getBytesNoCopy(), sizeof(uuid));
+ OSSafeReleaseNULL(uuid_data);
+ uuid_unparse(uuid, uuid_string);
+ OSKextPgoMetadataPut(pBuffer, position, bufferSize, num_pairs,
+ "UUID", uuid_string);
+ }
+
+ clock_sec_t secs;
+ clock_usec_t usecs;
+ clock_get_calendar_microtime(&secs, &usecs);
+ assert(usecs < 1000000);
+ char timestamp[max_timestamp_string_size + 1];
+ _static_assert_1_arg(sizeof(long) >= sizeof(clock_sec_t));
+ snprintf(timestamp, sizeof(timestamp), "%lu.%06d", (unsigned long)secs, (int)usecs);
+ OSKextPgoMetadataPut(pBuffer, position, bufferSize, num_pairs,
+ "TIMESTAMP", timestamp);
+ }
+
+ OSKextPgoMetadataPut(pBuffer, position, bufferSize, num_pairs,
+ "NAME", kext->getIdentifierCString());
+
+ char versionCString[kOSKextVersionMaxLength];
+ OSKextVersionGetString(kext->getVersion(), versionCString, kOSKextVersionMaxLength);
+ OSKextPgoMetadataPut(pBuffer, position, bufferSize, num_pairs,
+ "VERSION", versionCString);
+
+}
+
+static
+size_t OSKextPgoMetadataSize(OSKext *kext)
+{
+ size_t position = 0;
+ uuid_t fakeuuid = {};
+ OSKextPgoMetadataPutAll(kext, fakeuuid, NULL, &position, 0, NULL);
+ return position;
+}
+
+int OSKextGrabPgoDataLocked(OSKext *kext,
+ bool metadata,
+ uuid_t instance_uuid,
+ uint64_t *pSize,
+ char *pBuffer,
+ uint64_t bufferSize)
+{
+ int err = 0;
+
+ kernel_section_t *sect_prf_data = NULL;
+ kernel_section_t *sect_prf_name = NULL;
+ kernel_section_t *sect_prf_cnts = NULL;
+ uint64_t size;
+ size_t metadata_size = 0;
+
+ sect_prf_data = kext->lookupSection("__DATA", "__llvm_prf_data");
+ sect_prf_name = kext->lookupSection("__DATA", "__llvm_prf_name");
+ sect_prf_cnts = kext->lookupSection("__DATA", "__llvm_prf_cnts");
+
+ if (!sect_prf_data || !sect_prf_name || !sect_prf_cnts) {
+ err = ENOTSUP;
+ goto out;
+ }
+
+ size = __llvm_profile_get_size_for_buffer_internal(
+ (const char*) sect_prf_data->addr, (const char*) sect_prf_data->addr + sect_prf_data->size,
+ (const char*) sect_prf_cnts->addr, (const char*) sect_prf_cnts->addr + sect_prf_cnts->size,
+ (const char*) sect_prf_name->addr, (const char*) sect_prf_name->addr + sect_prf_name->size);
+
+ if (metadata) {
+ metadata_size = OSKextPgoMetadataSize(kext);
+ size += metadata_size;
+ size += sizeof(pgo_metadata_footer);
+ }
+
+
+ if (pSize) {
+ *pSize = size;
+ }
+
+ if (pBuffer && bufferSize) {
+ if (bufferSize < size) {
+ err = ERANGE;
+ goto out;
+ }
+
+ err = __llvm_profile_write_buffer_internal(
+ pBuffer,
+ (const char*) sect_prf_data->addr, (const char*) sect_prf_data->addr + sect_prf_data->size,
+ (const char*) sect_prf_cnts->addr, (const char*) sect_prf_cnts->addr + sect_prf_cnts->size,
+ (const char*) sect_prf_name->addr, (const char*) sect_prf_name->addr + sect_prf_name->size);
+
+ if (err) {
+ err = EIO;
+ goto out;
+ }
+
+ if (metadata) {
+ char *end_of_buffer = pBuffer + size;
+ struct pgo_metadata_footer *footerp = (struct pgo_metadata_footer *) (end_of_buffer - sizeof(struct pgo_metadata_footer));
+ char *metadata_buffer = end_of_buffer - (sizeof(struct pgo_metadata_footer) + metadata_size);
+
+ size_t metadata_position = 0;
+ uint32_t num_pairs = 0;
+ OSKextPgoMetadataPutAll(kext, instance_uuid, metadata_buffer, &metadata_position, metadata_size, &num_pairs);
+ while (metadata_position < metadata_size) {
+ metadata_buffer[metadata_position++] = 0;
+ }
+
+ struct pgo_metadata_footer footer;
+ footer.magic = htonl(0x6d657461);
+ footer.number_of_pairs = htonl( num_pairs );
+ footer.offset_to_pairs = htonl( sizeof(struct pgo_metadata_footer) + metadata_size );
+ memcpy(footerp, &footer, sizeof(footer));
+ }
+
+ }
+
+out:
+ return err;
+}
+
+
+int
+OSKextGrabPgoData(uuid_t uuid,
+ uint64_t *pSize,
+ char *pBuffer,
+ uint64_t bufferSize,
+ int wait_for_unload,
+ int metadata)
+{
+ int err = 0;
+ OSKext *kext = NULL;
+
+
+ IORecursiveLockLock(sKextLock);
+
+ kext = OSKext::lookupKextWithUUID(uuid);
+ if (!kext) {
+ err = ENOENT;
+ goto out;
+ }
+
+ if (wait_for_unload) {
+ OSKextGrabPgoStruct s;
+
+ s.metadata = metadata;
+ s.pSize = pSize;
+ s.pBuffer = pBuffer;
+ s.bufferSize = bufferSize;
+ s.err = EINTR;
+
+ struct list_head *prev = &kext->pendingPgoHead;
+ struct list_head *next = kext->pendingPgoHead.next;
+
+ s.list_head.prev = prev;
+ s.list_head.next = next;
+
+ prev->next = &s.list_head;
+ next->prev = &s.list_head;
+
+ kext->release();
+ kext = NULL;
+
+ IORecursiveLockSleep(sKextLock, &s, THREAD_ABORTSAFE);
+
+ prev = s.list_head.prev;
+ next = s.list_head.next;
+
+ prev->next = next;
+ next->prev = prev;
+
+ err = s.err;
+
+ } else {
+ err = OSKextGrabPgoDataLocked(kext, metadata, kext->instance_uuid, pSize, pBuffer, bufferSize);
+ }
+
+ out:
+ if (kext) {
+ kext->release();
+ }
+
+ IORecursiveLockUnlock(sKextLock);
+
+ return err;
+}
+
+void
+OSKextResetPgoCountersLock()
+{
+ IORecursiveLockLock(sKextLock);
+}
+
+void
+OSKextResetPgoCountersUnlock()
+{
+ IORecursiveLockUnlock(sKextLock);
+}
+
+
+extern unsigned int not_in_kdp;
+
+void
+OSKextResetPgoCounters()
+{
+ assert(!not_in_kdp);
+ uint32_t count = sLoadedKexts->getCount();
+ for (uint32_t i = 0; i < count; i++) {
+ OSKext *kext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i));
+ kernel_section_t *sect_prf_cnts = kext->lookupSection("__DATA", "__llvm_prf_cnts");
+ if (!sect_prf_cnts) {
+ continue;
+ }
+ memset((void*)sect_prf_cnts->addr, 0, sect_prf_cnts->size);
+ }
+}
+
+OSDictionary *
+OSKext::copyLoadedKextInfoByUUID(
+ OSArray * kextIdentifiers,
+ OSArray * infoKeys)
+{
+ OSDictionary * result = NULL;
+ OSDictionary * kextInfo = NULL; // must release
+ uint32_t count, i;
+ uint32_t idCount = 0;
+ uint32_t idIndex = 0;
+
+ IORecursiveLockLock(sKextLock);
+
+#if CONFIG_MACF
+ /* Is the calling process allowed to query kext info? */
+ if (current_task() != kernel_task) {
+ int macCheckResult = 0;
+ kauth_cred_t cred = NULL;
+
+ cred = kauth_cred_get_with_ref();
+ macCheckResult = mac_kext_check_query(cred);
+ kauth_cred_unref(&cred);
+
+ if (macCheckResult != 0) {
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogErrorLevel | kOSKextLogLoadFlag,
+ "Failed to query kext info (MAC policy error 0x%x).",
+ macCheckResult);
+ goto finish;
+ }
+ }
+#endif
+
+ /* Empty list of UUIDs is equivalent to no list (get all).
+ */
+ if (kextIdentifiers && !kextIdentifiers->getCount()) {
+ kextIdentifiers = NULL;
+ } else if (kextIdentifiers) {
+ idCount = kextIdentifiers->getCount();
+ }
+
+ /* Same for keys.
+ */
+ if (infoKeys && !infoKeys->getCount()) {
+ infoKeys = NULL;
+ }
+
+ count = sLoadedKexts->getCount();
+ result = OSDictionary::withCapacity(count);
+ if (!result) {
+ goto finish;
+ }
+
+ for (i = 0; i < count; i++) {
+ OSKext *thisKext = NULL; // do not release
+ Boolean includeThis = true;
+ uuid_t thisKextUUID;
+ OSData *uuid_data;
+ uuid_string_t uuid_key;
+
+ if (kextInfo) {
+ kextInfo->release();
+ kextInfo = NULL;
+ }
+
+ thisKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i));
+ if (!thisKext) {
+ continue;
+ }
+
+ uuid_data = thisKext->copyUUID();
+ if (!uuid_data) {
+ continue;
}
- response = (char *)serializer->text();
- responseLength = serializer->getLength();
- }
-
- if (responseOut && response) {
- char * buffer;
+ memcpy(&thisKextUUID, uuid_data->getBytesNoCopy(), sizeof(thisKextUUID));
+ OSSafeReleaseNULL(uuid_data);
- /* This kmem_alloc sets the return value of the function.
+ uuid_unparse(thisKextUUID, uuid_key);
+
+ /* Skip current kext if we have a list of UUIDs and
+ * it isn't in the list.
*/
- kmem_result = kmem_alloc(kernel_map, (vm_offset_t *)&buffer,
- responseLength);
- if (kmem_result != KERN_SUCCESS) {
- OSKextLog(/* kext */ NULL,
- kOSKextLogErrorLevel |
- kOSKextLogIPCFlag,
- "Failed to copy response to request from user space.");
- result = kmem_result;
- goto finish;
- } else {
- memcpy(buffer, response, responseLength);
- *responseOut = buffer;
- *responseLengthOut = responseLength;
- }
- }
+ if (kextIdentifiers) {
+ includeThis = false;
-finish:
+ for (idIndex = 0; idIndex < idCount; idIndex++) {
+ const OSString* wantedUUID = OSDynamicCast(OSString,
+ kextIdentifiers->getObject(idIndex));
- /* Gather up the collected log messages for user space. Any messages
- * messages past this call will not make it up as log messages but
- * will be in the system log. Note that we ignore the return of the
- * serialize; it has no bearing on the operation at hand even if we
- * fail to get the log messages.
- */
- logInfoArray = OSKext::clearUserSpaceLogFilter();
+ uuid_t uuid;
+ uuid_parse(wantedUUID->getCStringNoCopy(), uuid);
- if (logInfoArray && logInfoOut && logInfoLengthOut) {
- (void)OSKext::serializeLogInfo(logInfoArray,
- logInfoOut, logInfoLengthOut);
+ if (0 == uuid_compare(uuid, thisKextUUID)) {
+ includeThis = true;
+ break;
+ }
+
+ }
+ }
+
+ if (!includeThis) {
+ continue;
+ }
+
+ kextInfo = thisKext->copyInfo(infoKeys);
+ if (kextInfo) {
+ result->setObject(uuid_key, kextInfo);
+ }
}
+finish:
IORecursiveLockUnlock(sKextLock);
- OSSafeRelease(requestDict);
- OSSafeRelease(errorString);
- OSSafeRelease(responseData);
- OSSafeRelease(responseObject);
- OSSafeRelease(serializer);
- OSSafeRelease(logInfoArray);
+ if (kextInfo) kextInfo->release();
return result;
}
/*********************************************************************
*********************************************************************/
/* static */
-OSArray *
-OSKext::copyLoadedKextInfo(OSArray * kextIdentifiers)
+OSDictionary *
+OSKext::copyLoadedKextInfo(
+ OSArray * kextIdentifiers,
+ OSArray * infoKeys)
{
- OSArray * result = NULL;
+ OSDictionary * result = NULL;
OSDictionary * kextInfo = NULL; // must release
uint32_t count, i;
uint32_t idCount = 0;
IORecursiveLockLock(sKextLock);
+#if CONFIG_MACF
+ /* Is the calling process allowed to query kext info? */
+ if (current_task() != kernel_task) {
+ int macCheckResult = 0;
+ kauth_cred_t cred = NULL;
+
+ cred = kauth_cred_get_with_ref();
+ macCheckResult = mac_kext_check_query(cred);
+ kauth_cred_unref(&cred);
+
+ if (macCheckResult != 0) {
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogErrorLevel | kOSKextLogLoadFlag,
+ "Failed to query kext info (MAC policy error 0x%x).",
+ macCheckResult);
+ goto finish;
+ }
+ }
+#endif
+
/* Empty list of bundle ids is equivalent to no list (get all).
*/
if (kextIdentifiers && !kextIdentifiers->getCount()) {
idCount = kextIdentifiers->getCount();
}
+ /* Same for keys.
+ */
+ if (infoKeys && !infoKeys->getCount()) {
+ infoKeys = NULL;
+ }
+
count = sLoadedKexts->getCount();
- result = OSArray::withCapacity(count);
+ result = OSDictionary::withCapacity(count);
if (!result) {
goto finish;
}
+
+#if 0
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogErrorLevel |
+ kOSKextLogGeneralFlag,
+ "kaslr: vm_kernel_slide 0x%lx \n",
+ vm_kernel_slide);
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogErrorLevel |
+ kOSKextLogGeneralFlag,
+ "kaslr: vm_kernel_stext 0x%lx vm_kernel_etext 0x%lx \n",
+ vm_kernel_stext, vm_kernel_etext);
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogErrorLevel |
+ kOSKextLogGeneralFlag,
+ "kaslr: vm_kernel_base 0x%lx vm_kernel_top 0x%lx \n",
+ vm_kernel_base, vm_kernel_top);
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogErrorLevel |
+ kOSKextLogGeneralFlag,
+ "kaslr: vm_kext_base 0x%lx vm_kext_top 0x%lx \n",
+ vm_kext_base, vm_kext_top);
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogErrorLevel |
+ kOSKextLogGeneralFlag,
+ "kaslr: vm_prelink_stext 0x%lx vm_prelink_etext 0x%lx \n",
+ vm_prelink_stext, vm_prelink_etext);
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogErrorLevel |
+ kOSKextLogGeneralFlag,
+ "kaslr: vm_prelink_sinfo 0x%lx vm_prelink_einfo 0x%lx \n",
+ vm_prelink_sinfo, vm_prelink_einfo);
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogErrorLevel |
+ kOSKextLogGeneralFlag,
+ "kaslr: vm_slinkedit 0x%lx vm_elinkedit 0x%lx \n",
+ vm_slinkedit, vm_elinkedit);
+#endif
+
for (i = 0; i < count; i++) {
OSKext * thisKext = NULL; // do not release
Boolean includeThis = true;
continue;
}
- kextInfo = thisKext->copyInfo();
- result->setObject(kextInfo);
+ kextInfo = thisKext->copyInfo(infoKeys);
+ if (kextInfo) {
+ result->setObject(thisKext->getIdentifier(), kextInfo);
+ }
}
finish:
}
/*********************************************************************
-Load Tag
-Bundle ID
-Bundle Version
-Path
-Load Address
-Load Size
-Wired Size
-Version
-Dependency Load Tags
-# Dependent References
-UUID
-RetainCount
+* Any info that needs to do allocations must goto finish on alloc
+* failure. Info that is just a lookup should just not set the object
+* if the info does not exist.
*********************************************************************/
#define _OSKextLoadInfoDictCapacity (12)
OSDictionary *
-OSKext::copyInfo(void)
-{
- OSDictionary * result = NULL;
- bool success = false;
- OSNumber * cpuTypeNumber = NULL; // must release
- OSNumber * cpuSubtypeNumber = NULL; // must release
- OSString * versionString = NULL; // do not release
- OSData * uuid = NULL; // must release
- OSNumber * scratchNumber = NULL; // must release
- OSArray * dependencyLoadTags = NULL; // must release
- OSCollectionIterator * metaClassIterator = NULL; // must release
- OSArray * metaClassInfo = NULL; // must release
- OSDictionary * metaClassDict = NULL; // must release
- OSMetaClass * thisMetaClass = NULL; // do not release
- OSString * metaClassName = NULL; // must release
- OSString * superclassName = NULL; // must release
+OSKext::copyInfo(OSArray * infoKeys)
+{
+ OSDictionary * result = NULL;
+ bool success = false;
+ OSData * headerData = NULL; // must release
+ OSData * logData = NULL; // must release
+ OSNumber * cpuTypeNumber = NULL; // must release
+ OSNumber * cpuSubtypeNumber = NULL; // must release
+ OSString * versionString = NULL; // do not release
+ uint32_t executablePathCStringSize = 0;
+ char * executablePathCString = NULL; // must release
+ OSString * executablePathString = NULL; // must release
+ OSData * uuid = NULL; // must release
+ OSNumber * scratchNumber = NULL; // must release
+ OSArray * dependencyLoadTags = NULL; // must release
+ OSCollectionIterator * metaClassIterator = NULL; // must release
+ OSArray * metaClassInfo = NULL; // must release
+ OSDictionary * metaClassDict = NULL; // must release
+ OSMetaClass * thisMetaClass = NULL; // do not release
+ OSString * metaClassName = NULL; // must release
+ OSString * superclassName = NULL; // must release
uint32_t count, i;
result = OSDictionary::withCapacity(_OSKextLoadInfoDictCapacity);
goto finish;
}
- /* CPU Type & Subtype.
- * Use the CPU type of the kernel for all (loaded) kexts.
- * xxx - should we not include this for the kernel components,
- * xxx - or for any interface? they have mach-o files, they're just weird.
+
+ /* Empty keys means no keys, but NULL is quicker to check.
+ */
+ if (infoKeys && !infoKeys->getCount()) {
+ infoKeys = NULL;
+ }
+
+ /* Headers, CPU type, and CPU subtype.
*/
- if (linkedExecutable || (this == sKernelKext)) {
+ if (!infoKeys ||
+ _OSArrayContainsCString(infoKeys, kOSBundleMachOHeadersKey) ||
+ _OSArrayContainsCString(infoKeys, kOSBundleLogStringsKey) ||
+ _OSArrayContainsCString(infoKeys, kOSBundleCPUTypeKey) ||
+ _OSArrayContainsCString(infoKeys, kOSBundleCPUSubtypeKey))
+ {
+
+ if (linkedExecutable && !isInterface()) {
+
+ kernel_mach_header_t *kext_mach_hdr = (kernel_mach_header_t *)
+ linkedExecutable->getBytesNoCopy();
- cpuTypeNumber = OSNumber::withNumber(
- (long long unsigned int)_mh_execute_header.cputype,
- 8 * sizeof(_mh_execute_header.cputype));
- if (cpuTypeNumber) {
- result->setObject(kOSBundleCPUTypeKey, cpuTypeNumber);
+#if !SECURE_KERNEL
+ // do not return macho header info on shipping iOS - 19095897
+ if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleMachOHeadersKey)) {
+ kernel_mach_header_t * temp_kext_mach_hdr;
+ struct load_command * lcp;
+
+ headerData = OSData::withBytes(kext_mach_hdr,
+ (u_int) (sizeof(*kext_mach_hdr) + kext_mach_hdr->sizeofcmds));
+ if (!headerData) {
+ goto finish;
+ }
+
+ // unslide any vmaddrs we return to userspace - 10726716
+ temp_kext_mach_hdr = (kernel_mach_header_t *)
+ headerData->getBytesNoCopy();
+ if (temp_kext_mach_hdr == NULL) {
+ goto finish;
+ }
+
+ lcp = (struct load_command *) (temp_kext_mach_hdr + 1);
+ for (i = 0; i < temp_kext_mach_hdr->ncmds; i++) {
+ if (lcp->cmd == LC_SEGMENT_KERNEL) {
+ kernel_segment_command_t * segp;
+ kernel_section_t * secp;
+
+ segp = (kernel_segment_command_t *) lcp;
+ // 10543468 - if we jettisoned __LINKEDIT clear size info
+ if (flags.jettisonLinkeditSeg) {
+ if (strncmp(segp->segname, SEG_LINKEDIT, sizeof(segp->segname)) == 0) {
+ segp->vmsize = 0;
+ segp->fileoff = 0;
+ segp->filesize = 0;
+ }
+ }
+
+#if 0
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogErrorLevel |
+ kOSKextLogGeneralFlag,
+ "%s: LC_SEGMENT_KERNEL segname '%s' vmaddr 0x%llX 0x%lX vmsize %llu nsects %u",
+ __FUNCTION__, segp->segname, segp->vmaddr,
+ VM_KERNEL_UNSLIDE(segp->vmaddr),
+ segp->vmsize, segp->nsects);
+ if ( (VM_KERNEL_IS_SLID(segp->vmaddr) == false) &&
+ (VM_KERNEL_IS_KEXT(segp->vmaddr) == false) &&
+ (VM_KERNEL_IS_PRELINKTEXT(segp->vmaddr) == false) &&
+ (VM_KERNEL_IS_PRELINKINFO(segp->vmaddr) == false) &&
+ (VM_KERNEL_IS_KEXT_LINKEDIT(segp->vmaddr) == false) ) {
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogErrorLevel |
+ kOSKextLogGeneralFlag,
+ "%s: not in kext range - vmaddr 0x%llX vm_kext_base 0x%lX vm_kext_top 0x%lX",
+ __FUNCTION__, segp->vmaddr, vm_kext_base, vm_kext_top);
+ }
+#endif
+ segp->vmaddr = VM_KERNEL_UNSLIDE(segp->vmaddr);
+
+ for (secp = firstsect(segp); secp != NULL; secp = nextsect(segp, secp)) {
+ secp->addr = VM_KERNEL_UNSLIDE(secp->addr);
+ }
+ }
+ lcp = (struct load_command *)((caddr_t)lcp + lcp->cmdsize);
+ }
+ result->setObject(kOSBundleMachOHeadersKey, headerData);
+ }
+#endif // SECURE_KERNEL
+
+ if (_OSArrayContainsCString(infoKeys, kOSBundleLogStringsKey)) {
+ osLogDataHeaderRef *header;
+ char headerBytes[offsetof(osLogDataHeaderRef, sections) + NUM_OS_LOG_SECTIONS * sizeof(header->sections[0])];
+
+ void *os_log_data = NULL;
+ void *cstring_data = NULL;
+ unsigned long os_log_size = 0;
+ unsigned long cstring_size = 0;
+ uint32_t os_log_offset = 0;
+ uint32_t cstring_offset = 0;
+ bool res;
+
+ os_log_data = getsectdatafromheader(kext_mach_hdr, "__TEXT", "__os_log", &os_log_size);
+ os_log_offset = getsectoffsetfromheader(kext_mach_hdr, "__TEXT", "__os_log");
+ cstring_data = getsectdatafromheader(kext_mach_hdr, "__TEXT", "__cstring", &cstring_size);
+ cstring_offset = getsectoffsetfromheader(kext_mach_hdr, "__TEXT", "__cstring");
+
+ header = (osLogDataHeaderRef *) headerBytes;
+ header->version = OS_LOG_HDR_VERSION;
+ header->sect_count = NUM_OS_LOG_SECTIONS;
+ header->sections[OS_LOG_SECT_IDX].sect_offset = os_log_offset;
+ header->sections[OS_LOG_SECT_IDX].sect_size = (uint32_t) os_log_size;
+ header->sections[CSTRING_SECT_IDX].sect_offset = cstring_offset;
+ header->sections[CSTRING_SECT_IDX].sect_size = (uint32_t) cstring_size;
+
+
+ logData = OSData::withBytes(header, (u_int) (sizeof(osLogDataHeaderRef)));
+ if (!logData) {
+ goto finish;
+ }
+ res = logData->appendBytes(&(header->sections[0]), (u_int)(header->sect_count * sizeof(header->sections[0])));
+ if (!res) {
+ goto finish;
+ }
+ if (os_log_data) {
+ res = logData->appendBytes(os_log_data, (u_int)header->sections[OS_LOG_SECT_IDX].sect_size);
+ if (!res) {
+ goto finish;
+ }
+ }
+ if (cstring_data) {
+ res = logData->appendBytes(cstring_data, (u_int)header->sections[CSTRING_SECT_IDX].sect_size);
+ if (!res) {
+ goto finish;
+ }
+ }
+ result->setObject(kOSBundleLogStringsKey, logData);
+ }
+
+ if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleCPUTypeKey)) {
+ cpuTypeNumber = OSNumber::withNumber(
+ (uint64_t) kext_mach_hdr->cputype,
+ 8 * sizeof(kext_mach_hdr->cputype));
+ if (!cpuTypeNumber) {
+ goto finish;
+ }
+ result->setObject(kOSBundleCPUTypeKey, cpuTypeNumber);
+ }
+
+ if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleCPUSubtypeKey)) {
+ cpuSubtypeNumber = OSNumber::withNumber(
+ (uint64_t) kext_mach_hdr->cpusubtype,
+ 8 * sizeof(kext_mach_hdr->cpusubtype));
+ if (!cpuSubtypeNumber) {
+ goto finish;
+ }
+ result->setObject(kOSBundleCPUSubtypeKey, cpuSubtypeNumber);
+ }
}
}
- // I don't want to rely on a mach header for nonkernel kexts, yet
- if (this == sKernelKext) {
- cpuSubtypeNumber = OSNumber::withNumber(
- (long long unsigned int)_mh_execute_header.cputype,
- 8 * sizeof(_mh_execute_header.cputype));
- if (cpuSubtypeNumber) {
- result->setObject(kOSBundleCPUSubtypeKey, cpuSubtypeNumber);
- }
- }
-
- /* CFBundleIdentifier.
+ /* CFBundleIdentifier. We set this regardless because it's just stupid not to.
*/
result->setObject(kCFBundleIdentifierKey, bundleID);
/* CFBundleVersion.
*/
- versionString = OSDynamicCast(OSString,
- getPropertyForHostArch(kCFBundleVersionKey));
- if (versionString) {
- result->setObject(kCFBundleVersionKey, versionString);
+ if (!infoKeys || _OSArrayContainsCString(infoKeys, kCFBundleVersionKey)) {
+ versionString = OSDynamicCast(OSString,
+ getPropertyForHostArch(kCFBundleVersionKey));
+ if (versionString) {
+ result->setObject(kCFBundleVersionKey, versionString);
+ }
}
/* OSBundleCompatibleVersion.
*/
- versionString = OSDynamicCast(OSString,
- getPropertyForHostArch(kOSBundleCompatibleVersionKey));
- if (versionString) {
- result->setObject(kOSBundleCompatibleVersionKey, versionString);
+ if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleCompatibleVersionKey)) {
+ versionString = OSDynamicCast(OSString,
+ getPropertyForHostArch(kOSBundleCompatibleVersionKey));
+ if (versionString) {
+ result->setObject(kOSBundleCompatibleVersionKey, versionString);
+ }
}
/* Path.
*/
- if (path) {
- result->setObject(kOSBundlePathKey, path);
+ if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundlePathKey)) {
+ if (path) {
+ result->setObject(kOSBundlePathKey, path);
+ }
}
- /* UUID.
+
+ /* OSBundleExecutablePath.
*/
- uuid = copyUUID();
- if (uuid) {
- result->setObject(kOSBundleUUIDKey, uuid);
+ if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleExecutablePathKey)) {
+ if (path && executableRelPath) {
+
+ uint32_t pathLength = path->getLength(); // gets incremented below
+
+ // +1 for slash, +1 for \0
+ executablePathCStringSize = pathLength + executableRelPath->getLength() + 2;
+
+ executablePathCString = (char *)kalloc_tag((executablePathCStringSize) *
+ sizeof(char), VM_KERN_MEMORY_OSKEXT); // +1 for \0
+ if (!executablePathCString) {
+ goto finish;
+ }
+ strlcpy(executablePathCString, path->getCStringNoCopy(),
+ executablePathCStringSize);
+ executablePathCString[pathLength++] = '/';
+ executablePathCString[pathLength++] = '\0';
+ strlcat(executablePathCString, executableRelPath->getCStringNoCopy(),
+ executablePathCStringSize);
+
+ executablePathString = OSString::withCString(executablePathCString);
+
+ if (!executablePathString) {
+ goto finish;
+ }
+
+ result->setObject(kOSBundleExecutablePathKey, executablePathString);
+ }
+ }
+
+ /* UUID, if the kext has one.
+ */
+ if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleUUIDKey)) {
+ uuid = copyUUID();
+ if (uuid) {
+ result->setObject(kOSBundleUUIDKey, uuid);
+ }
}
/*****
* OSKernelResource, OSBundleIsInterface, OSBundlePrelinked, OSBundleStarted.
*/
- result->setObject(kOSKernelResourceKey,
- isKernelComponent() ? kOSBooleanTrue : kOSBooleanFalse);
+ if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSKernelResourceKey)) {
+ result->setObject(kOSKernelResourceKey,
+ isKernelComponent() ? kOSBooleanTrue : kOSBooleanFalse);
+ }
- result->setObject(kOSBundleIsInterfaceKey,
- isInterface() ? kOSBooleanTrue : kOSBooleanFalse);
+ if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleIsInterfaceKey)) {
+ result->setObject(kOSBundleIsInterfaceKey,
+ isInterface() ? kOSBooleanTrue : kOSBooleanFalse);
+ }
- result->setObject(kOSBundlePrelinkedKey,
- isPrelinked() ? kOSBooleanTrue : kOSBooleanFalse);
+ if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundlePrelinkedKey)) {
+ result->setObject(kOSBundlePrelinkedKey,
+ isPrelinked() ? kOSBooleanTrue : kOSBooleanFalse);
+ }
- result->setObject(kOSBundleStartedKey,
- isStarted() ? kOSBooleanTrue : kOSBooleanFalse);
+ if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleStartedKey)) {
+ result->setObject(kOSBundleStartedKey,
+ isStarted() ? kOSBooleanTrue : kOSBooleanFalse);
+ }
/* LoadTag (Index).
*/
- scratchNumber = OSNumber::withNumber((unsigned long long)loadTag,
- /* numBits */ 8 * sizeof(loadTag));
- if (scratchNumber) {
+ if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleLoadTagKey)) {
+ scratchNumber = OSNumber::withNumber((unsigned long long)loadTag,
+ /* numBits */ 8 * sizeof(loadTag));
+ if (!scratchNumber) {
+ goto finish;
+ }
result->setObject(kOSBundleLoadTagKey, scratchNumber);
OSSafeReleaseNULL(scratchNumber);
}
/* LoadAddress, LoadSize.
*/
- if (isInterface() || linkedExecutable) {
- /* These go to userspace via serialization, so we don't want any doubts
- * about their size.
- */
- uint64_t loadAddress = 0;
- uint32_t loadSize = 0;
- uint32_t wiredSize = 0;
-
- /* Interfaces always report 0 load address & size.
- * Just the way they roll.
- *
- * xxx - leaving in # when we have a linkedExecutable...a kernelcomp
- * xxx - shouldn't have one!
- */
- if (linkedExecutable /* && !isInterface() */) {
- loadAddress = (uint64_t)linkedExecutable->getBytesNoCopy();
- loadSize = linkedExecutable->getLength();
-
- /* If we have a kmod_info struct, calculated the wired size
- * from that. Otherwise it's the full load size.
+ if (!infoKeys ||
+ _OSArrayContainsCString(infoKeys, kOSBundleLoadAddressKey) ||
+ _OSArrayContainsCString(infoKeys, kOSBundleLoadSizeKey) ||
+ _OSArrayContainsCString(infoKeys, kOSBundleExecLoadAddressKey) ||
+ _OSArrayContainsCString(infoKeys, kOSBundleExecLoadSizeKey) ||
+ _OSArrayContainsCString(infoKeys, kOSBundleWiredSizeKey))
+ {
+ if (isInterface() || linkedExecutable) {
+ /* These go to userspace via serialization, so we don't want any doubts
+ * about their size.
*/
- if (kmod_info) {
- wiredSize = loadSize - kmod_info->hdr_size;
- } else {
- wiredSize = loadSize;
+ uint64_t loadAddress = 0;
+ uint32_t loadSize = 0;
+ uint32_t wiredSize = 0;
+ uint64_t execLoadAddress = 0;
+ uint32_t execLoadSize = 0;
+
+ /* Interfaces always report 0 load address & size.
+ * Just the way they roll.
+ *
+ * xxx - leaving in # when we have a linkedExecutable...a kernelcomp
+ * xxx - shouldn't have one!
+ */
+ if (linkedExecutable /* && !isInterface() */) {
+ kernel_mach_header_t *mh = NULL;
+ kernel_segment_command_t *seg = NULL;
+
+ loadAddress = (uint64_t)linkedExecutable->getBytesNoCopy();
+ mh = (kernel_mach_header_t *)loadAddress;
+ loadAddress = VM_KERNEL_UNSLIDE(loadAddress);
+ loadSize = linkedExecutable->getLength();
+
+ /* Walk through the kext, looking for the first executable
+ * segment in case we were asked for its size/address.
+ */
+ for (seg = firstsegfromheader(mh); seg != NULL; seg = nextsegfromheader(mh, seg)) {
+ if (seg->initprot & VM_PROT_EXECUTE) {
+ execLoadAddress = VM_KERNEL_UNSLIDE(seg->vmaddr);
+ execLoadSize = seg->vmsize;
+ break;
+ }
+ }
+
+ /* If we have a kmod_info struct, calculated the wired size
+ * from that. Otherwise it's the full load size.
+ */
+ if (kmod_info) {
+ wiredSize = loadSize - kmod_info->hdr_size;
+ } else {
+ wiredSize = loadSize;
+ }
}
- }
- scratchNumber = OSNumber::withNumber(
- (unsigned long long)(loadAddress),
- /* numBits */ 8 * sizeof(loadAddress));
- if (scratchNumber) {
- result->setObject(kOSBundleLoadAddressKey, scratchNumber);
- OSSafeReleaseNULL(scratchNumber);
- }
- scratchNumber = OSNumber::withNumber(
- (unsigned long long)(loadSize),
- /* numBits */ 8 * sizeof(loadSize));
- if (scratchNumber) {
- result->setObject(kOSBundleLoadSizeKey, scratchNumber);
- OSSafeReleaseNULL(scratchNumber);
- }
- scratchNumber = OSNumber::withNumber(
- (unsigned long long)(wiredSize),
- /* numBits */ 8 * sizeof(wiredSize));
- if (scratchNumber) {
- result->setObject(kOSBundleWiredSizeKey, scratchNumber);
- OSSafeReleaseNULL(scratchNumber);
+ if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleLoadAddressKey)) {
+ scratchNumber = OSNumber::withNumber(
+ (unsigned long long)(loadAddress),
+ /* numBits */ 8 * sizeof(loadAddress));
+ if (!scratchNumber) {
+ goto finish;
+ }
+ result->setObject(kOSBundleLoadAddressKey, scratchNumber);
+ OSSafeReleaseNULL(scratchNumber);
+ }
+ if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleExecLoadAddressKey)) {
+ scratchNumber = OSNumber::withNumber(
+ (unsigned long long)(execLoadAddress),
+ /* numBits */ 8 * sizeof(execLoadAddress));
+ if (!scratchNumber) {
+ goto finish;
+ }
+ result->setObject(kOSBundleExecLoadAddressKey, scratchNumber);
+ OSSafeReleaseNULL(scratchNumber);
+ }
+ if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleLoadSizeKey)) {
+ scratchNumber = OSNumber::withNumber(
+ (unsigned long long)(loadSize),
+ /* numBits */ 8 * sizeof(loadSize));
+ if (!scratchNumber) {
+ goto finish;
+ }
+ result->setObject(kOSBundleLoadSizeKey, scratchNumber);
+ OSSafeReleaseNULL(scratchNumber);
+ }
+ if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleExecLoadSizeKey)) {
+ scratchNumber = OSNumber::withNumber(
+ (unsigned long long)(execLoadSize),
+ /* numBits */ 8 * sizeof(execLoadSize));
+ if (!scratchNumber) {
+ goto finish;
+ }
+ result->setObject(kOSBundleExecLoadSizeKey, scratchNumber);
+ OSSafeReleaseNULL(scratchNumber);
+ }
+ if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleWiredSizeKey)) {
+ scratchNumber = OSNumber::withNumber(
+ (unsigned long long)(wiredSize),
+ /* numBits */ 8 * sizeof(wiredSize));
+ if (!scratchNumber) {
+ goto finish;
+ }
+ result->setObject(kOSBundleWiredSizeKey, scratchNumber);
+ OSSafeReleaseNULL(scratchNumber);
+ }
}
}
-
+
/* OSBundleDependencies. In descending order for
* easy compatibility with kextstat(8).
*/
- if ((count = getNumDependencies())) {
- dependencyLoadTags = OSArray::withCapacity(count);
- result->setObject(kOSBundleDependenciesKey, dependencyLoadTags);
+ if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleDependenciesKey)) {
+ if ((count = getNumDependencies())) {
+ dependencyLoadTags = OSArray::withCapacity(count);
+ result->setObject(kOSBundleDependenciesKey, dependencyLoadTags);
- i = count - 1;
- do {
- OSKext * dependency = OSDynamicCast(OSKext,
- dependencies->getObject(i));
+ i = count - 1;
+ do {
+ OSKext * dependency = OSDynamicCast(OSKext,
+ dependencies->getObject(i));
- OSSafeReleaseNULL(scratchNumber);
-
- if (!dependency) {
- continue;
- }
- scratchNumber = OSNumber::withNumber(
- (unsigned long long)dependency->getLoadTag(),
- /* numBits*/ 8 * sizeof(loadTag));
- if (scratchNumber) {
+ OSSafeReleaseNULL(scratchNumber);
+
+ if (!dependency) {
+ continue;
+ }
+ scratchNumber = OSNumber::withNumber(
+ (unsigned long long)dependency->getLoadTag(),
+ /* numBits*/ 8 * sizeof(loadTag));
+ if (!scratchNumber) {
+ goto finish;
+ }
dependencyLoadTags->setObject(scratchNumber);
- }
- } while (i--);
+ } while (i--);
+ }
}
OSSafeReleaseNULL(scratchNumber);
/* OSBundleMetaClasses.
*/
- if (metaClasses && metaClasses->getCount()) {
- metaClassIterator = OSCollectionIterator::withCollection(metaClasses);
- metaClassInfo = OSArray::withCapacity(metaClasses->getCount());
- if (!metaClassIterator || !metaClassInfo) {
- goto finish;
- }
- result->setObject(kOSBundleClassesKey, metaClassInfo);
+ if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleClassesKey)) {
+ if (metaClasses && metaClasses->getCount()) {
+ metaClassIterator = OSCollectionIterator::withCollection(metaClasses);
+ metaClassInfo = OSArray::withCapacity(metaClasses->getCount());
+ if (!metaClassIterator || !metaClassInfo) {
+ goto finish;
+ }
+ result->setObject(kOSBundleClassesKey, metaClassInfo);
- while ( (thisMetaClass = OSDynamicCast(OSMetaClass,
- metaClassIterator->getNextObject())) ) {
+ while ( (thisMetaClass = OSDynamicCast(OSMetaClass,
+ metaClassIterator->getNextObject())) ) {
- OSSafeReleaseNULL(metaClassDict);
- OSSafeReleaseNULL(metaClassName);
- OSSafeReleaseNULL(superclassName);
- OSSafeReleaseNULL(scratchNumber);
+ OSSafeReleaseNULL(metaClassDict);
+ OSSafeReleaseNULL(scratchNumber);
+ OSSafeReleaseNULL(metaClassName);
+ OSSafeReleaseNULL(superclassName);
- metaClassDict = OSDictionary::withCapacity(3);
- if (!metaClassDict) {
- goto finish;
- }
+ metaClassDict = OSDictionary::withCapacity(3);
+ if (!metaClassDict) {
+ goto finish;
+ }
- metaClassName = OSString::withCString(thisMetaClass->getClassName());
- if (thisMetaClass->getSuperClass()) {
- superclassName = OSString::withCString(
- thisMetaClass->getSuperClass()->getClassName());
- }
- scratchNumber = OSNumber::withNumber(thisMetaClass->getInstanceCount(),
- 8 * sizeof(unsigned int));
- if (!metaClassDict || !metaClassName || !superclassName ||
- !scratchNumber) {
+ metaClassName = OSString::withCString(thisMetaClass->getClassName());
+ if (thisMetaClass->getSuperClass()) {
+ superclassName = OSString::withCString(
+ thisMetaClass->getSuperClass()->getClassName());
+ }
+ scratchNumber = OSNumber::withNumber(thisMetaClass->getInstanceCount(),
+ 8 * sizeof(unsigned int));
+
+ /* Bail if any of the essentials is missing. The root class lacks a superclass,
+ * of course.
+ */
+ if (!metaClassDict || !metaClassName || !scratchNumber) {
+ goto finish;
+ }
- goto finish;
+ metaClassInfo->setObject(metaClassDict);
+ metaClassDict->setObject(kOSMetaClassNameKey, metaClassName);
+ if (superclassName) {
+ metaClassDict->setObject(kOSMetaClassSuperclassNameKey, superclassName);
+ }
+ metaClassDict->setObject(kOSMetaClassTrackingCountKey, scratchNumber);
}
-
- metaClassInfo->setObject(metaClassDict);
- metaClassDict->setObject(kOSMetaClassNameKey, metaClassName);
- metaClassDict->setObject(kOSMetaClassSuperclassNameKey, superclassName);
- metaClassDict->setObject(kOSMetaClassTrackingCountKey, scratchNumber);
}
}
/* OSBundleRetainCount.
*/
- OSSafeReleaseNULL(scratchNumber);
- {
- int extRetainCount = getRetainCount() - 1;
- if (isLoaded()) {
- extRetainCount--;
- }
- scratchNumber = OSNumber::withNumber(
- (int)extRetainCount,
- /* numBits*/ 8 * sizeof(int));
- if (scratchNumber) {
- result->setObject(kOSBundleRetainCountKey, scratchNumber);
+ if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleRetainCountKey)) {
+ OSSafeReleaseNULL(scratchNumber);
+ {
+ int kextRetainCount = getRetainCount() - 1;
+ if (isLoaded()) {
+ kextRetainCount--;
+ }
+ scratchNumber = OSNumber::withNumber(
+ (int)kextRetainCount,
+ /* numBits*/ 8 * sizeof(int));
+ if (scratchNumber) {
+ result->setObject(kOSBundleRetainCountKey, scratchNumber);
+ }
}
}
success = true;
+
finish:
- OSSafeRelease(cpuTypeNumber);
- OSSafeRelease(cpuSubtypeNumber);
- OSSafeRelease(uuid);
- OSSafeRelease(scratchNumber);
- OSSafeRelease(dependencyLoadTags);
- OSSafeRelease(metaClassIterator);
- OSSafeRelease(metaClassInfo);
- OSSafeRelease(metaClassDict);
- OSSafeRelease(metaClassName);
- OSSafeRelease(superclassName);
+ OSSafeReleaseNULL(headerData);
+ OSSafeReleaseNULL(logData);
+ OSSafeReleaseNULL(cpuTypeNumber);
+ OSSafeReleaseNULL(cpuSubtypeNumber);
+ OSSafeReleaseNULL(executablePathString);
+ if (executablePathCString) kfree(executablePathCString, executablePathCStringSize);
+ OSSafeReleaseNULL(uuid);
+ OSSafeReleaseNULL(scratchNumber);
+ OSSafeReleaseNULL(dependencyLoadTags);
+ OSSafeReleaseNULL(metaClassIterator);
+ OSSafeReleaseNULL(metaClassInfo);
+ OSSafeReleaseNULL(metaClassDict);
+ OSSafeReleaseNULL(metaClassName);
+ OSSafeReleaseNULL(superclassName);
if (!success) {
OSSafeReleaseNULL(result);
}
}
/*********************************************************************
-*********************************************************************/
+ *********************************************************************/
/* static */
OSReturn
OSKext::requestResource(
*requestTagOut = kOSKextRequestTagInvalid;
}
+ /* If requests to user space are disabled, don't go any further */
+ if (!sKernelRequestsEnabled) {
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogErrorLevel | kOSKextLogIPCFlag,
+ "Can't request resource %s for %s - requests to user space are disabled.",
+ resourceNameCString,
+ kextIdentifierCString);
+ result = kOSKextReturnDisabled;
+ goto finish;
+ }
+
if (!kextIdentifierCString || !resourceNameCString || !callback) {
result = kOSKextReturnInvalidArgument;
goto finish;
goto finish;
}
- OSKextPingKextd();
+ OSKext::pingKextd();
result = kOSReturnSuccess;
if (requestTagOut) {
}
/*********************************************************************
+* Assumes sKextLock is held.
*********************************************************************/
/* static */
OSReturn
callbackRecordOut);
finish:
- OSSafeRelease(requestTagNum);
+ OSSafeReleaseNULL(requestTagNum);
return result;
}
/*********************************************************************
+* Assumes sKextLock is held.
*********************************************************************/
/* static */
OSReturn
OSNumber * callbackTagNum = NULL; // do not release
unsigned int count, i;
- IORecursiveLockLock(sKextLock);
-
result = kOSReturnError;
count = sRequestCallbackRecords->getCount();
for (i = 0; i < count; i++) {
result = kOSKextReturnNotFound;
finish:
- IORecursiveLockUnlock(sKextLock);
return result;
}
+
+/*********************************************************************
+* Busy timeout triage
+*********************************************************************/
+/* static */
+bool
+OSKext::isWaitingKextd(void)
+{
+ return sRequestCallbackRecords && sRequestCallbackRecords->getCount();
+}
+
/*********************************************************************
+* Assumes sKextLock is held.
*********************************************************************/
/* static */
OSReturn
void * context = NULL; // do not free
OSKext * callbackKext = NULL; // must release (looked up)
- IORecursiveLockLock(sKextLock);
-
/* Get the args from the request. Right now we need the tag
* to look up the callback record, and the result for invoking the callback.
*/
if (!callbackKext) {
OSKextLog(/* kext */ NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
- "Can't invoke callback for resource request; "
- "no kext loaded at callback address %p.",
- callback);
+ "Can't invoke callback for resource request; ");
goto finish;
}
if (!callbackKext->flags.starting && !callbackKext->flags.started) {
OSKextLog(/* kext */ NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
- "Can't invoke kext resource callback; "
- "kext at callback address %p is not running.",
- callback);
+ "Can't invoke kext resource callback; ");
goto finish;
}
if (callbackKext) callbackKext->release();
if (callbackRecord) callbackRecord->release();
- IORecursiveLockUnlock(sKextLock);
return result;
}
}
/*********************************************************************
+* Assumes sKextLock is held.
*********************************************************************/
/* static */
OSReturn
OSDictionary * callbackRecord = NULL; // must release
OSData * contextWrapper = NULL; // do not release
+ IORecursiveLockLock(sKextLock);
result = OSKext::dequeueCallbackForRequestTag(requestTag,
&callbackRecord);
-
+ IORecursiveLockUnlock(sKextLock);
+
if (result == kOSReturnSuccess && contextOut) {
contextWrapper = OSDynamicCast(OSData,
_OSKextGetRequestArgument(callbackRecord,
}
/*********************************************************************
+* Assumes sKextLock is held.
*********************************************************************/
void
OSKext::invokeOrCancelRequestCallbacks(
{
unsigned int count, i;
- IORecursiveLockLock(sKextLock);
-
count = sRequestCallbackRecords->getCount();
if (!count) {
goto finish;
} while (i--);
finish:
- IORecursiveLockUnlock(sKextLock);
return;
}
/*********************************************************************
+* Assumes sKextLock is held.
*********************************************************************/
uint32_t
OSKext::countRequestCallbacks(void)
uint32_t result = 0;
unsigned int count, i;
- IORecursiveLockLock(sKextLock);
-
count = sRequestCallbackRecords->getCount();
if (!count) {
goto finish;
} while (i--);
finish:
- IORecursiveLockUnlock(sKextLock);
return result;
}
{
OSReturn result = kOSKextReturnNoMemory;
OSDictionary * request = NULL; // must release on error
- OSDictionary * args = NULL; // must release
request = OSDictionary::withCapacity(2);
if (!request) {
} else {
*requestP = request;
}
- if (args) args->release();
return result;
}
return result;
}
+/*********************************************************************
+*********************************************************************/
+static bool _OSArrayContainsCString(
+ OSArray * array,
+ const char * cString)
+{
+ bool result = false;
+ const OSSymbol * symbol = NULL;
+ uint32_t count, i;
+
+ if (!array || !cString) {
+ goto finish;
+ }
+
+ symbol = OSSymbol::withCStringNoCopy(cString);
+ if (!symbol) {
+ goto finish;
+ }
+
+ count = array->getCount();
+ for (i = 0; i < count; i++) {
+ OSObject * thisObject = array->getObject(i);
+ if (symbol->isEqualTo(thisObject)) {
+ result = true;
+ goto finish;
+ }
+ }
+
+finish:
+ if (symbol) symbol->release();
+ return result;
+}
+
+/*********************************************************************
+ * We really only care about boot / system start up related kexts.
+ * We return true if we're less than REBUILD_MAX_TIME since start up,
+ * otherwise return false.
+ *********************************************************************/
+bool _OSKextInPrelinkRebuildWindow(void)
+{
+ static bool outside_the_window = false;
+ AbsoluteTime my_abstime;
+ UInt64 my_ns;
+ SInt32 my_secs;
+
+ if (outside_the_window) {
+ return(false);
+ }
+ clock_get_uptime(&my_abstime);
+ absolutetime_to_nanoseconds(my_abstime, &my_ns);
+ my_secs = (SInt32)(my_ns / NSEC_PER_SEC);
+ if (my_secs > REBUILD_MAX_TIME) {
+ outside_the_window = true;
+ return(false);
+ }
+ return(true);
+}
+
+/*********************************************************************
+ *********************************************************************/
+bool _OSKextInUnloadedPrelinkedKexts( const OSSymbol * theBundleID )
+{
+ int unLoadedCount, i;
+ bool result = false;
+
+ IORecursiveLockLock(sKextLock);
+
+ if (sUnloadedPrelinkedKexts == NULL) {
+ goto finish;
+ }
+ unLoadedCount = sUnloadedPrelinkedKexts->getCount();
+ if (unLoadedCount == 0) {
+ goto finish;
+ }
+
+ for (i = 0; i < unLoadedCount; i++) {
+ const OSSymbol * myBundleID; // do not release
+
+ myBundleID = OSDynamicCast(OSSymbol, sUnloadedPrelinkedKexts->getObject(i));
+ if (!myBundleID) continue;
+ if (theBundleID->isEqualTo(myBundleID->getCStringNoCopy())) {
+ result = true;
+ break;
+ }
+ }
+finish:
+ IORecursiveLockUnlock(sKextLock);
+ return(result);
+}
+
#if PRAGMA_MARK
#pragma mark Personalities (IOKit Drivers)
#endif
return result;
}
-/*********************************************************************
-*********************************************************************/
-/* static */
-void
-OSKext::setPrelinkedPersonalities(OSArray * personalitiesArray)
-{
- sPrelinkedPersonalities = personalitiesArray;
- if (sPrelinkedPersonalities) {
- sPrelinkedPersonalities->retain();
- gIOCatalogue->addDrivers(sPrelinkedPersonalities);
- }
- return;
-}
-
/*********************************************************************
*********************************************************************/
/* static */
/*********************************************************************
Might want to change this to a bool return?
*********************************************************************/
-void
+OSReturn
OSKext::sendPersonalitiesToCatalog(
bool startMatching,
OSArray * personalityNames)
{
- OSArray * personalitiesToSend = NULL; // must release
- OSDictionary * kextPersonalities = NULL; // do not release
+ OSReturn result = kOSReturnSuccess;
+ OSArray * personalitiesToSend = NULL; // must release
+ OSDictionary * kextPersonalities = NULL; // do not release
int count, i;
+ if (!sLoadEnabled) {
+ OSKextLog(this,
+ kOSKextLogErrorLevel |
+ kOSKextLogLoadFlag,
+ "Kext loading is disabled (attempt to start matching for kext %s).",
+ getIdentifierCString());
+ result = kOSKextReturnDisabled;
+ goto finish;
+ }
+
if (sSafeBoot && !isLoadableInSafeBoot()) {
OSKextLog(this,
kOSKextLogErrorLevel |
"Kext %s is not loadable during safe boot; "
"not sending personalities to the IOCatalogue.",
getIdentifierCString());
- return;
+ result = kOSKextReturnNotLoadable;
+ goto finish;
}
if (!personalityNames || !personalityNames->getCount()) {
kextPersonalities = OSDynamicCast(OSDictionary,
getPropertyForHostArch(kIOKitPersonalitiesKey));
if (!kextPersonalities || !kextPersonalities->getCount()) {
+ // not an error
goto finish;
}
personalitiesToSend = OSArray::withCapacity(0);
if (!personalitiesToSend) {
+ result = kOSKextReturnNoMemory;
goto finish;
}
count = personalityNames->getCount();
if (personalitiesToSend) {
personalitiesToSend->release();
}
- return;
+ return result;
}
/*********************************************************************
+* xxx - We should allow removing the kext's declared personalities,
+* xxx - even with other bundle identifiers.
*********************************************************************/
void
OSKext::removePersonalitiesFromCatalog(void)
/* static */
OSKextLogSpec
OSKext::setUserSpaceLogFilter(
- OSKextLogSpec userLogFilter,
+ OSKextLogSpec newUserLogFilter,
bool captureFlag)
{
OSKextLogSpec result;
+ bool allocError = false;
- IORecursiveLockLock(sKextInnerLock);
+ /* Do not call any function that takes sKextLoggingLock during
+ * this critical block. That means do logging after.
+ */
+ IOLockLock(sKextLoggingLock);
result = sUserSpaceKextLogFilter;
- sUserSpaceKextLogFilter = userLogFilter;
-
- /* If the config flag itself is changing, log the state change
- * going both ways, before setting up the user-space log arrays,
- * so that this is only logged in the kernel.
- */
- if (sUserSpaceKextLogFilter != result) {
- OSKextLog(/* kext */ NULL,
- kOSKextLogDebugLevel |
- kOSKextLogGeneralFlag,
- "User-space log flags changed from 0x%x to 0x%x.",
- result, sUserSpaceKextLogFilter);
- }
+ sUserSpaceKextLogFilter = newUserLogFilter;
- if (userLogFilter && captureFlag &&
+ if (newUserLogFilter && captureFlag &&
!sUserSpaceLogSpecArray && !sUserSpaceLogMessageArray) {
// xxx - do some measurements for a good initial capacity?
sUserSpaceLogMessageArray = OSArray::withCapacity(0);
if (!sUserSpaceLogSpecArray || !sUserSpaceLogMessageArray) {
- OSKextLog(/* kext */ NULL,
- kOSKextLogErrorLevel |
- kOSKextLogGeneralFlag,
- "Failed to allocate user-space log message arrays.");
OSSafeReleaseNULL(sUserSpaceLogSpecArray);
OSSafeReleaseNULL(sUserSpaceLogMessageArray);
+ allocError = true;
}
}
- IORecursiveLockUnlock(sKextInnerLock);
+ IOLockUnlock(sKextLoggingLock);
+
+ /* If the config flag itself is changing, log the state change
+ * going both ways, before setting up the user-space log arrays,
+ * so that this is only logged in the kernel.
+ */
+ if (result != newUserLogFilter) {
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogDebugLevel |
+ kOSKextLogGeneralFlag,
+ "User-space log flags changed from 0x%x to 0x%x.",
+ result, newUserLogFilter);
+ }
+ if (allocError) {
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogErrorLevel |
+ kOSKextLogGeneralFlag,
+ "Failed to allocate user-space log message arrays.");
+ }
return result;
}
OSArray *
OSKext::clearUserSpaceLogFilter(void)
{
- OSArray * result = NULL;
+ OSArray * result = NULL;
OSKextLogSpec oldLogFilter;
+ OSKextLogSpec newLogFilter = kOSKextLogSilentFilter;
- IORecursiveLockLock(sKextInnerLock);
+ /* Do not call any function that takes sKextLoggingLock during
+ * this critical block. That means do logging after.
+ */
+ IOLockLock(sKextLoggingLock);
result = OSArray::withCapacity(2);
if (result) {
OSSafeReleaseNULL(sUserSpaceLogMessageArray);
oldLogFilter = sUserSpaceKextLogFilter;
- sUserSpaceKextLogFilter = kOSKextLogSilentFilter;
+ sUserSpaceKextLogFilter = newLogFilter;
+
+ IOLockUnlock(sKextLoggingLock);
/* If the config flag itself is changing, log the state change
* going both ways, after tearing down the user-space log
* arrays, so this is only logged within the kernel.
*/
- if (oldLogFilter != sUserSpaceKextLogFilter) {
+ if (oldLogFilter != newLogFilter) {
OSKextLog(/* kext */ NULL,
kOSKextLogDebugLevel |
kOSKextLogGeneralFlag,
"User-space log flags changed from 0x%x to 0x%x.",
- oldLogFilter, sUserSpaceKextLogFilter);
+ oldLogFilter, newLogFilter);
}
- IORecursiveLockUnlock(sKextInnerLock);
-
return result;
}
+
/*********************************************************************
* Do not call any function that takes sKextLock here!
*********************************************************************/
{
OSKextLogSpec result;
- IORecursiveLockLock(sKextInnerLock);
+ IOLockLock(sKextLoggingLock);
result = sUserSpaceKextLogFilter;
- IORecursiveLockUnlock(sKextInnerLock);
+ IOLockUnlock(sKextLoggingLock);
return result;
}
switch (logLevel) {
case kOSKextLogErrorLevel:
return VTRED VTBOLD;
- break;
case kOSKextLogWarningLevel:
return VTRED;
- break;
case kOSKextLogBasicLevel:
return VTYELLOW VTUNDER;
- break;
case kOSKextLogProgressLevel:
return VTYELLOW;
- break;
case kOSKextLogStepLevel:
return VTGREEN;
- break;
case kOSKextLogDetailLevel:
return VTCYAN;
- break;
case kOSKextLogDebugLevel:
return VTMAGENTA;
- break;
default:
return ""; // white
- break;
}
- return "";
}
inline bool logSpecMatch(
OSKext * aKext,
OSKextLogSpec msgLogSpec,
const char * format,
- va_list srcArgList)
+ va_list srcArgList)
{
extern int disableConsoleOutput;
OSString * logString = NULL; // must release
char * buffer = stackBuffer; // do not free
- IORecursiveLockLock(sKextInnerLock);
+ IOLockLock(sKextLoggingLock);
/* Set the kext/global bit in the message spec if we have no
* kext or if the kext requests logging.
va_end(argList);
if (length + 1 >= sizeof(stackBuffer)) {
- allocBuffer = (char *)kalloc((length + 1) * sizeof(char));
+ allocBuffer = (char *)kalloc_tag((length + 1) * sizeof(char), VM_KERN_MEMORY_OSKEXT);
if (!allocBuffer) {
goto finish;
}
}
finish:
+ IOLockUnlock(sKextLoggingLock);
+
if (allocBuffer) {
kfree(allocBuffer, (length + 1) * sizeof(char));
}
- OSSafeRelease(logString);
- OSSafeRelease(logSpecNum);
- IORecursiveLockUnlock(sKextInnerLock);
+ OSSafeReleaseNULL(logString);
+ OSSafeReleaseNULL(logSpecNum);
return;
}
+#if KASLR_IOREG_DEBUG
+
+#define IOLOG_INDENT( the_indention ) \
+{ \
+ int i; \
+ for ( i = 0; i < (the_indention); i++ ) { \
+ IOLog(" "); \
+ } \
+}
+
+extern vm_offset_t vm_kernel_stext;
+extern vm_offset_t vm_kernel_etext;
+extern mach_vm_offset_t kext_alloc_base;
+extern mach_vm_offset_t kext_alloc_max;
+
+bool ScanForAddrInObject(OSObject * theObject,
+ int indent );
+
+bool ScanForAddrInObject(OSObject * theObject,
+ int indent)
+{
+ const OSMetaClass * myTypeID;
+ OSCollectionIterator * myIter;
+ OSSymbol * myKey;
+ OSObject * myValue;
+ bool myResult = false;
+
+ if ( theObject == NULL ) {
+ IOLog("%s: theObject is NULL \n",
+ __FUNCTION__);
+ return myResult;
+ }
+
+ myTypeID = OSTypeIDInst(theObject);
+
+ if ( myTypeID == OSTypeID(OSDictionary) ) {
+ OSDictionary * myDictionary;
+
+ myDictionary = OSDynamicCast(OSDictionary, theObject);
+ myIter = OSCollectionIterator::withCollection( myDictionary );
+ if ( myIter == NULL )
+ return myResult;
+ myIter->reset();
+
+ while ( (myKey = OSDynamicCast(OSSymbol, myIter->getNextObject())) ) {
+ bool myTempResult;
+
+ myValue = myDictionary->getObject(myKey);
+ myTempResult = ScanForAddrInObject(myValue, (indent + 4));
+ if (myTempResult) {
+ // if we ever get a true result return true
+ myResult = true;
+ IOLOG_INDENT(indent);
+ IOLog("OSDictionary key \"%s\" \n", myKey->getCStringNoCopy());
+ }
+ }
+ myIter->release();
+ }
+ else if ( myTypeID == OSTypeID(OSArray) ) {
+ OSArray * myArray;
+
+ myArray = OSDynamicCast(OSArray, theObject);
+ myIter = OSCollectionIterator::withCollection(myArray);
+ if ( myIter == NULL )
+ return myResult;
+ myIter->reset();
+
+ while ( (myValue = myIter->getNextObject()) ) {
+ bool myTempResult;
+ myTempResult = ScanForAddrInObject(myValue, (indent + 4));
+ if (myTempResult) {
+ // if we ever get a true result return true
+ myResult = true;
+ IOLOG_INDENT(indent);
+ IOLog("OSArray: \n");
+ }
+ }
+ myIter->release();
+ }
+ else if ( myTypeID == OSTypeID(OSString) || myTypeID == OSTypeID(OSSymbol) ) {
+
+ // should we look for addresses in strings?
+ }
+ else if ( myTypeID == OSTypeID(OSData) ) {
+
+ void * * myPtrPtr;
+ unsigned int myLen;
+ OSData * myDataObj;
+
+ myDataObj = OSDynamicCast(OSData, theObject);
+ myPtrPtr = (void * *) myDataObj->getBytesNoCopy();
+ myLen = myDataObj->getLength();
+
+ if (myPtrPtr && myLen && myLen > 7) {
+ int i;
+ int myPtrCount = (myLen / sizeof(void *));
+
+ for (i = 0; i < myPtrCount; i++) {
+ UInt64 numberValue = (UInt64) *(myPtrPtr);
+
+ if ( kext_alloc_max != 0 &&
+ numberValue >= kext_alloc_base &&
+ numberValue < kext_alloc_max ) {
+
+ OSKext * myKext = NULL; // must release (looked up)
+ // IOLog("found OSData %p in kext map %p to %p \n",
+ // *(myPtrPtr),
+ // (void *) kext_alloc_base,
+ // (void *) kext_alloc_max);
+
+ myKext = OSKext::lookupKextWithAddress( (vm_address_t) *(myPtrPtr) );
+ if (myKext) {
+ IOLog("found addr %p from an OSData obj within kext \"%s\" \n",
+ *(myPtrPtr),
+ myKext->getIdentifierCString());
+ myKext->release();
+ }
+ myResult = true;
+ }
+ if ( vm_kernel_etext != 0 &&
+ numberValue >= vm_kernel_stext &&
+ numberValue < vm_kernel_etext ) {
+ IOLog("found addr %p from an OSData obj within kernel text segment %p to %p \n",
+ *(myPtrPtr),
+ (void *) vm_kernel_stext,
+ (void *) vm_kernel_etext);
+ myResult = true;
+ }
+ myPtrPtr++;
+ }
+ }
+ }
+ else if ( myTypeID == OSTypeID(OSBoolean) ) {
+
+ // do nothing here...
+ }
+ else if ( myTypeID == OSTypeID(OSNumber) ) {
+
+ OSNumber * number = OSDynamicCast(OSNumber, theObject);
+
+ UInt64 numberValue = number->unsigned64BitValue();
+
+ if ( kext_alloc_max != 0 &&
+ numberValue >= kext_alloc_base &&
+ numberValue < kext_alloc_max ) {
+
+ OSKext * myKext = NULL; // must release (looked up)
+ IOLog("found OSNumber in kext map %p to %p \n",
+ (void *) kext_alloc_base,
+ (void *) kext_alloc_max);
+ IOLog("OSNumber 0x%08llx (%llu) \n", numberValue, numberValue);
+
+ myKext = OSKext::lookupKextWithAddress( (vm_address_t) numberValue );
+ if (myKext) {
+ IOLog("found in kext \"%s\" \n",
+ myKext->getIdentifierCString());
+ myKext->release();
+ }
+
+ myResult = true;
+ }
+ if ( vm_kernel_etext != 0 &&
+ numberValue >= vm_kernel_stext &&
+ numberValue < vm_kernel_etext ) {
+ IOLog("found OSNumber in kernel text segment %p to %p \n",
+ (void *) vm_kernel_stext,
+ (void *) vm_kernel_etext);
+ IOLog("OSNumber 0x%08llx (%llu) \n", numberValue, numberValue);
+ myResult = true;
+ }
+ }
+#if 0
+ else {
+ const OSMetaClass* myMetaClass = NULL;
+
+ myMetaClass = theObject->getMetaClass();
+ if ( myMetaClass ) {
+ IOLog("class %s \n", myMetaClass->getClassName() );
+ }
+ else {
+ IOLog("Unknown object \n" );
+ }
+ }
+#endif
+
+ return myResult;
+}
+#endif // KASLR_KEXT_DEBUG
+
}; /* extern "C" */
#if PRAGMA_MARK
#pragma mark Backtrace Dump & kmod_get_info() support
#endif
/*********************************************************************
+* This function must be safe to call in panic context.
*********************************************************************/
/* static */
void
vm_offset_t * addr,
unsigned int cnt,
int (* printf_func)(const char *fmt, ...),
- bool lockFlag)
+ uint32_t flags)
{
- vm_offset_t * kscan_addr = NULL;
- kmod_info_t * k = NULL;
- kmod_reference_t * r = NULL;
- unsigned int i;
- int found_kmod = 0;
+ addr64_t summary_page = 0;
+ addr64_t last_summary_page = 0;
+ bool found_kmod = false;
+ u_int i = 0;
- if (lockFlag) {
- IORecursiveLockLock(sKextLock);
+ if (kPrintKextsLock & flags) {
+ if (!sKextSummariesLock) return;
+ IOLockLock(sKextSummariesLock);
}
- for (k = kmod; k; k = k->next) {
- if (pmap_find_phys(kernel_pmap, (addr64_t)((uintptr_t)k)) == 0) {
- (*printf_func)(" kmod scan stopped due to missing "
- "kmod page: %p\n", k);
- break;
+ if (!gLoadedKextSummaries) {
+ (*printf_func)(" can't perform kext scan: no kext summary");
+ goto finish;
+ }
+
+ summary_page = trunc_page((addr64_t)(uintptr_t)gLoadedKextSummaries);
+ last_summary_page = round_page(summary_page + sLoadedKextSummariesAllocSize);
+ for (; summary_page < last_summary_page; summary_page += PAGE_SIZE) {
+ if (pmap_find_phys(kernel_pmap, summary_page) == 0) {
+ (*printf_func)(" can't perform kext scan: "
+ "missing kext summary page %p", summary_page);
+ goto finish;
+ }
+ }
+
+ for (i = 0; i < gLoadedKextSummaries->numSummaries; ++i) {
+ OSKextLoadedKextSummary * summary;
+
+ summary = gLoadedKextSummaries->summaries + i;
+ if (!summary->address) {
+ continue;
+ }
+
+ if (!summaryIsInBacktrace(summary, addr, cnt)) {
+ continue;
}
- if (!k->address) {
- continue; // skip fake entries for built-in kernel components
+
+ if (!found_kmod) {
+ if (!(kPrintKextsTerse & flags)) {
+ (*printf_func)(" Kernel Extensions in backtrace:\n");
+ }
+ found_kmod = true;
}
- for (i = 0, kscan_addr = addr; i < cnt; i++, kscan_addr++) {
- if ((*kscan_addr >= k->address) &&
- (*kscan_addr < (k->address + k->size))) {
- if (!found_kmod) {
- (*printf_func)(" Kernel Extensions in backtrace "
- "(with dependencies):\n");
- }
- found_kmod = 1;
- (*printf_func)(" %s(%s)@%p->%p\n",
- k->name, k->version, k->address, k->address + k->size - 1);
+ printSummary(summary, printf_func, flags);
+ }
- for (r = k->reference_list; r; r = r->next) {
- kmod_info_t * rinfo;
+finish:
+ if (kPrintKextsLock & flags) {
+ IOLockUnlock(sKextSummariesLock);
+ }
- if (pmap_find_phys(kernel_pmap, (addr64_t)((uintptr_t)r)) == 0) {
- (*printf_func)(" kmod dependency scan stopped "
- "due to missing dependency page: %p\n", r);
- break;
- }
+ return;
+}
- rinfo = r->info;
+/*********************************************************************
+* This function must be safe to call in panic context.
+*********************************************************************/
+/* static */
+boolean_t
+OSKext::summaryIsInBacktrace(
+ OSKextLoadedKextSummary * summary,
+ vm_offset_t * addr,
+ unsigned int cnt)
+{
+ u_int i = 0;
+
+ for (i = 0; i < cnt; i++) {
+ vm_offset_t kscan_addr = addr[i];
+ if ((kscan_addr >= summary->address) &&
+ (kscan_addr < (summary->address + summary->size)))
+ {
+ return TRUE;
+ }
+ }
- if (pmap_find_phys(kernel_pmap, (addr64_t)((uintptr_t)rinfo)) == 0) {
- (*printf_func)(" kmod dependency scan stopped "
- "due to missing kmod page: %p\n", rinfo);
- break;
- }
+ return FALSE;
+}
- if (!rinfo->address) {
- continue; // skip fake entries for built-ins
- }
+/*
+ * Get the kext summary object for the kext where 'addr' lies. Must be called with
+ * sKextSummariesLock held.
+ */
+OSKextLoadedKextSummary *
+OSKext::summaryForAddress(const uintptr_t addr)
+{
+ for (unsigned i = 0; i < gLoadedKextSummaries->numSummaries; ++i) {
+
+ OSKextLoadedKextSummary *summary = &gLoadedKextSummaries->summaries[i];
+ if (!summary->address) {
+ continue;
+ }
+
+#if VM_MAPPED_KEXTS
+ /* On our platforms that use VM_MAPPED_KEXTS, we currently do not
+ * support split kexts, but we also may unmap the kexts, which can
+ * race with the above codepath (see OSKext::unload). As such,
+ * use a simple range lookup if we are using VM_MAPPED_KEXTS.
+ */
+ if ((addr >= summary->address) && (addr < (summary->address + summary->size))) {
+ return summary;
+ }
+#else
+ kernel_mach_header_t *mh = (kernel_mach_header_t *)summary->address;
+ kernel_segment_command_t *seg;
+
+ for (seg = firstsegfromheader(mh); seg != NULL; seg = nextsegfromheader(mh, seg)) {
+ if ((addr >= seg->vmaddr) && (addr < (seg->vmaddr + seg->vmsize))) {
+ return summary;
+ }
+ }
+#endif
+ }
- (*printf_func)(" dependency: %s(%s)@%p\n",
- rinfo->name, rinfo->version, rinfo->address);
- }
+ /* addr did not map to any kext */
+ return NULL;
+}
- break; // only report this kmod for one backtrace address
- }
+/* static */
+void *
+OSKext::kextForAddress(const void *addr)
+{
+ void *image = NULL;
+
+ if (((vm_offset_t)(uintptr_t)addr >= vm_kernel_stext) &&
+ ((vm_offset_t)(uintptr_t)addr < vm_kernel_etext)) {
+ return (void *)&_mh_execute_header;
+ }
+
+ if (!sKextSummariesLock) {
+ return NULL;
+ }
+ IOLockLock(sKextSummariesLock);
+ OSKextLoadedKextSummary *summary = OSKext::summaryForAddress((uintptr_t)addr);
+ if (summary) {
+ image = (void *)summary->address;
+ }
+ IOLockUnlock(sKextSummariesLock);
+
+ return image;
+}
+
+/*********************************************************************
+ * scan list of loaded kext summaries looking for a load address match and if
+ * found return the UUID C string. If not found then set empty string.
+ *********************************************************************/
+static void findSummaryUUID(
+ uint32_t tag_ID,
+ uuid_string_t uuid);
+
+static void findSummaryUUID(
+ uint32_t tag_ID,
+ uuid_string_t uuid)
+{
+ u_int i;
+
+ uuid[0] = 0x00; // default to no UUID
+
+ for (i = 0; i < gLoadedKextSummaries->numSummaries; ++i) {
+ OSKextLoadedKextSummary * summary;
+
+ summary = gLoadedKextSummaries->summaries + i;
+
+ if (summary->loadTag == tag_ID) {
+ (void) uuid_unparse(summary->uuid, uuid);
+ break;
}
}
-
- if (lockFlag) {
- IORecursiveLockUnlock(sKextLock);
+ return;
+}
+
+/*********************************************************************
+* This function must be safe to call in panic context.
+*********************************************************************/
+void OSKext::printSummary(
+ OSKextLoadedKextSummary * summary,
+ int (* printf_func)(const char *fmt, ...),
+ uint32_t flags)
+{
+ kmod_reference_t * kmod_ref = NULL;
+ uuid_string_t uuid;
+ char version[kOSKextVersionMaxLength];
+ uint64_t tmpAddr;
+
+ if (!OSKextVersionGetString(summary->version, version, sizeof(version))) {
+ strlcpy(version, "unknown version", sizeof(version));
+ }
+ (void) uuid_unparse(summary->uuid, uuid);
+
+ if (kPrintKextsUnslide & flags) {
+ tmpAddr = VM_KERNEL_UNSLIDE(summary->address);
+ }
+ else {
+ tmpAddr = summary->address;
}
+ (*printf_func)("%s%s(%s)[%s]@0x%llx->0x%llx\n",
+ (kPrintKextsTerse & flags) ? "" : " ",
+ summary->name, version, uuid,
+ tmpAddr, tmpAddr + summary->size - 1);
+ if (kPrintKextsTerse & flags) return;
+
+ /* print dependency info */
+ for (kmod_ref = (kmod_reference_t *) summary->reference_list;
+ kmod_ref;
+ kmod_ref = kmod_ref->next) {
+ kmod_info_t * rinfo;
+
+ if (pmap_find_phys(kernel_pmap, (addr64_t)((uintptr_t)kmod_ref)) == 0) {
+ (*printf_func)(" kmod dependency scan stopped "
+ "due to missing dependency page: %p\n",
+ (kPrintKextsUnslide & flags) ? (void *)VM_KERNEL_UNSLIDE(kmod_ref) : kmod_ref);
+ break;
+ }
+ rinfo = kmod_ref->info;
+
+ if (pmap_find_phys(kernel_pmap, (addr64_t)((uintptr_t)rinfo)) == 0) {
+ (*printf_func)(" kmod dependency scan stopped "
+ "due to missing kmod page: %p\n",
+ (kPrintKextsUnslide & flags) ? (void *)VM_KERNEL_UNSLIDE(rinfo) : rinfo);
+ break;
+ }
+
+ if (!rinfo->address) {
+ continue; // skip fake entries for built-ins
+ }
+
+ /* locate UUID in gLoadedKextSummaries */
+ findSummaryUUID(rinfo->id, uuid);
+
+ if (kPrintKextsUnslide & flags) {
+ tmpAddr = VM_KERNEL_UNSLIDE(rinfo->address);
+ }
+ else {
+ tmpAddr = rinfo->address;
+ }
+ (*printf_func)(" dependency: %s(%s)[%s]@%p\n",
+ rinfo->name, rinfo->version, uuid, tmpAddr);
+ }
return;
}
+
/*******************************************************************************
* substitute() looks at an input string (a pointer within a larger buffer)
* for a match to a substring, and on match it writes the marker & substitution
/* identPlusVers must be at least 2*KMOD_MAX_NAME in length.
*/
static int assemble_identifier_and_version(
- kmod_info_t * kmod_info,
- char * identPlusVers);
+ kmod_info_t * kmod_info,
+ char * identPlusVers,
+ int bufSize);
+
static int
assemble_identifier_and_version(
- kmod_info_t * kmod_info,
- char * identPlusVers)
+ kmod_info_t * kmod_info,
+ char * identPlusVers,
+ int bufSize)
{
int result = 0;
result = strnlen(identPlusVers, KMOD_MAX_NAME - 1);
identPlusVers[result++] = '\t'; // increment for real char
identPlusVers[result] = '\0'; // don't increment for nul char
- result = strlcat(identPlusVers, kmod_info->version, KMOD_MAX_NAME);
-
+ result = strlcat(identPlusVers, kmod_info->version, bufSize);
+ if (result >= bufSize) {
+ identPlusVers[bufSize - 1] = '\0';
+ result = bufSize - 1;
+ }
+
return result;
}
/*******************************************************************************
+* Assumes sKextLock is held.
*******************************************************************************/
-#define LAST_LOADED " - last loaded "
-#define LAST_LOADED_TS_WIDTH (16)
-
/* static */
-uint32_t
+int
OSKext::saveLoadedKextPanicListTyped(
const char * prefix,
int invertFlag,
int libsFlag,
char * paniclist,
- uint32_t list_size,
- uint32_t * list_length_ptr)
+ uint32_t list_size)
{
- uint32_t result = 0;
- int error = 0;
- unsigned int count, i;
+ int result = -1;
+ unsigned int count, i;
count = sLoadedKexts->getCount();
if (!count) {
i = count - 1;
do {
- OSKext * theKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i));
- kmod_info_t * kmod_info = theKext->kmod_info;
+ OSObject * rawKext = sLoadedKexts->getObject(i);
+ OSKext * theKext = OSDynamicCast(OSKext, rawKext);
int match;
- char identPlusVers[2*KMOD_MAX_NAME];
uint32_t identPlusVersLength;
- char timestampBuffer[17]; // enough for a uint64_t
+ uint32_t tempLen;
+ char identPlusVers[2*KMOD_MAX_NAME];
+
+ if (!rawKext) {
+ printf("OSKext::saveLoadedKextPanicListTyped - "
+ "NULL kext in loaded kext list; continuing\n");
+ continue;
+ }
+
+ if (!theKext) {
+ printf("OSKext::saveLoadedKextPanicListTyped - "
+ "Kext type cast failed in loaded kext list; continuing\n");
+ continue;
+ }
/* Skip all built-in kexts.
*/
continue;
}
+ kmod_info_t * kmod_info = theKext->kmod_info;
+
/* Filter for kmod name (bundle identifier).
*/
match = !strncmp(kmod_info->name, prefix, strnlen(prefix, KMOD_MAX_NAME));
!pmap_find_phys(kernel_pmap, (addr64_t)((uintptr_t)kmod_info))) {
printf("kext scan stopped due to missing kmod_info page: %p\n",
- kmod_info);
- error = 1;
+ kmod_info);
goto finish;
}
identPlusVersLength = assemble_identifier_and_version(kmod_info,
- identPlusVers);
+ identPlusVers,
+ sizeof(identPlusVers));
if (!identPlusVersLength) {
printf("error saving loaded kext info\n");
goto finish;
}
- /* We're going to note the last-loaded kext in the list.
- */
- if (i + 1 == count) {
- snprintf(timestampBuffer, sizeof(timestampBuffer), "%llu",
- AbsoluteTime_to_scalar(&last_loaded_timestamp));
- identPlusVersLength += sizeof(LAST_LOADED) - 1 +
- strnlen(timestampBuffer, sizeof(timestampBuffer));
- }
-
- /* Adding 1 for the newline.
- */
- if (*list_length_ptr + identPlusVersLength + 1 >= list_size) {
+ /* make sure everything fits and we null terminate.
+ */
+ tempLen = strlcat(paniclist, identPlusVers, list_size);
+ if (tempLen >= list_size) {
+ // panic list is full, keep it and null terminate
+ paniclist[list_size - 1] = 0x00;
+ result = 0;
goto finish;
}
-
- *list_length_ptr = strlcat(paniclist, identPlusVers, list_size);
- if (i + 1 == count) {
- *list_length_ptr = strlcat(paniclist, LAST_LOADED, list_size);
- *list_length_ptr = strlcat(paniclist, timestampBuffer, list_size);
+ tempLen = strlcat(paniclist, "\n", list_size);
+ if (tempLen >= list_size) {
+ // panic list is full, keep it and null terminate
+ paniclist[list_size - 1] = 0x00;
+ result = 0;
+ goto finish;
}
- *list_length_ptr = strlcat(paniclist, "\n", list_size);
-
} while (i--);
+ result = 0;
finish:
- if (!error) {
- if (*list_length_ptr + 1 <= list_size) {
- result = list_size - (*list_length_ptr + 1);
- }
- }
-
+
return result;
}
{
char * newlist = NULL;
uint32_t newlist_size = 0;
- uint32_t newlist_length = 0;
-
- IORecursiveLockLock(sKextLock);
-
- newlist_length = 0;
+
newlist_size = KEXT_PANICLIST_SIZE;
- newlist = (char *)kalloc(newlist_size);
+ newlist = (char *)kalloc_tag(newlist_size, VM_KERN_MEMORY_OSKEXT);
if (!newlist) {
OSKextLog(/* kext */ NULL,
- kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
- "Couldn't allocate kext panic log buffer.");
+ kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
+ "Couldn't allocate kext panic log buffer.");
goto finish;
}
newlist[0] = '\0';
-
+
// non-"com.apple." kexts
- if (!OSKext::saveLoadedKextPanicListTyped("com.apple.", /* invert? */ 1,
- /* libs? */ -1, newlist, newlist_size, &newlist_length)) {
+ if (OSKext::saveLoadedKextPanicListTyped("com.apple.", /* invert? */ 1,
+ /* libs? */ -1, newlist, newlist_size) != 0) {
goto finish;
}
// "com.apple." nonlibrary kexts
- if (!OSKext::saveLoadedKextPanicListTyped("com.apple.", /* invert? */ 0,
- /* libs? */ 0, newlist, newlist_size, &newlist_length)) {
+ if (OSKext::saveLoadedKextPanicListTyped("com.apple.", /* invert? */ 0,
+ /* libs? */ 0, newlist, newlist_size) != 0) {
goto finish;
}
// "com.apple." library kexts
- if (!OSKext::saveLoadedKextPanicListTyped("com.apple.", /* invert? */ 0,
- /* libs? */ 1, newlist, newlist_size, &newlist_length)) {
+ if (OSKext::saveLoadedKextPanicListTyped("com.apple.", /* invert? */ 0,
+ /* libs? */ 1, newlist, newlist_size) != 0) {
goto finish;
}
-
+
if (loaded_kext_paniclist) {
kfree(loaded_kext_paniclist, loaded_kext_paniclist_size);
}
loaded_kext_paniclist = newlist;
+ newlist = NULL;
loaded_kext_paniclist_size = newlist_size;
- loaded_kext_paniclist_length = newlist_length;
-
+
finish:
- IORecursiveLockUnlock(sKextLock);
+ if (newlist) {
+ kfree(newlist, newlist_size);
+ }
return;
}
-
+
/*********************************************************************
+* Assumes sKextLock is held.
*********************************************************************/
-/* static */
void
-OSKext::saveUnloadedKextPanicList(OSKext * aKext)
+OSKext::savePanicString(bool isLoading)
{
- char * newlist = NULL;
- uint32_t newlist_size = 0;
- uint32_t newlist_length = 0;
- char identPlusVers[2*KMOD_MAX_NAME];
- uint32_t identPlusVersLength;
+ u_long len;
- if (!aKext->kmod_info) {
+ if (!kmod_info) {
return; // do not goto finish here b/c of lock
}
- IORecursiveLockLock(sKextLock);
-
- clock_get_uptime(&last_unloaded_timestamp);
- last_unloaded_address = (void *)aKext->kmod_info->address;
- last_unloaded_size = aKext->kmod_info->size;
-
-
- identPlusVersLength = assemble_identifier_and_version(aKext->kmod_info,
- identPlusVers);
- if (!identPlusVersLength) {
+ len = assemble_identifier_and_version( kmod_info,
+ (isLoading) ? last_loaded_str_buf : last_unloaded_str_buf,
+ (isLoading) ? sizeof(last_loaded_str_buf) : sizeof(last_unloaded_str_buf) );
+ if (!len) {
printf("error saving unloaded kext info\n");
goto finish;
}
- newlist_length = identPlusVersLength;
- newlist_size = newlist_length + 1;
- newlist = (char *)kalloc(newlist_size);
-
- if (!newlist) {
- printf("couldn't allocate kext panic log buffer\n");
- goto finish;
- }
-
- newlist[0] = '\0';
-
- strlcpy(newlist, identPlusVers, newlist_size);
-
- if (unloaded_kext_paniclist) {
- kfree(unloaded_kext_paniclist, unloaded_kext_paniclist_size);
+ if (isLoading) {
+ last_loaded_strlen = len;
+ last_loaded_address = (void *)kmod_info->address;
+ last_loaded_size = kmod_info->size;
+ clock_get_uptime(&last_loaded_timestamp);
+ } else {
+ last_unloaded_strlen = len;
+ last_unloaded_address = (void *)kmod_info->address;
+ last_unloaded_size = kmod_info->size;
+ clock_get_uptime(&last_unloaded_timestamp);
}
- unloaded_kext_paniclist = newlist;
- unloaded_kext_paniclist_size = newlist_size;
- unloaded_kext_paniclist_length = newlist_length;
finish:
- IORecursiveLockUnlock(sKextLock);
return;
}
/*********************************************************************
*********************************************************************/
-#if __LP64__
-#define __kLoadSizeEscape "0x%lld"
-#else
-#define __kLoadSizeEscape "0x%ld"
-#endif /* __LP64__ */
-
/* static */
void
OSKext::printKextPanicLists(int (*printf_func)(const char *fmt, ...))
{
- printf_func("unloaded kexts:\n");
- if (unloaded_kext_paniclist &&
- pmap_find_phys(kernel_pmap, (addr64_t) (uintptr_t) unloaded_kext_paniclist) &&
- unloaded_kext_paniclist[0]) {
+ if (last_loaded_strlen) {
+ printf_func("last loaded kext at %llu: %.*s (addr %p, size %lu)\n",
+ AbsoluteTime_to_scalar(&last_loaded_timestamp),
+ last_loaded_strlen, last_loaded_str_buf,
+ last_loaded_address, last_loaded_size);
+ }
- printf_func(
- "%.*s (addr %p, size " __kLoadSizeEscape ") - last unloaded %llu\n",
- unloaded_kext_paniclist_length, unloaded_kext_paniclist,
- last_unloaded_address, last_unloaded_size,
- AbsoluteTime_to_scalar(&last_unloaded_timestamp));
- } else {
- printf_func("(none)\n");
+ if (last_unloaded_strlen) {
+ printf_func("last unloaded kext at %llu: %.*s (addr %p, size %lu)\n",
+ AbsoluteTime_to_scalar(&last_unloaded_timestamp),
+ last_unloaded_strlen, last_unloaded_str_buf,
+ last_unloaded_address, last_unloaded_size);
}
+
printf_func("loaded kexts:\n");
if (loaded_kext_paniclist &&
pmap_find_phys(kernel_pmap, (addr64_t) (uintptr_t) loaded_kext_paniclist) &&
loaded_kext_paniclist[0]) {
- printf_func("%.*s", loaded_kext_paniclist_length, loaded_kext_paniclist);
+ printf_func("%.*s",
+ strnlen(loaded_kext_paniclist, loaded_kext_paniclist_size),
+ loaded_kext_paniclist);
} else {
printf_func("(none)\n");
}
}
/*********************************************************************
+* Assumes sKextLock is held.
*********************************************************************/
-#if __ppc__ || __i386__
/* static */
-kern_return_t
-OSKext::getKmodInfo(
- kmod_info_array_t * kmodList,
- mach_msg_type_number_t * kmodCount)
+void
+OSKext::updateLoadedKextSummaries(void)
{
kern_return_t result = KERN_FAILURE;
- vm_offset_t data;
- kmod_info_t * k, * kmod_info_scan_ptr;
- kmod_reference_t * r, * ref_scan_ptr;
- int ref_count;
- unsigned size = 0;
-
- *kmodList = (kmod_info_t *)0;
- *kmodCount = 0;
-
- IORecursiveLockLock(sKextLock);
+ OSKextLoadedKextSummaryHeader *summaryHeader = NULL;
+ OSKextLoadedKextSummaryHeader *summaryHeaderAlloc = NULL;
+ OSKext *aKext;
+ vm_map_offset_t start, end;
+ size_t summarySize = 0;
+ size_t size;
+ u_int count;
+ u_int maxKexts;
+ u_int i, j;
+ OSKextActiveAccount * accountingList;
+ OSKextActiveAccount * prevAccountingList;
+ uint32_t idx, accountingListAlloc, accountingListCount, prevAccountingListCount;
+
+ prevAccountingList = NULL;
+ prevAccountingListCount = 0;
- k = kmod;
- while (k) {
- size += sizeof(kmod_info_t);
- r = k->reference_list;
- while (r) {
- size +=sizeof(kmod_reference_t);
- r = r->next;
- }
- k = k->next;
+#if DEVELOPMENT || DEBUG
+ if (IORecursiveLockHaveLock(sKextLock) == false) {
+ panic("sKextLock must be held");
}
- if (!size) {
- result = KERN_SUCCESS;
- goto finish;
+#endif
+
+ IOLockLock(sKextSummariesLock);
+
+ count = sLoadedKexts->getCount();
+ for (i = 0, maxKexts = 0; i < count; ++i) {
+ aKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i));
+ maxKexts += (aKext && aKext->isExecutable());
}
-
- result = kmem_alloc(kernel_map, &data, size);
- if (result != KERN_SUCCESS) {
- goto finish;
+
+ if (!maxKexts) goto finish;
+ if (maxKexts < kOSKextTypicalLoadCount) maxKexts = kOSKextTypicalLoadCount;
+
+ /* Calculate the size needed for the new summary headers.
+ */
+
+ size = sizeof(*gLoadedKextSummaries);
+ size += maxKexts * sizeof(*gLoadedKextSummaries->summaries);
+ size = round_page(size);
+
+ if (gLoadedKextSummaries == NULL || sLoadedKextSummariesAllocSize < size) {
+ if (gLoadedKextSummaries) {
+ kmem_free(kernel_map, (vm_offset_t)gLoadedKextSummaries, sLoadedKextSummariesAllocSize);
+ gLoadedKextSummaries = NULL;
+ gLoadedKextSummariesTimestamp = mach_absolute_time();
+ sLoadedKextSummariesAllocSize = 0;
+ }
+ result = kmem_alloc(kernel_map, (vm_offset_t *)&summaryHeaderAlloc, size, VM_KERN_MEMORY_OSKEXT);
+ if (result != KERN_SUCCESS) goto finish;
+ summaryHeader = summaryHeaderAlloc;
+ summarySize = size;
+ }
+ else {
+ summaryHeader = gLoadedKextSummaries;
+ summarySize = sLoadedKextSummariesAllocSize;
+
+ start = (vm_map_offset_t) summaryHeader;
+ end = start + summarySize;
+ result = vm_map_protect(kernel_map,
+ start,
+ end,
+ VM_PROT_DEFAULT,
+ FALSE);
+ if (result != KERN_SUCCESS) goto finish;
}
+
+ /* Populate the summary header.
+ */
+
+ bzero(summaryHeader, summarySize);
+ summaryHeader->version = kOSKextLoadedKextSummaryVersion;
+ summaryHeader->entry_size = sizeof(OSKextLoadedKextSummary);
- /* Copy each kmod_info struct sequentially into the data buffer.
- * Set each struct's nonzero 'next' pointer back to itself as a sentinel;
- * the kernel space address is used to match refs, and a zero 'next' flags
- * the end of kmod_infos in the data buffer and the beginning of references.
- */
- k = kmod;
- kmod_info_scan_ptr = (kmod_info_t *)data;
- while (k) {
- *kmod_info_scan_ptr = *k;
- if (k->next) {
- kmod_info_scan_ptr->next = k;
+ /* Populate each kext summary.
+ */
+
+ count = sLoadedKexts->getCount();
+ accountingListAlloc = 0;
+ for (i = 0, j = 0; i < count && j < maxKexts; ++i) {
+ aKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i));
+ if (!aKext || !aKext->isExecutable()) {
+ continue;
}
- kmod_info_scan_ptr++;
- k = k->next;
+
+ aKext->updateLoadedKextSummary(&summaryHeader->summaries[j++]);
+ summaryHeader->numSummaries++;
+ accountingListAlloc++;
}
- /* Now add references after the kmod_info structs in the same buffer.
- * Update each kmod_info with the ref_count so we can associate
- * references with kmod_info structs.
- */
- k = kmod;
- ref_scan_ptr = (kmod_reference_t *)kmod_info_scan_ptr;
- kmod_info_scan_ptr = (kmod_info_t *)data;
- while (k) {
- r = k->reference_list;
- ref_count = 0;
- while (r) {
- /* Note the last kmod_info in the data buffer has its next == 0.
- * Since there can only be one like that,
- * this case is handled by the caller.
- */
- *ref_scan_ptr = *r;
- ref_scan_ptr++;
- r = r->next;
- ref_count++;
+ accountingList = IONew(typeof(accountingList[0]), accountingListAlloc);
+ accountingListCount = 0;
+ for (i = 0, j = 0; i < count && j < maxKexts; ++i) {
+ aKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i));
+ if (!aKext || !aKext->isExecutable()) {
+ continue;
}
- /* Stuff the # of refs into the 'reference_list' field of the kmod_info
- * struct for the client to interpret.
- */
- kmod_info_scan_ptr->reference_list = (kmod_reference_t *)(long)ref_count;
- kmod_info_scan_ptr++;
- k = k->next;
+
+ OSKextActiveAccount activeAccount;
+ aKext->updateActiveAccount(&activeAccount);
+ // order by address
+ for (idx = 0; idx < accountingListCount; idx++)
+ {
+ if (activeAccount.address < accountingList[idx].address) break;
+ }
+ bcopy(&accountingList[idx], &accountingList[idx + 1], (accountingListCount - idx) * sizeof(accountingList[0]));
+ accountingList[idx] = activeAccount;
+ accountingListCount++;
}
+ assert(accountingListCount == accountingListAlloc);
+ /* Write protect the buffer and move it into place.
+ */
- result = vm_map_copyin(kernel_map, data, size, TRUE, (vm_map_copy_t *)kmodList);
- if (result != KERN_SUCCESS) {
+ start = (vm_map_offset_t) summaryHeader;
+ end = start + summarySize;
+
+ result = vm_map_protect(kernel_map, start, end, VM_PROT_READ, FALSE);
+ if (result != KERN_SUCCESS)
goto finish;
- }
- *kmodCount = size;
- result = KERN_SUCCESS;
+ gLoadedKextSummaries = summaryHeader;
+ gLoadedKextSummariesTimestamp = mach_absolute_time();
+ sLoadedKextSummariesAllocSize = summarySize;
+ summaryHeaderAlloc = NULL;
+
+ /* Call the magic breakpoint function through a static function pointer so
+ * the compiler can't optimize the function away.
+ */
+ if (sLoadedKextSummariesUpdated) (*sLoadedKextSummariesUpdated)();
+
+ IOSimpleLockLock(sKextAccountsLock);
+ prevAccountingList = sKextAccounts;
+ prevAccountingListCount = sKextAccountsCount;
+ sKextAccounts = accountingList;
+ sKextAccountsCount = accountingListCount;
+ IOSimpleLockUnlock(sKextAccountsLock);
finish:
- IORecursiveLockUnlock(sKextLock);
+ IOLockUnlock(sKextSummariesLock);
- if (result != KERN_SUCCESS && data) {
- kmem_free(kernel_map, data, size);
- *kmodList = (kmod_info_t *)0;
- *kmodCount = 0;
+ /* If we had to allocate a new buffer but failed to generate the summaries,
+ * free that now.
+ */
+ if (summaryHeaderAlloc) {
+ kmem_free(kernel_map, (vm_offset_t)summaryHeaderAlloc, summarySize);
}
- return result;
+ if (prevAccountingList) {
+ IODelete(prevAccountingList, typeof(accountingList[0]), prevAccountingListCount);
+ }
+
+ return;
}
-#endif /* __ppc__ || __i386__ */
-#if PRAGMA_MARK
-#pragma mark MAC Framework Support
-#endif
+
/*********************************************************************
*********************************************************************/
-#if CONFIG_MACF_KEXT
-/* MAC Framework support */
+void
+OSKext::updateLoadedKextSummary(OSKextLoadedKextSummary *summary)
+{
+ OSData *uuid;
-/*
- * define IOC_DEBUG to display run-time debugging information
- * #define IOC_DEBUG 1
- */
+ strlcpy(summary->name, getIdentifierCString(),
+ sizeof(summary->name));
-#ifdef IOC_DEBUG
-#define DPRINTF(x) printf x
-#else
-#define IOC_DEBUG
-#define DPRINTF(x)
-#endif
+ uuid = copyUUID();
+ if (uuid) {
+ memcpy(summary->uuid, uuid->getBytesNoCopy(), sizeof(summary->uuid));
+ OSSafeReleaseNULL(uuid);
+ }
+
+ summary->address = kmod_info->address;
+ summary->size = kmod_info->size;
+ summary->version = getVersion();
+ summary->loadTag = kmod_info->id;
+ summary->flags = 0;
+ summary->reference_list = (uint64_t) kmod_info->reference_list;
+
+ return;
+}
/*********************************************************************
*********************************************************************/
-static bool
-MACFObjectIsPrimitiveType(OSObject * obj)
+
+void
+OSKext::updateActiveAccount(OSKextActiveAccount *accountp)
{
- const OSMetaClass * typeID = NULL; // do not release
+ kernel_mach_header_t *hdr = NULL;
+ kernel_segment_command_t *seg = NULL;
- typeID = OSTypeIDInst(obj);
- if (typeID == OSTypeID(OSString) || typeID == OSTypeID(OSNumber) ||
- typeID == OSTypeID(OSBoolean) || typeID == OSTypeID(OSData)) {
+ hdr = (kernel_mach_header_t *)kmod_info->address;
- return true;
+ if (getcommandfromheader(hdr, LC_SEGMENT_SPLIT_INFO)) {
+ /* If this kext supports split segments, use the first
+ * executable segment as the range for instructions
+ * (and thus for backtracing.
+ */
+ for (seg = firstsegfromheader(hdr); seg != NULL; seg = nextsegfromheader(hdr, seg)) {
+ if (seg->initprot & VM_PROT_EXECUTE) {
+ break;
+ }
+ }
}
- return false;
-}
-/*********************************************************************
-*********************************************************************/
-static int
-MACFLengthForObject(OSObject * obj)
-{
- const OSMetaClass * typeID = NULL; // do not release
- int len;
-
- typeID = OSTypeIDInst(obj);
- if (typeID == OSTypeID(OSString)) {
- OSString * stringObj = OSDynamicCast(OSString, obj);
- len = stringObj->getLength() + 1;
- } else if (typeID == OSTypeID(OSNumber)) {
- len = sizeof("4294967295"); /* UINT32_MAX */
- } else if (typeID == OSTypeID(OSBoolean)) {
- OSBoolean * boolObj = OSDynamicCast(OSBoolean, obj);
- len = boolObj->isTrue() ? sizeof("true") : sizeof("false");
- } else if (typeID == OSTypeID(OSData)) {
- OSData * dataObj = OSDynamicCast(OSData, obj);
- len = dataObj->getLength();
+ bzero(accountp, sizeof(*accountp));
+ if (seg) {
+ accountp->address = seg->vmaddr;
+ if (accountp->address) {
+ accountp->address_end = seg->vmaddr + seg->vmsize;
+ }
} else {
- len = 0;
+ /* For non-split kexts and for kexts without executable
+ * segments, just use the kmod_info range (as the kext
+ * is either all in one range or should not show up in
+ * instruction backtraces).
+ */
+ accountp->address = kmod_info->address;
+ if (accountp->address) {
+ accountp->address_end = kmod_info->address + kmod_info->size;
+ }
}
- return len;
+ accountp->account = this->account;
}
-/*********************************************************************
-*********************************************************************/
-static void
-MACFInitElementFromObject(
- struct mac_module_data_element * element,
- OSObject * value)
-{
- const OSMetaClass * typeID = NULL; // do not release
-
- typeID = OSTypeIDInst(value);
- if (typeID == OSTypeID(OSString)) {
- OSString * stringObj = OSDynamicCast(OSString, value);
- element->value_type = MAC_DATA_TYPE_PRIMITIVE;
- element->value_size = stringObj->getLength() + 1;
- DPRINTF(("osdict: string %s size %d\n",
- stringObj->getCStringNoCopy(), element->value_size));
- memcpy(element->value, stringObj->getCStringNoCopy(),
- element->value_size);
- } else if (typeID == OSTypeID(OSNumber)) {
- OSNumber * numberObj = OSDynamicCast(OSNumber, value);
- element->value_type = MAC_DATA_TYPE_PRIMITIVE;
- element->value_size = sprintf(element->value, "%u",
- numberObj->unsigned32BitValue()) + 1;
- } else if (typeID == OSTypeID(OSBoolean)) {
- OSBoolean * boolObj = OSDynamicCast(OSBoolean, value);
- element->value_type = MAC_DATA_TYPE_PRIMITIVE;
- if (boolObj->isTrue()) {
- strcpy(element->value, "true");
- element->value_size = 5;
- } else {
- strcpy(element->value, "false");
- element->value_size = 6;
- }
- } else if (typeID == OSTypeID(OSData)) {
- OSData * dataObj = OSDynamicCast(OSData, value);
- element->value_type = MAC_DATA_TYPE_PRIMITIVE;
- element->value_size = dataObj->getLength();
- DPRINTF(("osdict: data size %d\n", dataObj->getLength()));
- memcpy(element->value, dataObj->getBytesNoCopy(),
- element->value_size);
+extern "C" const vm_allocation_site_t *
+OSKextGetAllocationSiteForCaller(uintptr_t address)
+{
+ OSKextActiveAccount * active;
+ vm_allocation_site_t * site;
+ vm_allocation_site_t * releasesite;
+
+ uint32_t baseIdx;
+ uint32_t lim;
+
+ IOSimpleLockLock(sKextAccountsLock);
+ site = releasesite = NULL;
+
+ // bsearch sKextAccounts list
+ for (baseIdx = 0, lim = sKextAccountsCount; lim; lim >>= 1)
+ {
+ active = &sKextAccounts[baseIdx + (lim >> 1)];
+ if ((address >= active->address) && (address < active->address_end))
+ {
+ site = &active->account->site;
+ if (!site->tag) vm_tag_alloc_locked(site, &releasesite);
+ break;
+ }
+ else if (address > active->address)
+ {
+ // move right
+ baseIdx += (lim >> 1) + 1;
+ lim--;
+ }
+ // else move left
+ }
+ IOSimpleLockUnlock(sKextAccountsLock);
+ if (releasesite) kern_allocation_name_release(releasesite);
+
+ return (site);
+}
+
+extern "C" uint32_t
+OSKextGetKmodIDForSite(const vm_allocation_site_t * site, char * name, vm_size_t namelen)
+{
+ OSKextAccount * account = (typeof(account)) site;
+ const char * kname;
+
+ if (name)
+ {
+ if (account->kext) kname = account->kext->getIdentifierCString();
+ else kname = "<>";
+ strlcpy(name, kname, namelen);
}
- return;
+
+ return (account->loadTag);
+}
+
+extern "C" void
+OSKextFreeSite(vm_allocation_site_t * site)
+{
+ OSKextAccount * freeAccount = (typeof(freeAccount)) site;
+ IODelete(freeAccount, OSKextAccount, 1);
}
/*********************************************************************
-* This function takes an OSDictionary and returns a struct mac_module_data
-* list.
*********************************************************************/
-static struct mac_module_data *
-MACFEncodeOSDictionary(OSDictionary * dict)
+
+#if CONFIG_KEC_FIPS
+
+#if PRAGMA_MARK
+#pragma mark Kernel External Components for FIPS compliance
+#endif
+
+/*********************************************************************
+ * Kernel External Components for FIPS compliance (KEC_FIPS)
+ *********************************************************************/
+static void *
+GetAppleTEXTHashForKext(OSKext * theKext, OSDictionary *theInfoDict)
{
- struct mac_module_data * result = NULL; // do not free
- const OSMetaClass * typeID = NULL; // do not release
- OSString * key = NULL; // do not release
- OSCollectionIterator * keyIterator = NULL; // must release
- struct mac_module_data_element * element = NULL; // do not free
- unsigned int strtabsize = 0;
- unsigned int listtabsize = 0;
- unsigned int dicttabsize = 0;
- unsigned int nkeys = 0;
- unsigned int datalen = 0;
- char * strtab = NULL; // do not free
- char * listtab = NULL; // do not free
- char * dicttab = NULL; // do not free
- vm_offset_t data_addr = 0;
+ AppleTEXTHash_t my_ath = {2, 0, NULL};
+ AppleTEXTHash_t * my_athp = NULL; // do not release
+ OSData * segmentHash = NULL; // do not release
- keyIterator = OSCollectionIterator::withCollection(dict);
- if (!keyIterator) {
- goto finish;
+ if (theKext == NULL || theInfoDict == NULL) {
+ return(NULL);
}
- /* Iterate over OSModuleData to figure out total size */
- while ( (key = OSDynamicCast(OSString, keyIterator->getNextObject())) ) {
-
- // Get the key's value and determine its type
- OSObject * value = dict->getObject(key);
- if (!value) {
- continue;
- }
-
- typeID = OSTypeIDInst(value);
- if (MACFObjectIsPrimitiveType(value)) {
- strtabsize += MACFLengthForObject(value);
- }
- else if (typeID == OSTypeID(OSArray)) {
- unsigned int k, cnt, nents;
- OSArray * arrayObj = OSDynamicCast(OSArray, value);
-
- nents = 0;
- cnt = arrayObj->getCount();
- for (k = 0; k < cnt; k++) {
- value = arrayObj->getObject(k);
- typeID = OSTypeIDInst(value);
- if (MACFObjectIsPrimitiveType(value)) {
- listtabsize += MACFLengthForObject(value);
- nents++;
- }
- else if (typeID == OSTypeID(OSDictionary)) {
- unsigned int dents = 0;
- OSDictionary * dictObj = NULL; // do not release
- OSString * dictkey = NULL; // do not release
- OSCollectionIterator * dictIterator = NULL; // must release
-
- dictObj = OSDynamicCast(OSDictionary, value);
- dictIterator = OSCollectionIterator::withCollection(dictObj);
- if (!dictIterator) {
- goto finish;
- }
- while ((dictkey = OSDynamicCast(OSString,
- dictIterator->getNextObject()))) {
-
- OSObject * dictvalue = NULL; // do not release
-
- dictvalue = dictObj->getObject(dictkey);
- if (!dictvalue) {
- continue;
- }
- if (MACFObjectIsPrimitiveType(dictvalue)) {
- strtabsize += MACFLengthForObject(dictvalue);
- } else {
- continue; /* Only handle primitive types here. */
- }
- /*
- * Allow for the "arraynnn/" prefix in the key length.
- */
- strtabsize += dictkey->getLength() + 1;
- dents++;
- }
- dictIterator->release();
- if (dents-- > 0) {
- dicttabsize += sizeof(struct mac_module_data_list) +
- dents * sizeof(struct mac_module_data_element);
- nents++;
- }
- }
- else {
- continue; /* Skip everything else. */
- }
- }
- if (nents == 0) {
- continue;
- }
- listtabsize += sizeof(struct mac_module_data_list) +
- (nents - 1) * sizeof(struct mac_module_data_element);
- } else {
- continue; /* skip anything else */
- }
- strtabsize += key->getLength() + 1;
- nkeys++;
- }
- if (nkeys == 0) {
- goto finish;
+ // Get the part of the plist associate with kAppleTextHashesKey and let
+ // the crypto library do further parsing (slice/architecture)
+ segmentHash = OSDynamicCast(OSData, theInfoDict->getObject(kAppleTextHashesKey));
+ // Support for ATH v1 while rolling out ATH v2 without revision locking submissions
+ // Remove this when v2 PLIST are supported
+ if (segmentHash == NULL) {
+ // If this fails, we may be dealing with a v1 PLIST
+ OSDictionary * textHashDict = NULL; // do not release
+ textHashDict = OSDynamicCast(OSDictionary, theInfoDict->getObject(kAppleTextHashesKey));
+ if (textHashDict == NULL) {
+ return(NULL);
+ }
+ my_ath.ath_version=1;
+ segmentHash = OSDynamicCast(OSData,textHashDict->getObject(ARCHNAME));
+ } // end of v2 rollout
+
+ if (segmentHash == NULL) {
+ return(NULL);
}
- /*
- * Allocate and fill in the module data structures.
- */
- datalen = sizeof(struct mac_module_data) +
- sizeof(mac_module_data_element) * (nkeys - 1) +
- strtabsize + listtabsize + dicttabsize;
- DPRINTF(("osdict: datalen %d strtabsize %d listtabsize %d dicttabsize %d\n",
- datalen, strtabsize, listtabsize, dicttabsize));
- if (kmem_alloc(kernel_map, &data_addr, datalen) != KERN_SUCCESS) {
- goto finish;
+ // KEC_FIPS type kexts never unload so we don't have to clean up our
+ // AppleTEXTHash_t
+ if (kmem_alloc(kernel_map, (vm_offset_t *) &my_athp,
+ sizeof(AppleTEXTHash_t), VM_KERN_MEMORY_OSKEXT) != KERN_SUCCESS) {
+ return(NULL);
}
- result = (mac_module_data *)data_addr;
- result->base_addr = data_addr;
- result->size = datalen;
- result->count = nkeys;
- strtab = (char *)&result->data[nkeys];
- listtab = strtab + strtabsize;
- dicttab = listtab + listtabsize;
- DPRINTF(("osdict: data_addr %p strtab %p listtab %p dicttab %p end %p\n",
- data_addr, strtab, listtab, dicttab, data_addr + datalen));
- keyIterator->reset();
- nkeys = 0;
- element = &result->data[0];
- DPRINTF(("osdict: element %p\n", element));
- while ( (key = OSDynamicCast(OSString, keyIterator->getNextObject())) ) {
-
- // Get the key's value and determine its type
- OSObject * value = dict->getObject(key);
- if (!value) {
- continue;
- }
+ memcpy(my_athp, &my_ath, sizeof(my_ath));
+ my_athp->ath_length = segmentHash->getLength();
+ if (my_athp->ath_length > 0) {
+ my_athp->ath_hash = (void *)segmentHash->getBytesNoCopy();
+ }
- /* Store key */
- DPRINTF(("osdict: element @%p\n", element));
- element->key = strtab;
- element->key_size = key->getLength() + 1;
- DPRINTF(("osdict: key %s size %d @%p\n", key->getCStringNoCopy(),
- element->key_size, strtab));
- memcpy(element->key, key->getCStringNoCopy(), element->key_size);
+#if 0
+ OSKextLog(theKext,
+ kOSKextLogErrorLevel |
+ kOSKextLogGeneralFlag,
+ "Kext %s ath_version %d ath_length %d ath_hash %p",
+ theKext->getIdentifierCString(),
+ my_athp->ath_version,
+ my_athp->ath_length,
+ my_athp->ath_hash);
+#endif
- typeID = OSTypeIDInst(value);
- if (MACFObjectIsPrimitiveType(value)) {
- /* Store value */
- element->value = element->key + element->key_size;
- DPRINTF(("osdict: primitive element value %p\n", element->value));
- MACFInitElementFromObject(element, value);
- strtab += element->key_size + element->value_size;
- DPRINTF(("osdict: new strtab %p\n", strtab));
- } else if (typeID == OSTypeID(OSArray)) {
- unsigned int k, cnt, nents;
- char *astrtab;
- struct mac_module_data_list *arrayhd;
- struct mac_module_data_element *ele;
- OSArray *arrayObj = OSDynamicCast(OSArray, value);
-
- element->value = listtab;
- DPRINTF(("osdict: array element value %p\n", element->value));
- element->value_type = MAC_DATA_TYPE_ARRAY;
- arrayhd = (struct mac_module_data_list *)element->value;
- arrayhd->type = 0;
- DPRINTF(("osdict: arrayhd %p\n", arrayhd));
- nents = 0;
- astrtab = strtab + element->key_size;
- ele = &(arrayhd->list[0]);
- cnt = arrayObj->getCount();
- for (k = 0; k < cnt; k++) {
- value = arrayObj->getObject(k);
- DPRINTF(("osdict: array ele %d @%p\n", nents, ele));
- ele->key = NULL;
- ele->key_size = 0;
- typeID = OSTypeIDInst(value);
- if (MACFObjectIsPrimitiveType(value)) {
- if (arrayhd->type != 0 &&
- arrayhd->type != MAC_DATA_TYPE_PRIMITIVE) {
-
- continue;
- }
- arrayhd->type = MAC_DATA_TYPE_PRIMITIVE;
- ele->value = astrtab;
- MACFInitElementFromObject(ele, value);
- astrtab += ele->value_size;
- DPRINTF(("osdict: array new astrtab %p\n", astrtab));
- } else if (typeID == OSTypeID(OSDictionary)) {
- unsigned int dents;
- char * dstrtab = NULL; // do not free
- OSDictionary * dictObj = NULL; // do not release
- OSString * dictkey = NULL; // do not release
- OSCollectionIterator * dictIterator = NULL; // must release
- struct mac_module_data_list * dicthd = NULL; // do not free
- struct mac_module_data_element * dele = NULL; // do not free
-
- if (arrayhd->type != 0 &&
- arrayhd->type != MAC_DATA_TYPE_DICT) {
-
- continue;
- }
- dictObj = OSDynamicCast(OSDictionary, value);
- dictIterator = OSCollectionIterator::withCollection(dictObj);
- if (!dictIterator) {
- goto finish;
- }
- DPRINTF(("osdict: dict\n"));
- ele->value = dicttab;
- ele->value_type = MAC_DATA_TYPE_DICT;
- dicthd = (struct mac_module_data_list *)ele->value;
- DPRINTF(("osdict: dicthd %p\n", dicthd));
- dstrtab = astrtab;
- dents = 0;
- while ((dictkey = OSDynamicCast(OSString,
- dictIterator->getNextObject()))) {
-
- OSObject * dictvalue = NULL; // do not release
-
- dictvalue = dictObj->getObject(dictkey);
- if (!dictvalue) {
- continue;
- }
- dele = &(dicthd->list[dents]);
- DPRINTF(("osdict: dict ele %d @%p\n", dents, dele));
- if (MACFObjectIsPrimitiveType(dictvalue)) {
- dele->key = dstrtab;
- dele->key_size = dictkey->getLength() + 1;
- DPRINTF(("osdict: dictkey %s size %d @%p\n",
- dictkey->getCStringNoCopy(), dictkey->getLength(), dstrtab));
- memcpy(dele->key, dictkey->getCStringNoCopy(),
- dele->key_size);
- dele->value = dele->key + dele->key_size;
- MACFInitElementFromObject(dele, dictvalue);
- dstrtab += dele->key_size + dele->value_size;
- DPRINTF(("osdict: dict new dstrtab %p\n", dstrtab));
- } else {
- continue; /* Only handle primitive types here. */
- }
- dents++;
- }
- dictIterator->release();
- if (dents == 0) {
- continue;
- }
- arrayhd->type = MAC_DATA_TYPE_DICT;
- ele->value_size = sizeof(struct mac_module_data_list) +
- (dents - 1) * sizeof(struct mac_module_data_element);
- DPRINTF(("osdict: dict ele size %d ents %d\n", ele->value_size, dents));
- dicttab += ele->value_size;
- DPRINTF(("osdict: new dicttab %p\n", dicttab));
- dicthd->count = dents;
- astrtab = dstrtab;
- } else {
- continue; /* Skip everything else. */
- }
- nents++;
- ele++;
- }
- if (nents == 0) {
- continue;
- }
- element->value_size = sizeof(struct mac_module_data_list) +
- (nents - 1) * sizeof(struct mac_module_data_element);
- listtab += element->value_size;
- DPRINTF(("osdict: new listtab %p\n", listtab));
- arrayhd->count = nents;
- strtab = astrtab;
- DPRINTF(("osdict: new strtab %p\n", strtab));
- } else {
- continue; /* skip anything else */
- }
- element++;
- }
- DPRINTF(("result list @%p, key %p value %p\n",
- result, result->data[0].key, result->data[0].value));
-finish:
- if (keyIterator) keyIterator->release();
- return result;
+ return( (void *) my_athp );
}
+
+#endif // CONFIG_KEC_FIPS
-/*********************************************************************
-* This function takes a plist and looks for an OSModuleData dictionary.
-* If it is found, an encoded copy is returned. The value must be
-* kmem_free()'d.
-*********************************************************************/
-static void *
-MACFCopyModuleDataForKext(
- OSKext * theKext,
- mach_msg_type_number_t * datalen)
-
+#if CONFIG_IMAGEBOOT
+int OSKextGetUUIDForName(const char *name, uuid_t uuid)
{
- struct mac_module_data * result = NULL;
- OSDictionary * kextModuleData = NULL; // do not release
- vm_map_copy_t copy = 0;
-
- kextModuleData = OSDynamicCast(OSDictionary,
- theKext->getPropertyForHostArch("OSModuleData"));
- if (!kextModuleData) {
- goto finish;
- }
-
- result = MACFEncodeOSDictionary(kextModuleData);
- if (!result) {
- goto finish;
- }
- *datalen = module_data->size;
+ OSKext *kext = OSKext::lookupKextWithIdentifier(name);
+ if (!kext) {
+ return 1;
+ }
-finish:
- return (void *)result;
+ OSData *uuid_data = kext->copyUUID();
+ if (uuid_data) {
+ memcpy(uuid, uuid_data->getBytesNoCopy(), sizeof(uuid_t));
+ OSSafeReleaseNULL(uuid_data);
+ return 0;
+ }
+
+ return 1;
}
-#endif /* CONFIG_MACF_KEXT */
+#endif
+