/*
- * 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 <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/IOService.h>
#include <IOKit/IOStatisticsPrivate.h>
+#include <IOKit/IOBSD.h>
+
+#include <san/kasan.h>
#if PRAGMA_MARK
#pragma mark External & Internal Function Protos
OSDictionary * dict,
const char * key,
const char * value);
+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_MACF_KEXT
-static void * MACFCopyModuleDataForKext(
- OSKext * theKext,
- mach_msg_type_number_t * datalen);
-#endif /* CONFIG_MACF_KEXT */
+#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 IORecursiveLock * sKextLock = NULL;
static OSDictionary * sKextsByID = NULL;
+static OSDictionary * sExcludeListByID = NULL;
static OSArray * sLoadedKexts = NULL;
static OSArray * sUnloadedPrelinkedKexts = NULL;
/* 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,
static char * loaded_kext_paniclist = NULL;
static uint32_t loaded_kext_paniclist_size = 0;
-static uint32_t loaded_kext_paniclist_length = 0;
-
+
AbsoluteTime last_loaded_timestamp;
-static char last_loaded_str[2*KMOD_MAX_NAME];
+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;
AbsoluteTime last_unloaded_timestamp;
-static char last_unloaded_str[2*KMOD_MAX_NAME];
+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;
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
*
* 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 = NULL;
+OSKextLoadedKextSummaryHeader * gLoadedKextSummaries __attribute__((used)) = NULL;
+uint64_t gLoadedKextSummariesTimestamp __attribute__((used)) = 0;
static size_t sLoadedKextSummariesAllocSize = 0;
-OSKextLoadedKextSummaryHeader * sPrevLoadedKextSummaries = NULL;
-static size_t sPrevLoadedKextSummariesAllocSize = 0;
+
+static OSKextActiveAccount * sKextAccounts;
+static uint32_t sKextAccountsCount;
};
/*********************************************************************
kOSKextLogVerboseFlagsMask;
static OSKextLogSpec sKernelLogFilter = kDefaultKernelLogFilter;
static bool sBootArgLogFilterFound = false;
-SYSCTL_INT(_debug, OID_AUTO, kextlog, CTLFLAG_RW | CTLFLAG_LOCKED, &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
goto finish;
}
linkBuffer->setDeallocFunction(osdata_kext_free);
-
OSKextLog(theKext,
kOSKextLogProgressLevel |
kOSKextLogLoadFlag | kOSKextLogLinkFlag,
result = 0;
}
- OSSafeRelease(linkBuffer);
+ OSSafeReleaseNULL(linkBuffer);
return (kxld_addr_t)result;
}
#define notifyKextLoadObservers(kext, kmod_info) \
do { \
- IOStatistics::onKextLoad(kext, kmod_info); \
+ IOStatistics::onKextLoad(kext, kmod_info); \
} while (0)
#define notifyKextUnloadObservers(kext) \
do { \
- IOStatistics::onKextUnload(kext); \
+ IOStatistics::onKextUnload(kext); \
} while (0)
#define notifyAddClassObservers(kext, addedClass, flags) \
do { \
- IOStatistics::onClassAdded(kext, addedClass); \
+ IOStatistics::onClassAdded(kext, addedClass); \
} while (0)
#define notifyRemoveClassObservers(kext, removedClass, flags) \
do { \
- IOStatistics::onClassRemoved(kext, removedClass); \
+ IOStatistics::onClassRemoved(kext, removedClass); \
} while (0)
#else
/* 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
}
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.
*/
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->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 |
"Kext system initialized.");
notifyKextLoadObservers(sKernelKext, sKernelKext->kmod_info);
-
+
return;
}
kernel_segment_command_t * seg_to_remove = NULL;
+#if __arm__ || __arm64__
+ const char * dt_segment_name = NULL;
+ void * segment_paddress = NULL;
+ int segment_size = 0;
+#endif
+
/* This must be the very first thing done by this function.
*/
IORecursiveLockLock(sKextLock);
OSRuntimeUnloadCPPForSegment(seg_to_remove);
}
-#if __i386__ || __x86_64__
+#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.
* This may invalidate any assumptions about "avail_start"
* 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_random((void *) 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
* 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;
/* Allocate space for the LINKEDIT copy.
*/
mem_result = kmem_alloc(kernel_map, (vm_offset_t *) &seg_copy,
- seg_length);
+ seg_length, VM_KERN_MEMORY_KEXT);
if (mem_result != KERN_SUCCESS) {
OSKextLog(/* kext */ NULL,
kOSKextLogErrorLevel |
/* Copy it out.
*/
memcpy(seg_copy, seg_data, seg_length);
-
+
/* Dump the booter memory.
*/
ml_static_mfree(seg_offset, seg_length);
&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_ALL,
+ /* cur_protection */ VM_PROT_READ | VM_PROT_WRITE,
/* max_protection */ VM_PROT_ALL,
/* inheritance */ VM_INHERIT_DEFAULT);
if ((mem_result != KERN_SUCCESS) ||
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) {
-#if __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);
+ 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);
}
-#else /* from if __arm__ */
-
-#error arch
-#endif /* from if __arm__ */
-
} else {
OSKextLog(/* kext */ NULL,
kOSKextLogBasicLevel |
finish:
IORecursiveLockUnlock(sKextLock);
- OSSafeRelease(prelinkedKexts);
- OSSafeRelease(kextIterator);
- OSSafeRelease(prelinkIterator);
+ OSSafeReleaseNULL(prelinkedKexts);
+ OSSafeReleaseNULL(kextIterator);
+ OSSafeReleaseNULL(prelinkIterator);
return;
}
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;
OSString * kextPath = NULL; // do not release
if (!setInfoDictionaryAndPath(anInfoDict, kextPath)) {
goto finish;
}
+#if KASLR_KEXT_DEBUG
+ IOLog("kaslr: doCoalesedSlides %d kext %s \n", doCoalesedSlides, getIdentifierCString());
+#endif
/* Also get the executable's bundle-relative path if present.
* Don't look for an arch-specific path property.
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);
}
getIdentifierCString());
goto finish;
}
+
+#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:
- OSSafeRelease(prelinkedExecutable);
+ OSSafeReleaseNULL(prelinkedExecutable);
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);
+ }
+
+finish:
+ IORecursiveLockUnlock(sKextLock);
+ OSSafeReleaseNULL(kextIterator);
+
+ return;
+}
+
/*********************************************************************
*********************************************************************/
OSKext *
result = registerIdentifier();
finish:
- OSSafeRelease(parsedXML);
- OSSafeRelease(kextPath);
- OSSafeRelease(errorString);
- OSSafeRelease(executable);
+ OSSafeReleaseNULL(parsedXML);
+ OSSafeReleaseNULL(kextPath);
+ OSSafeReleaseNULL(errorString);
+ OSSafeReleaseNULL(executable);
return result;
}
getIdentifierCString(), newVersionCString);
}
- OSSafeRelease(newUUID);
- OSSafeRelease(existingUUID);
+ OSSafeReleaseNULL(newUUID);
+ OSSafeReleaseNULL(existingUUID);
return result;
}
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(!)
result = true;
goto finish;
}
-
+
if (infoDict->getObject(_kOSKextExecutableKey) ||
infoDict->getObject(_kOSKextMkextExecutableReferenceKey)) {
panic("Attempt to free loaded kext %s.", getIdentifierCString());
}
- OSSafeRelease(infoDict);
- OSSafeRelease(bundleID);
- OSSafeRelease(path);
- OSSafeRelease(executableRelPath);
- OSSafeRelease(dependencies);
- 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 |
/*********************************************************************
* 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.
+* xxx - need to add further bounds checking for each file entry
*
* 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
*********************************************************************/
/* static */
OSReturn
-OSKext::readMkext1Archive(
- OSData * mkextData,
- uint32_t * checksumPtr)
+OSKext::readMkext2Archive(
+ OSData * mkextData,
+ OSDictionary ** mkextPlistOut,
+ uint32_t * checksumPtr)
{
- OSReturn result = kOSReturnError;
+ OSReturn result = kOSReturnError;
uint32_t mkextLength;
- mkext1_header * mkextHeader = 0; // do not free
- void * mkextEnd = 0; // do not free
+ mkext2_header * mkextHeader = NULL; // do not free
+ void * mkextEnd = NULL; // do not free
uint32_t mkextVersion;
- uint8_t * crc_address = 0;
+ uint8_t * crc_address = NULL;
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
+ uint32_t mkextPlistOffset;
+ uint32_t mkextPlistCompressedSize;
+ char * mkextPlistEnd = NULL; // do not free
+ uint32_t mkextPlistFullSize;
+ OSString * errorString = NULL; // must release
+ OSData * mkextPlistUncompressedData = NULL; // must release
+ const char * mkextPlistDataBuffer = NULL; // do not free
+ OSObject * parsedXML = NULL; // must release
+ OSDictionary * mkextPlist = NULL; // do not release
+ OSArray * mkextInfoDictArray = NULL; // do not release
+ uint32_t count, i;
mkextLength = mkextData->getLength();
- mkextHeader = (mkext1_header *)mkextData->getBytesNoCopy();
+ mkextHeader = (mkext2_header *)mkextData->getBytesNoCopy();
mkextEnd = (char *)mkextHeader + mkextLength;
- mkextVersion = OSSwapBigToHostInt32(mkextHeader->version);
+ mkextVersion = MKEXT_GET_VERSION(mkextHeader);
crc_address = (u_int8_t *)&mkextHeader->version;
checksum = mkext_adler32(crc_address,
(uintptr_t)mkextHeader +
- OSSwapBigToHostInt32(mkextHeader->length) - (uintptr_t)crc_address);
+ MKEXT_GET_LENGTH(mkextHeader) - (uintptr_t)crc_address);
- if (OSSwapBigToHostInt32(mkextHeader->adler32) != checksum) {
+ if (MKEXT_GET_CHECKSUM(mkextHeader) != checksum) {
OSKextLog(/* kext */ NULL,
- kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
- "Kext archive has a bad checksum.");
+ kOSKextLogErrorLevel |
+ kOSKextLogArchiveFlag,
+ "Mkext archive has bad checksum.");
result = kOSKextReturnBadData;
goto finish;
}
}
/* Check that the CPU type & subtype match that of the running kernel. */
- if (OSSwapBigToHostInt32(mkextHeader->cputype) != (UInt32)CPU_TYPE_ANY) {
+ if (MKEXT_GET_CPUTYPE(mkextHeader) == (UInt32)CPU_TYPE_ANY) {
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogErrorLevel |
+ kOSKextLogArchiveFlag,
+ "Mkext archive must have a specific CPU type.");
+ result = kOSKextReturnBadData;
+ goto finish;
+ } else {
if ((UInt32)_mh_execute_header.cputype !=
- OSSwapBigToHostInt32(mkextHeader->cputype)) {
+ MKEXT_GET_CPUTYPE(mkextHeader)) {
OSKextLog(/* kext */ NULL,
- kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
- "Kext archive doesn't contain software "
- "for this computer's CPU type.");
+ kOSKextLogErrorLevel |
+ kOSKextLogArchiveFlag,
+ "Mkext archive does not match the running kernel'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);
+ mkextPlistOffset = MKEXT2_GET_PLIST(mkextHeader);
+ mkextPlistCompressedSize = MKEXT2_GET_PLIST_COMPSIZE(mkextHeader);
+ mkextPlistEnd = (char *)mkextHeader + mkextPlistOffset +
+ mkextPlistCompressedSize;
+ if (mkextPlistEnd > mkextEnd) {
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogErrorLevel |
+ kOSKextLogArchiveFlag,
+ "Mkext archive file overrun.");
+ result = kOSKextReturnBadData;
+ }
- 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;
+ mkextPlistFullSize = MKEXT2_GET_PLIST_FULLSIZE(mkextHeader);
+ if (mkextPlistCompressedSize) {
+ mkextPlistUncompressedData = sKernelKext->extractMkext2FileData(
+ (UInt8 *)mkextHeader + mkextPlistOffset,
+ "plist",
+ mkextPlistCompressedSize, mkextPlistFullSize);
+ if (!mkextPlistUncompressedData) {
goto finish;
}
+ mkextPlistDataBuffer = (const char *)
+ mkextPlistUncompressedData->getBytesNoCopy();
+ } else {
+ mkextPlistDataBuffer = (const char *)mkextHeader + mkextPlistOffset;
+ }
- /* 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;
+ /* IOCFSerialize added a nul byte to the end of the string. Very nice of it.
+ */
+ parsedXML = OSUnserializeXML(mkextPlistDataBuffer, &errorString);
+ if (parsedXML) {
+ mkextPlist = OSDynamicCast(OSDictionary, parsedXML);
+ }
+ if (!mkextPlist) {
+ 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 unserializing mkext plist: %s.", errorCString);
+ goto finish;
+ }
- /*****
- * 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;
+ /* If the caller needs the plist, hand it back and retain it.
+ * (This function releases it at the end.)
+ */
+ if (mkextPlistOut) {
+ *mkextPlistOut = mkextPlist;
+ (*mkextPlistOut)->retain();
+ }
- mkextExecutableInfo = OSData::withCapacity(sizeof(entryRef));
- if (!mkextExecutableInfo) {
- panic("Error: Couldn't allocate data object "
- "for mkext archive entry %d.\n", i);
- }
+ mkextInfoDictArray = OSDynamicCast(OSArray,
+ mkextPlist->getObject(kMKEXTInfoDictionariesKey));
+ if (!mkextInfoDictArray) {
+ OSKextLog(/* kext */ NULL,
+ kOSKextLogErrorLevel |
+ kOSKextLogArchiveFlag,
+ "Mkext archive contains no kext info dictionaries.");
+ goto finish;
+ }
- 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;
- }
+ count = mkextInfoDictArray->getCount();
+ for (i = 0; i < count; i++) {
+ OSDictionary * infoDict;
- }
- /* Init can fail because of a data/runtime error, or because the
- * kext is a dup. Either way, we don't care here.
+ infoDict = OSDynamicCast(OSDictionary,
+ 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.
*/
- if (!theKext->initWithMkext1Info(infoDict, mkextExecutableInfo,
- mkextData)) {
-
- // theKext is released at the top of the loop or in the finish block
- continue;
+ if (infoDict) {
+ OSKext * newKext = OSKext::withMkext2Info(infoDict, mkextData);
+ OSSafeReleaseNULL(newKext);
}
-
- /* 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;
}
+ /* Even if we didn't keep any kexts from the mkext, we may have a load
+ * request to process, so we are successful (no errors occurred).
+ */
+ result = kOSReturnSuccess;
+
finish:
- OSSafeRelease(infoDictDataObject);
- OSSafeRelease(parsedXML);
- OSSafeRelease(errorString);
- OSSafeRelease(mkextExecutableInfo);
- OSSafeRelease(theKext);
+ OSSafeReleaseNULL(parsedXML);
+ OSSafeReleaseNULL(mkextPlistUncompressedData);
+ OSSafeReleaseNULL(errorString);
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
-*
-* 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::readMkext2Archive(
- OSData * mkextData,
- OSDictionary ** mkextPlistOut,
- uint32_t * checksumPtr)
-{
- OSReturn result = kOSReturnError;
- uint32_t mkextLength;
- mkext2_header * mkextHeader = NULL; // do not free
- void * mkextEnd = NULL; // do not free
- uint32_t mkextVersion;
- uint8_t * crc_address = NULL;
- uint32_t checksum;
- uint32_t mkextPlistOffset;
- uint32_t mkextPlistCompressedSize;
- char * mkextPlistEnd = NULL; // do not free
- uint32_t mkextPlistFullSize;
- OSString * errorString = NULL; // must release
- OSData * mkextPlistUncompressedData = NULL; // must release
- const char * mkextPlistDataBuffer = NULL; // do not free
- OSObject * parsedXML = NULL; // must release
- OSDictionary * mkextPlist = NULL; // do not release
- OSArray * mkextInfoDictArray = NULL; // do not release
- uint32_t count, i;
-
- mkextLength = mkextData->getLength();
- mkextHeader = (mkext2_header *)mkextData->getBytesNoCopy();
- mkextEnd = (char *)mkextHeader + mkextLength;
- mkextVersion = MKEXT_GET_VERSION(mkextHeader);
-
- crc_address = (u_int8_t *)&mkextHeader->version;
- checksum = mkext_adler32(crc_address,
- (uintptr_t)mkextHeader +
- MKEXT_GET_LENGTH(mkextHeader) - (uintptr_t)crc_address);
-
- if (MKEXT_GET_CHECKSUM(mkextHeader) != checksum) {
- OSKextLog(/* kext */ NULL,
- kOSKextLogErrorLevel |
- kOSKextLogArchiveFlag,
- "Mkext archive has bad checksum.");
- result = kOSKextReturnBadData;
- goto finish;
- }
-
- if (checksumPtr) {
- *checksumPtr = checksum;
- }
-
- /* Check that the CPU type & subtype match that of the running kernel. */
- if (MKEXT_GET_CPUTYPE(mkextHeader) == (UInt32)CPU_TYPE_ANY) {
- OSKextLog(/* kext */ NULL,
- kOSKextLogErrorLevel |
- kOSKextLogArchiveFlag,
- "Mkext archive must have a specific CPU type.");
- result = kOSKextReturnBadData;
- goto finish;
- } else {
- if ((UInt32)_mh_execute_header.cputype !=
- MKEXT_GET_CPUTYPE(mkextHeader)) {
-
- OSKextLog(/* kext */ NULL,
- kOSKextLogErrorLevel |
- kOSKextLogArchiveFlag,
- "Mkext archive does not match the running kernel's CPU type.");
- result = kOSKextReturnArchNotFound;
- goto finish;
- }
- }
-
- mkextPlistOffset = MKEXT2_GET_PLIST(mkextHeader);
- mkextPlistCompressedSize = MKEXT2_GET_PLIST_COMPSIZE(mkextHeader);
- mkextPlistEnd = (char *)mkextHeader + mkextPlistOffset +
- mkextPlistCompressedSize;
- if (mkextPlistEnd > mkextEnd) {
- OSKextLog(/* kext */ NULL,
- kOSKextLogErrorLevel |
- kOSKextLogArchiveFlag,
- "Mkext archive file overrun.");
- result = kOSKextReturnBadData;
- }
-
- mkextPlistFullSize = MKEXT2_GET_PLIST_FULLSIZE(mkextHeader);
- if (mkextPlistCompressedSize) {
- mkextPlistUncompressedData = sKernelKext->extractMkext2FileData(
- (UInt8 *)mkextHeader + mkextPlistOffset,
- "plist",
- mkextPlistCompressedSize, mkextPlistFullSize);
- if (!mkextPlistUncompressedData) {
- goto finish;
- }
- mkextPlistDataBuffer = (const char *)
- mkextPlistUncompressedData->getBytesNoCopy();
- } else {
- mkextPlistDataBuffer = (const char *)mkextHeader + mkextPlistOffset;
- }
-
- /* IOCFSerialize added a nul byte to the end of the string. Very nice of it.
- */
- parsedXML = OSUnserializeXML(mkextPlistDataBuffer, &errorString);
- if (parsedXML) {
- mkextPlist = OSDynamicCast(OSDictionary, parsedXML);
- }
- if (!mkextPlist) {
- 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 unserializing mkext plist: %s.", errorCString);
- goto finish;
- }
-
- /* If the caller needs the plist, hand it back and retain it.
- * (This function releases it at the end.)
- */
- if (mkextPlistOut) {
- *mkextPlistOut = mkextPlist;
- (*mkextPlistOut)->retain();
- }
-
- mkextInfoDictArray = OSDynamicCast(OSArray,
- mkextPlist->getObject(kMKEXTInfoDictionariesKey));
- if (!mkextInfoDictArray) {
- OSKextLog(/* kext */ NULL,
- kOSKextLogErrorLevel |
- kOSKextLogArchiveFlag,
- "Mkext archive contains no kext info dictionaries.");
- goto finish;
- }
-
- count = mkextInfoDictArray->getCount();
- for (i = 0; i < count; i++) {
- OSDictionary * infoDict;
-
-
- infoDict = OSDynamicCast(OSDictionary,
- 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);
- }
-
- /* Even if we didn't keep any kexts from the mkext, we may have a load
- * request to process, so we are successful (no errors occurred).
- */
- result = kOSReturnSuccess;
-
-finish:
-
- OSSafeRelease(parsedXML);
- OSSafeRelease(mkextPlistUncompressedData);
- OSSafeRelease(errorString);
-
- return result;
-}
-
-/*********************************************************************
-*********************************************************************/
-/* static */
-OSKext *
-OSKext::withMkext2Info(
+/* static */
+OSKext *
+OSKext::withMkext2Info(
OSDictionary * anInfoDict,
OSData * mkextData)
{
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;
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 (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) {
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;
}
-/*********************************************************************
-*********************************************************************/
-/* static */
-bool OSKext::isKextWithIdentifierLoaded(const char * kextIdentifier)
+OSData *
+OSKext::copyKextUUIDForAddress(OSNumber *address)
{
- bool result = false;
- OSKext * foundKext = NULL; // returned
+ OSData *uuid = NULL;
- IORecursiveLockLock(sKextLock);
+ if (!address) {
+ return NULL;
+ }
- foundKext = OSDynamicCast(OSKext, sKextsByID->getObject(kextIdentifier));
- if (foundKext && foundKext->isLoaded()) {
- result = true;
- }
+ 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;
+}
+
+/*********************************************************************
+*********************************************************************/
+OSKext *
+OSKext::lookupKextWithUUID(uuid_t wanted)
+{
+ OSKext * foundKext = NULL; // returned
+ uint32_t count, i;
+
+ IORecursiveLockLock(sKextLock);
+
+ 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);
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
}
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
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;
}
*********************************************************************/
void OSKext::getSizeInfo(uint32_t *loadSize, uint32_t *wiredSize)
{
- if (linkedExecutable) {
- *loadSize = linkedExecutable->getLength();
+ 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;
- }
+ /* 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;
+ }
}
/*********************************************************************
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 (__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 */
startOpt, startMatchingOpt, personalityNames);
finish:
- OSSafeRelease(kextIdentifier);
+ OSSafeReleaseNULL(kextIdentifier);
return result;
}
}
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 (isInExcludeList()) {
+ OSKextLog(this,
+ kOSKextLogErrorLevel | kOSKextLogGeneralFlag |
+ kOSKextLogLoadFlag,
+ "Kext %s is in exclude list, not loadable",
+ getIdentifierCString());
+
+ result = kOSKextReturnNotLoadable;
+ goto finish;
+ }
+
if (isLoaded()) {
alreadyLoaded = true;
result = kOSReturnSuccess;
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,
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
#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:
}
size = 1 + strlen(string);
- result = (char *)kalloc(size);
+ result = (char *)kalloc_tag(size, VM_KERN_MEMORY_OSKEXT);
if (!result) {
goto finish;
}
}
/*********************************************************************
-* called only by load()
+*
*********************************************************************/
-OSReturn
-OSKext::loadExecutable()
+
+kernel_section_t *
+OSKext::lookupSection(const char *segname, const char *secname)
{
- OSReturn result = kOSReturnError;
- kern_return_t kxldResult;
- 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;
- 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
- OSString * versString = NULL; // do not release
- const char * versCString = NULL; // do not free
- const char * string = NULL; // do not free
- unsigned int i;
+ kernel_section_t * found_section = NULL;
+ kernel_mach_header_t * mh = NULL;
+ kernel_segment_command_t * seg = NULL;
+ kernel_section_t * sec = NULL;
- /* We need the version string for a variety of bits below.
- */
- versString = OSDynamicCast(OSString,
- getPropertyForHostArch(kCFBundleVersionKey));
- if (!versString) {
+ 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
+OSKext::loadExecutable()
+{
+ OSReturn result = kOSReturnError;
+ kern_return_t kxldResult;
+ 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;
+ 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
+ OSString * versString = NULL; // do not release
+ const char * versCString = NULL; // do not free
+ const char * string = NULL; // do not free
+ unsigned int i;
+
+ /* We need the version string for a variety of bits below.
+ */
+ versString = OSDynamicCast(OSString,
+ getPropertyForHostArch(kCFBundleVersionKey));
+ if (!versString) {
goto finish;
}
versCString = versString->getCStringNoCopy();
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 finish;
}
- kxlddeps = (KXLDDependency *)kalloc(num_kxlddeps * sizeof(*kxlddeps));
+ kxlddeps = (KXLDDependency *)kalloc_tag(num_kxlddeps * sizeof(*kxlddeps), VM_KERN_MEMORY_OSKEXT);
if (!kxlddeps) {
OSKextLog(this,
kOSKextLogErrorLevel |
* cache and invalidate the instruction cache.
* I/D caches are coherent on x86
*/
-#if !defined(__i386__) && !defined(__x86_64__)
+#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
/* 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:
- OSSafeRelease(linkDependencies);
+ OSSafeReleaseNULL(linkDependencies);
/* Clear up locally allocated dependency info.
*/
{
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;
- vm_offset_t linkeditaddr = 0;
OSData * data = NULL;
-
- if (sKeepSymbols || isLibrary() || !isExecutable() || !linkedExecutable) {
+
+#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;
}
*/
linkeditsize = round_page(linkedit->vmsize);
kextsize = kmod_info->size - linkeditsize;
-
- /* Save linkedit address as removeLinkeditHeaders() will zero it */
- linkeditaddr = trunc_page(linkedit->vmaddr);
-
+ start = linkedit->vmaddr;
+
data = OSData::withBytesNoCopy((void *)kmod_info->address, kextsize);
if (!data) {
goto finish;
}
- data->setDeallocFunction(osdata_kext_free);
-
- /* Rewrite the Mach-O headers.
- */
- if (KERN_SUCCESS != removeLinkeditHeaders(linkedit)) {
- 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.
*/
- kext_free(linkeditaddr, linkeditsize);
+#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
*********************************************************************/
-OSReturn
-OSKext::removeLinkeditHeaders(kernel_segment_command_t *linkedit)
+void
+OSKext::jettisonDATASegmentPadding(void)
{
- OSReturn result = KERN_FAILURE;
- kernel_mach_header_t * machhdr = (kernel_mach_header_t *)kmod_info->address;
- vm_map_t kext_map;
- u_char * src, * dst;
- uint32_t cmdsize, ncmds;
- u_int i = 0;
+ kernel_mach_header_t * mh;
+ kernel_segment_command_t * dataSeg;
+ kernel_section_t * sec, * lastSec;
+ vm_offset_t dataSegEnd, lastSecEnd;
+ vm_size_t padSize;
- kext_map = kext_get_vm_map(kmod_info);
- if (!kext_map) {
- result = KERN_MEMORY_ERROR;
- goto finish;
- }
+ mh = (kernel_mach_header_t *)kmod_info->address;
- result = vm_map_protect(kext_map, kmod_info->address,
- kmod_info->address + kmod_info->hdr_size, VM_PROT_DEFAULT, TRUE);
- if (result != KERN_SUCCESS) {
- goto finish;
+ dataSeg = getsegbynamefromheader(mh, SEG_DATA);
+ if (dataSeg == NULL) {
+ return;
}
- ncmds = machhdr->ncmds;
- src = dst = (u_char *)(kmod_info->address + sizeof(*machhdr));
-
- for (i = 0; i < ncmds; ++i, src += cmdsize) {
- struct load_command * lc = (struct load_command *) src;
- cmdsize = lc->cmdsize;
+ lastSec = NULL;
+ sec = firstsect(dataSeg);
+ while (sec != NULL) {
+ lastSec = sec;
+ sec = nextsect(dataSeg, sec);
+ }
- switch (lc->cmd) {
- case LC_SEGMENT:
- case LC_SEGMENT_64:
- if (src != (u_char *)linkedit) break;
- /* FALLTHROUGH */
- case LC_SYMTAB:
- case LC_DYSYMTAB:
- bzero(src, cmdsize);
- machhdr->ncmds--;
- machhdr->sizeofcmds -= cmdsize;
- continue;
- }
+ if (lastSec == NULL) {
+ return;
+ }
- memmove(dst, src, cmdsize);
- dst += cmdsize;
+ if ((dataSeg->vmaddr != round_page(dataSeg->vmaddr)) ||
+ (dataSeg->vmsize != round_page(dataSeg->vmsize))) {
+ return;
}
- result = vm_map_protect(kext_map, kmod_info->address,
- kmod_info->address + kmod_info->hdr_size, VM_PROT_READ, TRUE);
- if (result != KERN_SUCCESS) {
- goto finish;
+ dataSegEnd = dataSeg->vmaddr + dataSeg->vmsize;
+ lastSecEnd = round_page(lastSec->addr + lastSec->size);
+
+ if (dataSegEnd <= lastSecEnd) {
+ return;
}
- result = KERN_SUCCESS;
+ padSize = dataSegEnd - lastSecEnd;
-finish:
- return result;
+ if (padSize >= PAGE_SIZE) {
+#if VM_MAPPED_KEXTS
+ kext_free(lastSecEnd, padSize);
+#else
+ ml_static_mfree(lastSecEnd, padSize);
+#endif
+ }
}
/*********************************************************************
}
extern "C" {
- extern int (*dtrace_modload)(struct kmod_info *);
+ extern int (*dtrace_modload)(struct kmod_info *, uint32_t);
extern int (*dtrace_modunload)(struct kmod_info *);
};
* prevent a kext from loading, so we ignore the return code.
*/
if (!flags.dtraceInitialized && (dtrace_modload != NULL)) {
- (void)(*dtrace_modload)(kmod_info);
+ 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();
}
/*********************************************************************
* 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;
+ }
}
- if (segmentShouldBeWired(seg)) {
- result = vm_map_wire(kext_map, start, end, seg->initprot, FALSE);
+ if (segmentShouldBeWired(seg) && wire) {
+ result = OSKext_wire(kext_map, start, end, seg->initprot, FALSE);
if (result != KERN_SUCCESS) {
goto finish;
}
/* 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's segments are backed by physical memory.
*/
/*********************************************************************
*********************************************************************/
-OSReturn
-OSKext::start(bool startDependenciesFlag)
+static void
+OSKextLogKextInfo(OSKext *aKext, uint64_t address, uint64_t size, firehose_tracepoint_code_t code)
{
- 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 */
- if (isStarted() || isInterface() || isKernelComponent()) {
- result = kOSReturnSuccess;
- goto finish;
+ 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
+OSKext::start(bool startDependenciesFlag)
+{
+ OSReturn result = kOSReturnError;
+ kern_return_t (* startfunc)(kmod_info_t *, void *);
+ unsigned int i, count;
+ void * kmodStartData = NULL;
+
+ if (isStarted() || isInterface() || isKernelComponent()) {
+ result = kOSReturnSuccess;
+ goto finish;
}
if (!isLoaded()) {
}
}
-#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;
+ // 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 !CONFIG_STATIC_CPPINIT
}
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;
}
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 (hasOSMetaClassInstances()) {
+ if (!isLoaded()) {
+ result = kOSReturnSuccess;
+ goto finish;
+ }
+
+ if (isKernelComponent()) {
+ result = kOSKextReturnInvalidArgument;
+ goto finish;
+ }
+
+ 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).
"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
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) {
+#if KASAN
+ kasan_unload_kext((vm_offset_t)linkedExecutable->getBytesNoCopy(), linkedExecutable->getLength());
+#endif
+
+#if VM_MAPPED_KEXTS
if (!isInterface()) {
kernel_segment_command_t *seg = NULL;
vm_map_t kext_map = kext_get_vm_map(kmod_info);
seg = nextsegfromheader((kernel_mach_header_t *) kmod_info->address, seg);
}
}
-
+#endif
OSSafeReleaseNULL(linkedExecutable);
}
* kernel cache. 9055303
*/
if (isPrelinked()) {
- sUnloadedPrelinkedKexts->setObject(bundleID);
+ if (!_OSKextInUnloadedPrelinkedKexts(bundleID)) {
+ IORecursiveLockLock(sKextLock);
+ if (sUnloadedPrelinkedKexts) {
+ sUnloadedPrelinkedKexts->setObject(bundleID);
+ }
+ IORecursiveLockUnlock(sKextLock);
+ }
}
OSKextLog(this,
result = kOSReturnSuccess;
finish:
- OSSafeRelease(loadRequest);
+ OSSafeReleaseNULL(loadRequest);
return result;
}
result = OSKext::removeKext(aKext);
finish:
-
return result;
}
-
+
/*********************************************************************
*********************************************************************/
void
IORecursiveLockLock(sKextInnerLock);
OSKext::flushNonloadedKexts(/* flushPrelinkedKexts */ true);
-
- IOLockLock(sKextSummariesLock);
-
- /* If there is an old kext summary, free that now.
- */
- if (sPrevLoadedKextSummaries) {
- kmem_free(kernel_map, (vm_offset_t)sPrevLoadedKextSummaries,
- sPrevLoadedKextSummariesAllocSize);
- sPrevLoadedKextSummaries = NULL;
- sPrevLoadedKextSummariesAllocSize = 0;
- }
-
- IOLockUnlock(sKextSummariesLock);
-
+
/* 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,
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);
sConsiderUnloadsPending = false;
sConsiderUnloadsExecuted = true;
- (void) OSKext::considerRebuildOfPrelinkedKernel(NULL);
+ (void) OSKext::considerRebuildOfPrelinkedKernel();
IORecursiveLockUnlock(sKextInnerLock);
IORecursiveLockUnlock(sKextLock);
*********************************************************************/
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;
*********************************************************************/
/* static */
void
-OSKext::considerRebuildOfPrelinkedKernel(OSString * moduleName)
-{
- OSReturn checkResult = kOSReturnError;
- static bool requestedPrelink = false;
- OSDictionary * prelinkRequest = NULL; // must release
-
+OSKext::considerRebuildOfPrelinkedKernel(void)
+{
+ 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);
- /* moduleName is only passed when we see a load come in. We are only
- * interested in rebuilding the kernel cache if the kext we are loading
- * is not already in the original kernel cache. 9055303
+ /* We need to wait for kextd to get up and running with unloads already done
+ * and any new startup kexts loaded.
*/
- if (moduleName) {
- int count = sUnloadedPrelinkedKexts->getCount();
- int i;
-
- for (i = 0; i < count; i++) {
- const OSSymbol * myBundleID; // do not release
-
- myBundleID = OSDynamicCast(OSSymbol, sUnloadedPrelinkedKexts->getObject(i));
- if (!myBundleID) continue;
- if (moduleName->isEqualTo(myBundleID->getCStringNoCopy())) {
- OSKextLog(/* kext */ NULL,
- kOSKextLogDetailLevel |
- kOSKextLogArchiveFlag,
- "bundleID %s already in cache skipping rebuild.",
- myBundleID->getCStringNoCopy());
-
- /* no need to rebuild, already in kernel cache */
- goto finish;
- }
+ if (!sConsiderUnloadsExecuted ||
+ !sDeferredLoadSucceeded) {
+ goto finish;
+ }
+
+ /* 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;
}
- (void) OSKext::setDeferredLoadSucceeded();
+ /* 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;
}
-
- if (!sDeferredLoadSucceeded || !sConsiderUnloadsExecuted ||
- sSafeBoot || requestedPrelink)
- {
+ sUnloadedPrelinkedKexts->flushCollection();
+
+ if (!doRebuild) {
goto finish;
}
-
- OSKextLog(/* kext */ NULL,
- kOSKextLogProgressLevel |
- kOSKextLogArchiveFlag,
- "Requesting build of prelinked kernel.");
-
+
checkResult = _OSKextCreateRequest(kKextRequestPredicateRequestPrelink,
- &prelinkRequest);
+ &prelinkRequest);
if (checkResult != kOSReturnSuccess) {
goto finish;
}
-
+
if (!sKernelRequests->setObject(prelinkRequest)) {
goto finish;
}
-
+
OSKext::pingKextd();
- requestedPrelink = true;
-
+
finish:
IORecursiveLockUnlock(sKextLock);
- OSSafeRelease(prelinkRequest);
+ OSSafeReleaseNULL(prelinkRequest);
+ OSSafeReleaseNULL(kextIterator);
+
return;
}
getIdentifierCString());
}
- OSSafeRelease(localLoopStack);
- OSSafeRelease(libraryIterator);
+ OSSafeReleaseNULL(localLoopStack);
+ OSSafeReleaseNULL(libraryIterator);
return result;
}
finish:
- OSSafeRelease(classIterator);
+ OSSafeReleaseNULL(classIterator);
return result;
}
theKext->reportOSMetaClassInstances(msgLogSpec);
finish:
- OSSafeRelease(theKext);
+ OSSafeReleaseNULL(theKext);
return;
}
}
finish:
- OSSafeRelease(classIterator);
+ OSSafeReleaseNULL(classIterator);
return;
}
OSDictionary * requestDict = NULL; // do not release
OSString * errorString = NULL; // must release
- OSData * responseData = 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(kKextRequestPredicateGetKernelImage) &&
- !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)) {
+ } 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;
}
}
- responseObject = OSKext::copyLoadedKextInfo(kextIdentifiers, infoKeys);
+ 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 = addressNum;
- result = kOSReturnSuccess;
-
- } else if (predicate->isEqualTo(kKextRequestPredicateGetKernelImage)) {
- OSKextLog(/* kext */ NULL,
- kOSKextLogDebugLevel |
- kOSKextLogIPCFlag,
- "Returning kernel image.");
- responseData = OSKext::copySanitizedKernelImage();
- result = kOSReturnSuccess;
-
} else if (predicate->isEqualTo(kKextRequestPredicateGetKernelRequests)) {
/* Hand the current sKernelRequests array to the caller
"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;
if (!responseObject->serialize(serializer)) {
OSKextLog(/* kext */ NULL,
- kOSKextLogErrorLevel |
- kOSKextLogIPCFlag,
+ kOSKextLogGeneralFlag | kOSKextLogErrorLevel,
"Failed to serialize response to request from user space.");
result = kOSKextReturnSerialization;
goto finish;
/* This kmem_alloc sets the return value of the function.
*/
kmem_result = kmem_alloc(kernel_map, (vm_offset_t *)&buffer,
- responseLength);
+ round_page(responseLength), VM_KERN_MEMORY_OSKEXT);
if (kmem_result != KERN_SUCCESS) {
OSKextLog(/* kext */ NULL,
kOSKextLogErrorLevel |
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;
IORecursiveLockUnlock(sKextLock);
- OSSafeRelease(parsedXML);
- OSSafeRelease(errorString);
- OSSafeRelease(responseData);
- OSSafeRelease(responseObject);
- OSSafeRelease(serializer);
- OSSafeRelease(logInfoArray);
+ OSSafeReleaseNULL(parsedXML);
+ OSSafeReleaseNULL(errorString);
+ OSSafeReleaseNULL(responseObject);
+ OSSafeReleaseNULL(serializer);
+ OSSafeReleaseNULL(logInfoArray);
return result;
}
-/*********************************************************************
-*********************************************************************/
-/* static */
-OSDictionary *
-OSKext::copyLoadedKextInfo(
- 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);
+// #include <InstrProfiling.h>
+extern "C" {
- /* Empty list of bundle ids is equivalent to no list (get all).
- */
- if (kextIdentifiers && !kextIdentifiers->getCount()) {
- kextIdentifiers = NULL;
- } else if (kextIdentifiers) {
- idCount = kextIdentifiers->getCount();
- }
+ 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);
+}
- /* Same for keys.
- */
- if (infoKeys && !infoKeys->getCount()) {
- infoKeys = NULL;
- }
- count = sLoadedKexts->getCount();
- result = OSDictionary::withCapacity(count);
- if (!result) {
- goto finish;
+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)++;
+ }
}
- for (i = 0; i < count; i++) {
- OSKext * thisKext = NULL; // do not release
- Boolean includeThis = true;
+}
- if (kextInfo) {
- kextInfo->release();
- kextInfo = NULL;
- }
- thisKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i));
- if (!thisKext) {
- continue;
- }
- /* Skip current kext if we have a list of bundle IDs and
- * it isn't in the list.
- */
- if (kextIdentifiers) {
- const OSString * thisKextID = thisKext->getIdentifier();
+static
+void OSKextPgoMetadataPutMax(size_t *position, const char *key, size_t value_max)
+{
+ *position += strlen(key) + 1 + value_max + 1;
+}
- includeThis = false;
- for (idIndex = 0; idIndex < idCount; idIndex++) {
- const OSString * thisRequestID = OSDynamicCast(OSString,
- kextIdentifiers->getObject(idIndex));
- if (thisKextID->isEqualTo(thisRequestID)) {
- includeThis = true;
- break;
- }
- }
- }
-
- if (!includeThis) {
- continue;
- }
+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;
- kextInfo = thisKext->copyInfo(infoKeys);
- if (kextInfo) {
- result->setObject(thisKext->getIdentifier(), kextInfo);
+ 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);
}
-
-finish:
- IORecursiveLockUnlock(sKextLock);
- if (kextInfo) kextInfo->release();
+ 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);
- return result;
}
-/*********************************************************************
-* Any info that needs to do allocations must goto finish on alloc
+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;
+ }
+
+ memcpy(&thisKextUUID, uuid_data->getBytesNoCopy(), sizeof(thisKextUUID));
+ OSSafeReleaseNULL(uuid_data);
+
+ uuid_unparse(thisKextUUID, uuid_key);
+
+ /* Skip current kext if we have a list of UUIDs and
+ * it isn't in the list.
+ */
+ if (kextIdentifiers) {
+ includeThis = false;
+
+ for (idIndex = 0; idIndex < idCount; idIndex++) {
+ const OSString* wantedUUID = OSDynamicCast(OSString,
+ kextIdentifiers->getObject(idIndex));
+
+ uuid_t uuid;
+ uuid_parse(wantedUUID->getCStringNoCopy(), uuid);
+
+ 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);
+
+ if (kextInfo) kextInfo->release();
+
+ return result;
+}
+
+/*********************************************************************
+*********************************************************************/
+/* static */
+OSDictionary *
+OSKext::copyLoadedKextInfo(
+ 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 bundle ids 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;
+ }
+
+#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;
+
+ if (kextInfo) {
+ kextInfo->release();
+ kextInfo = NULL;
+ }
+ thisKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i));
+ if (!thisKext) {
+ continue;
+ }
+
+ /* Skip current kext if we have a list of bundle IDs and
+ * it isn't in the list.
+ */
+ if (kextIdentifiers) {
+ const OSString * thisKextID = thisKext->getIdentifier();
+
+ includeThis = false;
+
+ for (idIndex = 0; idIndex < idCount; idIndex++) {
+ const OSString * thisRequestID = OSDynamicCast(OSString,
+ kextIdentifiers->getObject(idIndex));
+ if (thisKextID->isEqualTo(thisRequestID)) {
+ includeThis = true;
+ break;
+ }
+ }
+ }
+
+ if (!includeThis) {
+ continue;
+ }
+
+ kextInfo = thisKext->copyInfo(infoKeys);
+ if (kextInfo) {
+ result->setObject(thisKext->getIdentifier(), kextInfo);
+ }
+ }
+
+finish:
+ IORecursiveLockUnlock(sKextLock);
+
+ if (kextInfo) kextInfo->release();
+
+ return result;
+}
+
+/*********************************************************************
+* 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.
*********************************************************************/
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;
+ uint32_t executablePathCStringSize = 0;
char * executablePathCString = NULL; // must release
OSString * executablePathString = NULL; // must release
OSData * uuid = NULL; // must release
*/
if (!infoKeys ||
_OSArrayContainsCString(infoKeys, kOSBundleMachOHeadersKey) ||
+ _OSArrayContainsCString(infoKeys, kOSBundleLogStringsKey) ||
_OSArrayContainsCString(infoKeys, kOSBundleCPUTypeKey) ||
_OSArrayContainsCString(infoKeys, kOSBundleCPUSubtypeKey))
{
kernel_mach_header_t *kext_mach_hdr = (kernel_mach_header_t *)
linkedExecutable->getBytesNoCopy();
+#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(
// +1 for slash, +1 for \0
executablePathCStringSize = pathLength + executableRelPath->getLength() + 2;
- executablePathCString = (char *)kalloc((executablePathCStringSize) *
- sizeof(char)); // +1 for \0
+ executablePathCString = (char *)kalloc_tag((executablePathCStringSize) *
+ sizeof(char), VM_KERN_MEMORY_OSKEXT); // +1 for \0
if (!executablePathCString) {
goto finish;
}
executablePathString = OSString::withCString(executablePathCString);
- if (!executablePathCString) {
+ if (!executablePathString) {
goto finish;
}
/*****
* OSKernelResource, OSBundleIsInterface, OSBundlePrelinked, OSBundleStarted.
*/
- if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleUUIDKey)) {
+ if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSKernelResourceKey)) {
result->setObject(kOSKernelResourceKey,
isKernelComponent() ? kOSBooleanTrue : kOSBooleanFalse);
}
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.
*/
- uint64_t loadAddress = 0;
- uint32_t loadSize = 0;
- uint32_t wiredSize = 0;
+ 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 - 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.
*/
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),
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),
while ( (thisMetaClass = OSDynamicCast(OSMetaClass,
metaClassIterator->getNextObject())) ) {
- OSSafeReleaseNULL(metaClassDict);
- OSSafeReleaseNULL(scratchNumber);
- OSSafeReleaseNULL(metaClassName);
- OSSafeReleaseNULL(superclassName);
+ OSSafeReleaseNULL(metaClassDict);
+ OSSafeReleaseNULL(scratchNumber);
+ OSSafeReleaseNULL(metaClassName);
+ OSSafeReleaseNULL(superclassName);
metaClassDict = OSDictionary::withCapacity(3);
if (!metaClassDict) {
success = true;
finish:
- OSSafeRelease(headerData);
- OSSafeRelease(cpuTypeNumber);
- OSSafeRelease(cpuSubtypeNumber);
- OSSafeRelease(executablePathString);
- if (executablePathString) kfree(executablePathCString, executablePathCStringSize);
- 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);
}
return result;
}
-/********************************************************************/
-static struct symtab_command * getKernelSymtab(void)
-{
- struct symtab_command * result = NULL;
- struct load_command * load_cmd = NULL;
- unsigned long i;
-
- load_cmd = (struct load_command *)
- ((uintptr_t)&_mh_execute_header + sizeof(_mh_execute_header));
- for(i = 0; i < _mh_execute_header.ncmds; i++){
- if (load_cmd->cmd == LC_SYMTAB) {
- result = (struct symtab_command *)load_cmd;
- goto finish;
- }
- load_cmd = (struct load_command *)
- ((uintptr_t)load_cmd + load_cmd->cmdsize);
- }
-
-finish:
- return result;
-}
-
-/*********************************************************************
-*********************************************************************/
-/* static */
-OSData *
-OSKext::copySanitizedKernelImage(void)
-{
- OSData * result = NULL;
-
- kernel_mach_header_t * kernelHeader = NULL;
- uint32_t sizeofcmds = 0;
-
- /* These start out pointing to running kernel but
- * after copying point to the copied info.
- */
- kernel_segment_command_t * text_seg = NULL;
- kernel_segment_command_t * data_seg = NULL;
- kernel_segment_command_t * linkedit_seg = NULL;
- struct symtab_command * symtab_cmd = NULL;
- kernel_section_t * text_const_sect = NULL;
- kernel_section_t * data_const_sect = NULL;
-
- kern_return_t kern_result = 0;
- u_long kernelCopyLength = 0;
- vm_offset_t kernelCopyAddr = 0;
- u_char * kernelCopy = NULL;
-
- vm_offset_t contentOffset = 0;
- struct load_command * scan_cmd = NULL;
- kernel_section_t * scan_sect = NULL;
- int64_t stroff_shift = 0;
-
- uint32_t i;
-
- text_seg = getsegbyname("__TEXT");
- data_seg = getsegbyname("__DATA");
- linkedit_seg = getsegbyname("__LINKEDIT");
- symtab_cmd = getKernelSymtab();
-
- text_const_sect = getsectbyname("__TEXT", "__const");
- data_const_sect = getsectbyname("__DATA", "__const");
-
- if (!text_seg || !data_seg || !linkedit_seg || !symtab_cmd ||
- !text_const_sect || ! data_const_sect) {
-
- OSKextLog(/* kext */ NULL,
- kOSKextLogErrorLevel | kOSKextLogIPCFlag,
- "Can't provide kernel image for linking; missing component.");
- goto finish;
- }
-
- /* Figure the size of the kernel image to build. We don't use the sizes of
- * the __TEXT & __DATA segments overall as we only use the __const sections,
- * so add those in manually. We're going to round each part to page size
- * multiples too, just to be extra cautious.
- */
- sizeofcmds = text_seg->cmdsize + data_seg->cmdsize +
- linkedit_seg->cmdsize + symtab_cmd->cmdsize;
- kernelCopyLength = round_page(sizeof(_mh_execute_header) + sizeofcmds) +
- round_page(text_const_sect->size) +
- round_page(data_const_sect->size) +
- round_page(linkedit_seg->filesize);
-
- kern_result = kmem_alloc(kernel_map, &kernelCopyAddr, kernelCopyLength);
- if (kern_result != KERN_SUCCESS) {
- goto finish;
- }
-
- kernelCopy = (u_char *)kernelCopyAddr;
- bzero(kernelCopy, kernelCopyLength); // ??? - is this really necessary?
-
- /*****
- * Copy the kernel Mach header and the load commands we want.
- */
- memcpy(kernelCopy, &_mh_execute_header, sizeof(_mh_execute_header));
- kernelHeader = (kernel_mach_header_t *)kernelCopy;
- kernelHeader->ncmds = 0;
- kernelHeader->sizeofcmds = sizeofcmds;
- contentOffset = round_page(sizeof(_mh_execute_header) + sizeofcmds);
-
- /* __TEXT segment load command and sections.
- * Note that the __TEXT segment's 'offset' and 'filesize' include
- * the data from the beginning of the mach header.
- *
- * Don't muck with the __TEXT segment's vmsize here;
- * user-space linking requires it to match what is in the running kernel.
- * We'll just have to live with it not being accurate
- * (not like we can run the sanitized image after all).
- */
- scan_cmd = (struct load_command *)&kernelHeader[1]; // just past mach header
- memcpy(scan_cmd, text_seg, text_seg->cmdsize);
- kernelHeader->ncmds++;
- text_seg = (kernel_segment_command_t *)scan_cmd; // retarget to constructed segment
- text_seg->fileoff = 0;
- text_seg->filesize = round_page(sizeof(_mh_execute_header) + sizeofcmds);
-
- scan_sect = (kernel_section_t *)(text_seg + 1);
- for (i = 0; i < text_seg->nsects; i++, scan_sect++) {
- if (0 == strncmp("__const", scan_sect->sectname, sizeof("__const"))) {
- text_const_sect = scan_sect; // retarget to constructed section
-
- text_seg->filesize += scan_sect->size;
-
- scan_sect->offset = contentOffset;
- contentOffset += scan_sect->size;
-
- memcpy(kernelCopy + scan_sect->offset, (void *)(uintptr_t)scan_sect->addr,
- scan_sect->size);
- } else {
- scan_sect->addr = 0;
- scan_sect->size = 0;
- scan_sect->offset = contentOffset;
- scan_sect->nreloc = 0;
- }
- }
-
- contentOffset = round_page(contentOffset);
-
- /* __DATA segment load command and sections.
- * Leave the vmsize as in the running kernel here, too.
- */
- scan_cmd = (struct load_command *)((uintptr_t)scan_cmd + scan_cmd->cmdsize);
- memcpy(scan_cmd, data_seg, data_seg->cmdsize);
- kernelHeader->ncmds++;
- data_seg = (kernel_segment_command_t *)scan_cmd; // retarget to constructed segment
- data_seg->fileoff = contentOffset;
- data_seg->filesize = 0;
-
- scan_sect = (kernel_section_t *)(data_seg + 1);
- for (i = 0; i < data_seg->nsects; i++, scan_sect++) {
- if (0 == strncmp("__const", scan_sect->sectname, sizeof("__const"))) {
- data_const_sect = scan_sect; // retarget to constructed section
-
- data_seg->filesize += scan_sect->size;
-
- scan_sect->offset = contentOffset;
- contentOffset += scan_sect->size;
-
- memcpy(kernelCopy + scan_sect->offset, (void *)(uintptr_t)scan_sect->addr,
- scan_sect->size);
- } else {
- scan_sect->addr = 0;
- scan_sect->size = 0;
- scan_sect->offset = contentOffset;
- scan_sect->nreloc = 0;
- }
- }
-
- contentOffset = round_page(contentOffset);
-
- /* __LINKEDIT segment load command.
- * Leave the vmsize as in the running kernel here, too.
- */
- scan_cmd = (struct load_command *)((uintptr_t)scan_cmd + scan_cmd->cmdsize);
- memcpy(scan_cmd, linkedit_seg, linkedit_seg->cmdsize);
- kernelHeader->ncmds++;
- linkedit_seg = (kernel_segment_command_t *)scan_cmd; // retarget to constructed segment
- linkedit_seg->fileoff = contentOffset;
- linkedit_seg->filesize = linkedit_seg->vmsize;
-
- contentOffset += round_page(linkedit_seg->vmsize);
-
- memcpy(kernelCopy + linkedit_seg->fileoff, (void *)(uintptr_t)linkedit_seg->vmaddr,
- linkedit_seg->vmsize);
-
- /* __SYMTAB load command (contents shared with __LINKEDIT).
- */
- scan_cmd = (struct load_command *)((uintptr_t)scan_cmd + scan_cmd->cmdsize);
- memcpy(scan_cmd, symtab_cmd, symtab_cmd->cmdsize);
- kernelHeader->ncmds++;
- symtab_cmd = (struct symtab_command *)scan_cmd; // retarget to constructed cmd
- stroff_shift = symtab_cmd->stroff - symtab_cmd->symoff;
- symtab_cmd->symoff = linkedit_seg->fileoff;
- symtab_cmd->stroff = symtab_cmd->symoff + stroff_shift;
-
- /* Wrap the thing up in an OSData.
- */
- result = OSData::withBytesNoCopy(kernelCopy, kernelCopyLength);
- if (result) {
- result->setDeallocFunction(osdata_kmem_free);
- kernelCopy = NULL;
- }
-
-finish:
- if (kernelCopy) kmem_free(kernel_map, kernelCopyAddr, kernelCopyLength);
-
- return result;
-}
-
/*********************************************************************
-*********************************************************************/
+ *********************************************************************/
/* static */
OSReturn
OSKext::requestResource(
OSKextLog(/* kext */ NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Can't request resource %s for %s - requests to user space are disabled.",
- resourceNameCString,
- kextIdentifierCString);
+ resourceNameCString,
+ kextIdentifierCString);
result = kOSKextReturnDisabled;
goto finish;
}
callbackRecordOut);
finish:
- OSSafeRelease(requestTagNum);
+ OSSafeReleaseNULL(requestTagNum);
return result;
}
return result;
}
+
+/*********************************************************************
+* Busy timeout triage
+*********************************************************************/
+/* static */
+bool
+OSKext::isWaitingKextd(void)
+{
+ return sRequestCallbackRecords && sRequestCallbackRecords->getCount();
+}
+
/*********************************************************************
* Assumes sKextLock is held.
*********************************************************************/
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;
}
{
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;
}
+/*********************************************************************
+ * 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
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(
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;
}
if (allocBuffer) {
kfree(allocBuffer, (length + 1) * sizeof(char));
}
- OSSafeRelease(logString);
- OSSafeRelease(logSpecNum);
+ 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
vm_offset_t * addr,
unsigned int cnt,
int (* printf_func)(const char *fmt, ...),
- bool lockFlag)
+ uint32_t flags)
{
addr64_t summary_page = 0;
addr64_t last_summary_page = 0;
bool found_kmod = false;
u_int i = 0;
- if (lockFlag) {
+ if (kPrintKextsLock & flags) {
+ if (!sKextSummariesLock) return;
IOLockLock(sKextSummariesLock);
}
if (!summary->address) {
continue;
}
-
+
if (!summaryIsInBacktrace(summary, addr, cnt)) {
continue;
}
-
+
if (!found_kmod) {
- (*printf_func)(" Kernel Extensions in backtrace:\n");
+ if (!(kPrintKextsTerse & flags)) {
+ (*printf_func)(" Kernel Extensions in backtrace:\n");
+ }
found_kmod = true;
}
- printSummary(summary, printf_func);
+ printSummary(summary, printf_func, flags);
}
finish:
- if (lockFlag) {
+ if (kPrintKextsLock & flags) {
IOLockUnlock(sKextSummariesLock);
}
- return;
+ return;
+}
+
+/*********************************************************************
+* 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;
+ }
+ }
+
+ return FALSE;
+}
+
+/*
+ * 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
+ }
+
+ /* addr did not map to any kext */
+ return NULL;
}
-/*********************************************************************
-* This function must be safe to call in panic context.
-*********************************************************************/
/* static */
-boolean_t
-OSKext::summaryIsInBacktrace(
- OSKextLoadedKextSummary * summary,
- vm_offset_t * addr,
- unsigned int cnt)
+void *
+OSKext::kextForAddress(const void *addr)
{
- u_int i = 0;
+ void *image = NULL;
- 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 (((vm_offset_t)(uintptr_t)addr >= vm_kernel_stext) &&
+ ((vm_offset_t)(uintptr_t)addr < vm_kernel_etext)) {
+ return (void *)&_mh_execute_header;
+ }
- return FALSE;
+ if (!sKextSummariesLock) {
+ return NULL;
+ }
+ IOLockLock(sKextSummariesLock);
+ OSKextLoadedKextSummary *summary = OSKext::summaryForAddress((uintptr_t)addr);
+ if (summary) {
+ image = (void *)summary->address;
+ }
+ IOLockUnlock(sKextSummariesLock);
+
+ return image;
}
/*********************************************************************
*********************************************************************/
void OSKext::printSummary(
OSKextLoadedKextSummary * summary,
- int (* printf_func)(const char *fmt, ...))
+ 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);
-
- (*printf_func)(" %s(%s)[%s]@0x%llx->0x%llx\n",
+
+ 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,
- summary->address, summary->address + summary->size - 1);
+ tmpAddr, tmpAddr + summary->size - 1);
+
+ if (kPrintKextsTerse & flags) return;
/* print dependency info */
for (kmod_ref = (kmod_reference_t *) summary->reference_list;
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", kmod_ref);
+ "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", rinfo);
+ "due to missing kmod page: %p\n",
+ (kPrintKextsUnslide & flags) ? (void *)VM_KERNEL_UNSLIDE(rinfo) : rinfo);
break;
}
/* 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, rinfo->address);
+ rinfo->name, rinfo->version, uuid, tmpAddr);
}
return;
}
/* 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.
*******************************************************************************/
/* 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) {
OSObject * rawKext = sLoadedKexts->getObject(i);
OSKext * theKext = OSDynamicCast(OSKext, rawKext);
int match;
- char identPlusVers[2*KMOD_MAX_NAME];
uint32_t identPlusVersLength;
-
+ uint32_t tempLen;
+ char identPlusVers[2*KMOD_MAX_NAME];
+
if (!rawKext) {
printf("OSKext::saveLoadedKextPanicListTyped - "
"NULL kext in loaded kext list; continuing\n");
!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;
}
- /* 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;
+ }
+ 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, identPlusVers, list_size);
- *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;
-
- 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:
+ if (newlist) {
+ kfree(newlist, newlist_size);
+ }
return;
}
-
+
/*********************************************************************
* Assumes sKextLock is held.
*********************************************************************/
return; // do not goto finish here b/c of lock
}
- len = assemble_identifier_and_version(kmod_info,
- (isLoading) ? last_loaded_str : last_unloaded_str);
+ 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;
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,
+ last_loaded_strlen, last_loaded_str_buf,
last_loaded_address, last_loaded_size);
}
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,
+ last_unloaded_strlen, last_unloaded_str_buf,
last_unloaded_address, last_unloaded_size);
}
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");
}
size_t summarySize = 0;
size_t size;
u_int count;
- u_int numKexts;
+ u_int maxKexts;
u_int i, j;
+ OSKextActiveAccount * accountingList;
+ OSKextActiveAccount * prevAccountingList;
+ uint32_t idx, accountingListAlloc, accountingListCount, prevAccountingListCount;
+
+ prevAccountingList = NULL;
+ prevAccountingListCount = 0;
+#if DEVELOPMENT || DEBUG
+ if (IORecursiveLockHaveLock(sKextLock) == false) {
+ panic("sKextLock must be held");
+ }
+#endif
+
IOLockLock(sKextSummariesLock);
-
+
count = sLoadedKexts->getCount();
- for (i = 0, numKexts = 0; i < count; ++i) {
+ for (i = 0, maxKexts = 0; i < count; ++i) {
aKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i));
- numKexts += (aKext && aKext->isExecutable());
+ maxKexts += (aKext && aKext->isExecutable());
}
-
- if (!numKexts) goto finish;
-
- /* Calculate the size needed for the new summary headers.
- */
+
+ if (!maxKexts) goto finish;
+ if (maxKexts < kOSKextTypicalLoadCount) maxKexts = kOSKextTypicalLoadCount;
+
+ /* Calculate the size needed for the new summary headers.
+ */
size = sizeof(*gLoadedKextSummaries);
- size += numKexts * sizeof(*gLoadedKextSummaries->summaries);
+ size += maxKexts * sizeof(*gLoadedKextSummaries->summaries);
size = round_page(size);
-
- /* If the previous summary is large enough, use it (and be sure to make
- * it writable). If it's too small, free it and allocate a new buffer.
- */
-
- if (sPrevLoadedKextSummariesAllocSize < size) {
- if (sPrevLoadedKextSummaries) {
- kmem_free(kernel_map, (vm_offset_t)sPrevLoadedKextSummaries,
- sPrevLoadedKextSummariesAllocSize);
- sPrevLoadedKextSummaries = NULL;
- sPrevLoadedKextSummariesAllocSize = 0;
- }
-
- result = kmem_alloc(kernel_map,
- (vm_offset_t*)&summaryHeaderAlloc, 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 = sPrevLoadedKextSummaries;
- summarySize = sPrevLoadedKextSummariesAllocSize;
-
+ }
+ 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);
+ result = vm_map_protect(kernel_map,
+ start,
+ end,
+ VM_PROT_DEFAULT,
+ FALSE);
if (result != KERN_SUCCESS) goto finish;
}
-
- /* Populate the summary header.
- */
-
+
+ /* Populate the summary header.
+ */
+
bzero(summaryHeader, summarySize);
summaryHeader->version = kOSKextLoadedKextSummaryVersion;
summaryHeader->entry_size = sizeof(OSKextLoadedKextSummary);
- summaryHeader->numSummaries = numKexts;
-
- /* Populate each kext summary.
- */
+ /* Populate each kext summary.
+ */
+
count = sLoadedKexts->getCount();
- for (i = 0, j = 0; i < count; ++i) {
+ accountingListAlloc = 0;
+ for (i = 0, j = 0; i < count && j < maxKexts; ++i) {
aKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i));
- if (!aKext || !aKext->isExecutable()) continue;
-
+ if (!aKext || !aKext->isExecutable()) {
+ continue;
+ }
+
aKext->updateLoadedKextSummary(&summaryHeader->summaries[j++]);
+ summaryHeader->numSummaries++;
+ accountingListAlloc++;
}
- /* Write protect the buffer and move it into place.
- */
+ 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;
+ }
+ 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.
+ */
+
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;
- sPrevLoadedKextSummaries = gLoadedKextSummaries;
- sPrevLoadedKextSummariesAllocSize = sLoadedKextSummariesAllocSize;
+ result = vm_map_protect(kernel_map, start, end, VM_PROT_READ, FALSE);
+ if (result != KERN_SUCCESS)
+ goto finish;
gLoadedKextSummaries = summaryHeader;
+ gLoadedKextSummariesTimestamp = mach_absolute_time();
sLoadedKextSummariesAllocSize = summarySize;
-
summaryHeaderAlloc = NULL;
/* Call the magic breakpoint function through a static function pointer so
*/
if (sLoadedKextSummariesUpdated) (*sLoadedKextSummariesUpdated)();
+ IOSimpleLockLock(sKextAccountsLock);
+ prevAccountingList = sKextAccounts;
+ prevAccountingListCount = sKextAccountsCount;
+ sKextAccounts = accountingList;
+ sKextAccountsCount = accountingListCount;
+ IOSimpleLockUnlock(sKextAccountsLock);
+
finish:
IOLockUnlock(sKextSummariesLock);
if (summaryHeaderAlloc) {
kmem_free(kernel_map, (vm_offset_t)summaryHeaderAlloc, summarySize);
}
+ if (prevAccountingList) {
+ IODelete(prevAccountingList, typeof(accountingList[0]), prevAccountingListCount);
+ }
return;
}
uuid = copyUUID();
if (uuid) {
memcpy(summary->uuid, uuid->getBytesNoCopy(), sizeof(summary->uuid));
- OSSafeRelease(uuid);
+ OSSafeReleaseNULL(uuid);
}
summary->address = kmod_info->address;
/*********************************************************************
*********************************************************************/
-#if __i386__
-/* static */
-kern_return_t
-OSKext::getKmodInfo(
- kmod_info_array_t * kmodList,
- mach_msg_type_number_t * kmodCount)
-{
- kern_return_t result = KERN_FAILURE;
- vm_offset_t data = 0;
- 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;
+void
+OSKext::updateActiveAccount(OSKextActiveAccount *accountp)
+{
+ kernel_mach_header_t *hdr = NULL;
+ kernel_segment_command_t *seg = NULL;
- IORecursiveLockLock(sKextLock);
+ hdr = (kernel_mach_header_t *)kmod_info->address;
- k = kmod;
- while (k) {
- size += sizeof(kmod_info_t);
- r = k->reference_list;
- while (r) {
- size +=sizeof(kmod_reference_t);
- r = r->next;
+ 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;
+ }
}
- k = k->next;
- }
- if (!size) {
- result = KERN_SUCCESS;
- goto finish;
- }
-
- result = kmem_alloc(kernel_map, &data, size);
- if (result != KERN_SUCCESS) {
- goto finish;
}
- /* 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;
+ bzero(accountp, sizeof(*accountp));
+ if (seg) {
+ accountp->address = seg->vmaddr;
+ if (accountp->address) {
+ accountp->address_end = seg->vmaddr + seg->vmsize;
}
- kmod_info_scan_ptr++;
- k = k->next;
- }
-
- /* 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++;
+ } else {
+ /* 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;
}
- /* 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;
- }
-
- result = vm_map_copyin(kernel_map, data, size, TRUE, (vm_map_copy_t *)kmodList);
- if (result != KERN_SUCCESS) {
- goto finish;
}
+ accountp->account = this->account;
+}
- *kmodCount = size;
- result = KERN_SUCCESS;
+extern "C" const vm_allocation_site_t *
+OSKextGetAllocationSiteForCaller(uintptr_t address)
+{
+ OSKextActiveAccount * active;
+ vm_allocation_site_t * site;
+ vm_allocation_site_t * releasesite;
-finish:
- IORecursiveLockUnlock(sKextLock);
+ uint32_t baseIdx;
+ uint32_t lim;
- if (result != KERN_SUCCESS && data) {
- kmem_free(kernel_map, data, size);
- *kmodList = (kmod_info_t *)0;
- *kmodCount = 0;
+ 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
}
- return result;
-}
-#endif /* __i386__ */
-#if PRAGMA_MARK
-#pragma mark MAC Framework Support
-#endif
-/*********************************************************************
-*********************************************************************/
-#if CONFIG_MACF_KEXT
-/* MAC Framework support */
-
-/*
- * define IOC_DEBUG to display run-time debugging information
- * #define IOC_DEBUG 1
- */
+ IOSimpleLockUnlock(sKextAccountsLock);
+ if (releasesite) kern_allocation_name_release(releasesite);
-#ifdef IOC_DEBUG
-#define DPRINTF(x) printf x
-#else
-#define IOC_DEBUG
-#define DPRINTF(x)
-#endif
+ return (site);
+}
-/*********************************************************************
-*********************************************************************/
-static bool
-MACFObjectIsPrimitiveType(OSObject * obj)
+extern "C" uint32_t
+OSKextGetKmodIDForSite(const vm_allocation_site_t * site, char * name, vm_size_t namelen)
{
- const OSMetaClass * typeID = NULL; // do not release
+ OSKextAccount * account = (typeof(account)) site;
+ const char * kname;
- typeID = OSTypeIDInst(obj);
- if (typeID == OSTypeID(OSString) || typeID == OSTypeID(OSNumber) ||
- typeID == OSTypeID(OSBoolean) || typeID == OSTypeID(OSData)) {
-
- return true;
+ if (name)
+ {
+ if (account->kext) kname = account->kext->getIdentifierCString();
+ else kname = "<>";
+ strlcpy(name, kname, namelen);
}
- 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 == kOSBooleanTrue) ? sizeof("true") : sizeof("false");
- } else if (typeID == OSTypeID(OSData)) {
- OSData * dataObj = OSDynamicCast(OSData, obj);
- len = dataObj->getLength();
- } else {
- len = 0;
- }
- return len;
+ return (account->loadTag);
}
-/*********************************************************************
-*********************************************************************/
-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 == kOSBooleanTrue) {
- 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);
- }
- return;
+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
+