extern "C" {
#include <kern/clock.h>
#include <kern/host.h>
#include <kern/kext_alloc.h>
#include <kextd/kextd_mach.h>
#include <libkern/kernel_mach_header.h>
#include <libkern/kext_panic_report.h>
#include <libkern/kext_request_keys.h>
#include <libkern/mkext.h>
#include <libkern/prelink.h>
#include <libkern/version.h>
#include <libkern/zlib.h>
#include <mach/host_special_ports.h>
#include <mach/mach_vm.h>
#include <mach/mach_time.h>
#include <sys/sysctl.h>
#include <uuid/uuid.h>
#include <sys/random.h>
};
#include <libkern/OSKextLibPrivate.h>
#include <libkern/c++/OSKext.h>
#include <libkern/c++/OSLib.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOCatalogue.h>
#include <IOKit/IORegistryEntry.h>
#include <IOKit/IOService.h>
#include <IOKit/IOStatisticsPrivate.h>
#if PRAGMA_MARK
#pragma mark External & Internal Function Protos
#endif
extern "C" {
extern int IODTGetLoaderInfo(const char * key, void ** infoAddr, int * infoSize);
extern void IODTFreeLoaderInfo(const char * key, void * infoAddr, int infoSize);
extern void OSRuntimeUnloadCPPForSegment(kernel_segment_command_t * segment);
extern void OSRuntimeUnloadCPP(kmod_info_t * ki, void * data);
extern ppnum_t pmap_find_phys(pmap_t pmap, addr64_t va);
}
static OSReturn _OSKextCreateRequest(
const char * predicate,
OSDictionary ** requestP);
static OSString * _OSKextGetRequestPredicate(OSDictionary * requestDict);
static OSObject * _OSKextGetRequestArgument(
OSDictionary * requestDict,
const char * argName);
static bool _OSKextSetRequestArgument(
OSDictionary * requestDict,
const char * argName,
OSObject * value);
static void * _OSKextExtractPointer(OSData * wrapper);
static OSReturn _OSDictionarySetCStringValue(
OSDictionary * dict,
const char * key,
const char * value);
static bool _OSArrayContainsCString(OSArray * array, const char * cString);
#if CONFIG_MACF_KEXT
static void * MACFCopyModuleDataForKext(
OSKext * theKext,
mach_msg_type_number_t * datalen);
#endif
#if PRAGMA_MARK
#pragma mark Constants & Macros
#endif
#define kOSKextTypicalLoadCount (120)
#define kOSKextMinRetainCount (1)
#define kOSKextMinLoadedRetainCount (2)
#define APPLE_KEXT_PREFIX "com.apple."
#define KERNEL_LIB "com.apple.kernel"
#define PRIVATE_KPI "com.apple.kpi.private"
#define KERNEL6_LIB "com.apple.kernel.6.0"
#define KERNEL6_VERSION "7.9.9"
#define KERNEL_LIB_PREFIX "com.apple.kernel."
#define KPI_LIB_PREFIX "com.apple.kpi."
#define STRING_HAS_PREFIX(s, p) (strncmp((s), (p), strlen(p)) == 0)
#define _kOSKextExecutableKey "_OSKextExecutable"
#define _kOSKextMkextExecutableReferenceKey "_OSKextMkextExecutableReference"
#define _kOSKextExecutableExternalDataKey "_OSKextExecutableExternalData"
#if PRAGMA_MARK
#pragma mark Typedefs
#endif
typedef struct MkextEntryRef {
mkext_basic_header * mkext; void * fileinfo; } MkextEntryRef;
#if PRAGMA_MARK
#pragma mark Global and static Module Variables
#endif
static bool sPrelinkBoot = false;
static bool sSafeBoot = false;
static bool sKeepSymbols = false;
static IORecursiveLock * sKextLock = NULL;
static OSDictionary * sKextsByID = NULL;
static OSArray * sLoadedKexts = NULL;
static OSArray * sUnloadedPrelinkedKexts = NULL;
static OSArray * sKernelRequests = NULL;
static OSSet * sPostedKextLoadIdentifiers = NULL;
static OSArray * sRequestCallbackRecords = NULL;
static OSSet * sAllKextLoadIdentifiers = NULL;
static KXLDContext * sKxldContext = NULL;
static uint32_t sNextLoadTag = 0;
static uint32_t sNextRequestTag = 0;
static bool sUserLoadsActive = false;
static bool sKextdActive = false;
static bool sDeferredLoadSucceeded = false;
static bool sConsiderUnloadsExecuted = false;
#if NO_KEXTD
static bool sKernelRequestsEnabled = false;
#else
static bool sKernelRequestsEnabled = true;
#endif
static bool sLoadEnabled = true;
static bool sUnloadEnabled = true;
static OSKext * sKernelKext = NULL;
kmod_info_t g_kernel_kmod_info = {
0,
KMOD_INFO_VERSION,
0, kOSKextKernelIdentifier, "0", -1, NULL,
(vm_address_t)&_mh_execute_header,
0, 0,
0,
0
};
extern "C" {
kmod_info_t * kmod = NULL;
#define KEXT_PANICLIST_SIZE (2 * PAGE_SIZE)
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 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 u_long last_unloaded_strlen = 0;
static void * last_unloaded_address = NULL;
static u_long last_unloaded_size = 0;
static IORecursiveLock * sKextInnerLock = NULL;
static bool sAutounloadEnabled = true;
static bool sConsiderUnloadsCalled = false;
static bool sConsiderUnloadsPending = false;
static unsigned int sConsiderUnloadDelay = 60; static thread_call_t sUnloadCallout = 0;
static thread_call_t sDestroyLinkContextThread = 0; static bool sSystemSleep = false;
static IOLock * sKextSummariesLock = NULL;
void (*sLoadedKextSummariesUpdated)(void) = OSKextLoadedKextSummariesUpdated;
OSKextLoadedKextSummaryHeader * gLoadedKextSummaries = NULL;
static size_t sLoadedKextSummariesAllocSize = 0;
OSKextLoadedKextSummaryHeader * sPrevLoadedKextSummaries = NULL;
static size_t sPrevLoadedKextSummariesAllocSize = 0;
};
static IOLock * sKextLoggingLock = NULL;
static const OSKextLogSpec kDefaultKernelLogFilter = kOSKextLogBasicLevel |
kOSKextLogVerboseFlagsMask;
static OSKextLogSpec sKernelLogFilter = kDefaultKernelLogFilter;
static bool sBootArgLogFilterFound = false;
SYSCTL_INT(_debug, OID_AUTO, kextlog, CTLFLAG_RW | CTLFLAG_LOCKED, &sKernelLogFilter,
sKernelLogFilter, "kernel kext logging");
static OSKextLogSpec sUserSpaceKextLogFilter = kOSKextLogSilentFilter;
static OSArray * sUserSpaceLogSpecArray = NULL;
static OSArray * sUserSpaceLogMessageArray = NULL;
#if PRAGMA_MARK
#pragma mark OSData callbacks (need to move to OSData)
#endif
extern "C" {
void osdata_kmem_free(void * ptr, unsigned int length) {
kmem_free(kernel_map, (vm_address_t)ptr, length);
return;
}
void osdata_phys_free(void * ptr, unsigned int length) {
ml_static_mfree((vm_offset_t)ptr, length);
return;
}
void osdata_vm_deallocate(void * ptr, unsigned int length)
{
(void)vm_deallocate(kernel_map, (vm_offset_t)ptr, length);
return;
}
void osdata_kext_free(void * ptr, unsigned int length)
{
(void)kext_free((vm_offset_t)ptr, length);
}
};
#if PRAGMA_MARK
#pragma mark KXLD Allocation Callback
#endif
kxld_addr_t
kern_allocate(
u_long size,
KXLDAllocateFlags * flags,
void * user_data)
{
vm_address_t result = 0; kern_return_t mach_result = KERN_FAILURE;
bool success = false;
OSKext * theKext = (OSKext *)user_data;
u_long roundSize = round_page(size);
OSData * linkBuffer = NULL;
mach_result = kext_alloc(&result, roundSize, FALSE);
if (mach_result != KERN_SUCCESS) {
OSKextLog(theKext,
kOSKextLogErrorLevel |
kOSKextLogGeneralFlag,
"Can't allocate kernel memory to link %s.",
theKext->getIdentifierCString());
goto finish;
}
linkBuffer = OSData::withBytesNoCopy((void *)result, roundSize);
if (!linkBuffer) {
OSKextLog(theKext,
kOSKextLogErrorLevel |
kOSKextLogGeneralFlag,
"Can't allocate linked executable wrapper for %s.",
theKext->getIdentifierCString());
goto finish;
}
linkBuffer->setDeallocFunction(osdata_kext_free);
OSKextLog(theKext,
kOSKextLogProgressLevel |
kOSKextLogLoadFlag | kOSKextLogLinkFlag,
"Allocated link buffer for kext %s at %p (%lu bytes).",
theKext->getIdentifierCString(),
(void *)result, (unsigned long)roundSize);
theKext->setLinkedExecutable(linkBuffer);
*flags = kKxldAllocateWritable;
success = true;
finish:
if (!success && result) {
kext_free(result, roundSize);
result = 0;
}
OSSafeRelease(linkBuffer);
return (kxld_addr_t)result;
}
void
kxld_log_callback(
KXLDLogSubsystem subsystem,
KXLDLogLevel level,
const char * format,
va_list argList,
void * user_data)
{
OSKext *theKext = (OSKext *) user_data;
OSKextLogSpec logSpec = 0;
switch (subsystem) {
case kKxldLogLinking:
logSpec |= kOSKextLogLinkFlag;
break;
case kKxldLogPatching:
logSpec |= kOSKextLogPatchFlag;
break;
}
switch (level) {
case kKxldLogExplicit:
logSpec |= kOSKextLogExplicitLevel;
break;
case kKxldLogErr:
logSpec |= kOSKextLogErrorLevel;
break;
case kKxldLogWarn:
logSpec |= kOSKextLogWarningLevel;
break;
case kKxldLogBasic:
logSpec |= kOSKextLogProgressLevel;
break;
case kKxldLogDetail:
logSpec |= kOSKextLogDetailLevel;
break;
case kKxldLogDebug:
logSpec |= kOSKextLogDebugLevel;
break;
}
OSKextVLog(theKext, logSpec, format, argList);
}
#if PRAGMA_MARK
#pragma mark IOStatistics defines
#endif
#if IOKITSTATS
#define notifyKextLoadObservers(kext, kmod_info) \
do { \
IOStatistics::onKextLoad(kext, kmod_info); \
} while (0)
#define notifyKextUnloadObservers(kext) \
do { \
IOStatistics::onKextUnload(kext); \
} while (0)
#define notifyAddClassObservers(kext, addedClass, flags) \
do { \
IOStatistics::onClassAdded(kext, addedClass); \
} while (0)
#define notifyRemoveClassObservers(kext, removedClass, flags) \
do { \
IOStatistics::onClassRemoved(kext, removedClass); \
} while (0)
#else
#define notifyKextLoadObservers(kext, kmod_info)
#define notifyKextUnloadObservers(kext)
#define notifyAddClassObservers(kext, addedClass, flags)
#define notifyRemoveClassObservers(kext, removedClass, flags)
#endif
#if PRAGMA_MARK
#pragma mark Module Config (Startup & Shutdown)
#endif
#define super OSObject
OSDefineMetaClassAndStructors(OSKext, OSObject)
void
OSKext::initialize(void)
{
OSData * kernelExecutable = NULL; u_char * kernelStart = NULL; size_t kernelLength = 0;
OSString * scratchString = NULL; IORegistryEntry * registryRoot = NULL; OSNumber * kernelCPUType = NULL; OSNumber * kernelCPUSubtype = NULL; OSKextLogSpec bootLogFilter = kOSKextLogSilentFilter;
bool setResult = false;
uint64_t * timestamp = 0;
char bootArgBuffer[16];
sKextLock = IORecursiveLockAlloc();
sKextInnerLock = IORecursiveLockAlloc();
sKextSummariesLock = IOLockAlloc();
sKextLoggingLock = IOLockAlloc();
assert(sKextLock);
assert(sKextInnerLock);
assert(sKextSummariesLock);
assert(sKextLoggingLock);
sKextsByID = OSDictionary::withCapacity(kOSKextTypicalLoadCount);
sLoadedKexts = OSArray::withCapacity(kOSKextTypicalLoadCount);
sUnloadedPrelinkedKexts = OSArray::withCapacity(kOSKextTypicalLoadCount / 10);
sKernelRequests = OSArray::withCapacity(0);
sPostedKextLoadIdentifiers = OSSet::withCapacity(0);
sAllKextLoadIdentifiers = OSSet::withCapacity(kOSKextTypicalLoadCount);
sRequestCallbackRecords = OSArray::withCapacity(0);
assert(sKextsByID && sLoadedKexts && sKernelRequests &&
sPostedKextLoadIdentifiers && sAllKextLoadIdentifiers &&
sRequestCallbackRecords && sUnloadedPrelinkedKexts);
if (PE_parse_boot_argn("kextlog", &bootLogFilter, sizeof("kextlog=0x00000000 "))) {
sBootArgLogFilterFound = true;
sKernelLogFilter = bootLogFilter;
OSKextLog( NULL,
kOSKextLogBasicLevel |
kOSKextLogFlagsMask,
"Kernel kext log filter 0x%x per kextlog boot arg.",
(unsigned)sKernelLogFilter);
}
sSafeBoot = PE_parse_boot_argn("-x", bootArgBuffer,
sizeof(bootArgBuffer)) ? true : false;
if (sSafeBoot) {
OSKextLog( NULL,
kOSKextLogWarningLevel |
kOSKextLogGeneralFlag,
"SAFE BOOT DETECTED - "
"only valid OSBundleRequired kexts will be loaded.");
}
PE_parse_boot_argn("keepsyms", &sKeepSymbols, sizeof(sKeepSymbols));
sKernelKext = new OSKext;
assert(sKernelKext);
kernelStart = (u_char *)&_mh_execute_header;
kernelLength = getlastaddr() - (vm_offset_t)kernelStart;
kernelExecutable = OSData::withBytesNoCopy(
kernelStart, kernelLength);
assert(kernelExecutable);
sKernelKext->loadTag = sNextLoadTag++; sKernelKext->bundleID = OSSymbol::withCString(kOSKextKernelIdentifier);
sKernelKext->version = OSKextParseVersionString(osrelease);
sKernelKext->compatibleVersion = sKernelKext->version;
sKernelKext->linkedExecutable = kernelExecutable;
sKernelKext->flags.hasAllDependencies = 1;
sKernelKext->flags.kernelComponent = 1;
sKernelKext->flags.prelinked = 0;
sKernelKext->flags.loaded = 1;
sKernelKext->flags.started = 1;
sKernelKext->flags.CPPInitialized = 0;
sKernelKext->kmod_info = &g_kernel_kmod_info;
strlcpy(g_kernel_kmod_info.version, osrelease,
sizeof(g_kernel_kmod_info.version));
g_kernel_kmod_info.size = kernelLength;
g_kernel_kmod_info.id = sKernelKext->loadTag;
sKernelKext->infoDict = OSDictionary::withCapacity(5);
assert(sKernelKext->infoDict);
setResult = sKernelKext->infoDict->setObject(kCFBundleIdentifierKey,
sKernelKext->bundleID);
assert(setResult);
setResult = sKernelKext->infoDict->setObject(kOSKernelResourceKey,
kOSBooleanTrue);
assert(setResult);
scratchString = OSString::withCStringNoCopy(osrelease);
assert(scratchString);
setResult = sKernelKext->infoDict->setObject(kCFBundleVersionKey,
scratchString);
assert(setResult);
OSSafeReleaseNULL(scratchString);
scratchString = OSString::withCStringNoCopy("mach_kernel");
assert(scratchString);
setResult = sKernelKext->infoDict->setObject(kCFBundleNameKey,
scratchString);
assert(setResult);
OSSafeReleaseNULL(scratchString);
setResult = sKextsByID->setObject(sKernelKext->bundleID, sKernelKext);
assert(setResult);
setResult = sLoadedKexts->setObject(sKernelKext);
assert(setResult);
sKernelKext->release();
registryRoot = IORegistryEntry::getRegistryRoot();
kernelCPUType = OSNumber::withNumber(
(long long unsigned int)_mh_execute_header.cputype,
8 * sizeof(_mh_execute_header.cputype));
kernelCPUSubtype = OSNumber::withNumber(
(long long unsigned int)_mh_execute_header.cpusubtype,
8 * sizeof(_mh_execute_header.cpusubtype));
assert(registryRoot && kernelCPUSubtype && kernelCPUType);
registryRoot->setProperty(kOSKernelCPUTypeKey, kernelCPUType);
registryRoot->setProperty(kOSKernelCPUSubtypeKey, kernelCPUSubtype);
OSSafeRelease(kernelCPUType);
OSSafeRelease(kernelCPUSubtype);
timestamp = __OSAbsoluteTimePtr(&last_loaded_timestamp);
*timestamp = 0;
timestamp = __OSAbsoluteTimePtr(&last_unloaded_timestamp);
*timestamp = 0;
OSKextLog( NULL,
kOSKextLogProgressLevel |
kOSKextLogGeneralFlag,
"Kext system initialized.");
notifyKextLoadObservers(sKernelKext, sKernelKext->kmod_info);
return;
}
OSReturn
OSKext::removeKextBootstrap(void)
{
OSReturn result = kOSReturnError;
static bool alreadyDone = false;
const char * dt_kernel_header_name = "Kernel-__HEADER";
const char * dt_kernel_symtab_name = "Kernel-__SYMTAB";
kernel_mach_header_t * dt_mach_header = NULL;
int dt_mach_header_size = 0;
struct symtab_command * dt_symtab = NULL;
int dt_symtab_size = 0;
int dt_result = 0;
kernel_segment_command_t * seg_to_remove = NULL;
IORecursiveLockLock(sKextLock);
if (alreadyDone) {
result = kOSReturnSuccess;
goto finish;
}
OSKextLog( NULL,
kOSKextLogProgressLevel |
kOSKextLogGeneralFlag,
"Jettisoning kext bootstrap segments.");
dt_result = IODTGetLoaderInfo(dt_kernel_header_name,
(void **)&dt_mach_header, &dt_mach_header_size);
if (dt_result == 0 && dt_mach_header) {
IODTFreeLoaderInfo(dt_kernel_header_name, (void *)dt_mach_header,
round_page_32(dt_mach_header_size));
}
dt_result = IODTGetLoaderInfo(dt_kernel_symtab_name,
(void **)&dt_symtab, &dt_symtab_size);
if (dt_result == 0 && dt_symtab) {
IODTFreeLoaderInfo(dt_kernel_symtab_name, (void *)dt_symtab,
round_page_32(dt_symtab_size));
}
seg_to_remove = getsegbyname("__KLD");
if (seg_to_remove) {
OSRuntimeUnloadCPPForSegment(seg_to_remove);
}
#if __i386__ || __x86_64__
if (seg_to_remove && seg_to_remove->vmaddr && seg_to_remove->vmsize) {
read_random((void *) seg_to_remove->vmaddr, seg_to_remove->vmsize);
ml_static_mfree(seg_to_remove->vmaddr, seg_to_remove->vmsize);
}
#else
#error arch
#endif
seg_to_remove = NULL;
kernel_section_t * sect;
sect = getsectbyname("__PRELINK", "__symtab");
if (sect && sect->addr && sect->size) {
ml_static_mfree(sect->addr, sect->size);
}
seg_to_remove = (kernel_segment_command_t *)getsegbyname("__LINKEDIT");
#if CONFIG_KXLD
if (!sKeepSymbols) {
kern_return_t mem_result;
void *seg_copy = NULL;
void *seg_data = NULL;
vm_map_offset_t seg_offset = 0;
vm_map_offset_t seg_copy_offset = 0;
vm_map_size_t seg_length = 0;
seg_data = (void *) seg_to_remove->vmaddr;
seg_offset = (vm_map_offset_t) seg_to_remove->vmaddr;
seg_length = (vm_map_size_t) seg_to_remove->vmsize;
mem_result = kmem_alloc(kernel_map, (vm_offset_t *) &seg_copy,
seg_length);
if (mem_result != KERN_SUCCESS) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogGeneralFlag | kOSKextLogArchiveFlag,
"Can't copy __LINKEDIT segment for VM reassign.");
goto finish;
}
seg_copy_offset = (vm_map_offset_t) seg_copy;
memcpy(seg_copy, seg_data, seg_length);
ml_static_mfree(seg_offset, seg_length);
mem_result = vm_map_enter_mem_object(
kernel_map,
&seg_offset,
seg_length, 0,
VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE,
(ipc_port_t)NULL,
(vm_object_offset_t) 0,
FALSE,
VM_PROT_ALL,
VM_PROT_ALL,
VM_INHERIT_DEFAULT);
if ((mem_result != KERN_SUCCESS) ||
(seg_offset != (vm_map_offset_t) seg_data))
{
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogGeneralFlag | kOSKextLogArchiveFlag,
"Can't create __LINKEDIT VM entry at %p, length 0x%llx (error 0x%x).",
seg_data, seg_length, mem_result);
goto finish;
}
memcpy(seg_data, seg_copy, seg_length);
kmem_free(kernel_map, seg_copy_offset, seg_length);
}
#else
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);
}
#else
#error arch
#endif
} else {
OSKextLog( NULL,
kOSKextLogBasicLevel |
kOSKextLogGeneralFlag,
"keepsyms boot arg specified; keeping linkedit segment for symbols.");
}
#endif
seg_to_remove = NULL;
alreadyDone = true;
result = kOSReturnSuccess;
finish:
IORecursiveLockUnlock(sKextLock);
return result;
}
void
OSKext::flushNonloadedKexts(
Boolean flushPrelinkedKexts)
{
OSSet * prelinkedKexts = NULL; OSCollectionIterator * kextIterator = NULL; OSCollectionIterator * prelinkIterator = NULL; const OSSymbol * thisID = NULL; OSKext * thisKext = NULL; uint32_t count, i;
IORecursiveLockLock(sKextLock);
OSKextLog( NULL,
kOSKextLogProgressLevel |
kOSKextLogKextBookkeepingFlag,
"Flushing nonloaded kexts and other unused data.");
OSKext::considerDestroyingLinkContext();
if (!flushPrelinkedKexts) {
prelinkedKexts = OSSet::withCapacity(0);
if (!prelinkedKexts) {
goto finish;
}
}
kextIterator = OSCollectionIterator::withCollection(sKextsByID);
if (!kextIterator) {
goto finish;
}
while ((thisID = OSDynamicCast(OSSymbol,
kextIterator->getNextObject()))) {
thisKext = OSDynamicCast(OSKext, sKextsByID->getObject(thisID));
if (thisKext) {
if (prelinkedKexts && thisKext->isPrelinked()) {
prelinkedKexts->setObject(thisKext);
}
thisKext->flushDependencies( false);
}
}
sKextsByID->flushCollection();
count = sLoadedKexts->getCount();
for (i = 0; i < count; i++) {
thisKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i));
sKextsByID->setObject(thisKext->getIdentifierCString(), thisKext);
}
if (prelinkedKexts) {
prelinkIterator = OSCollectionIterator::withCollection(prelinkedKexts);
if (!prelinkIterator) {
goto finish;
}
while ((thisKext = OSDynamicCast(OSKext,
prelinkIterator->getNextObject()))) {
sKextsByID->setObject(thisKext->getIdentifierCString(),
thisKext);
}
}
finish:
IORecursiveLockUnlock(sKextLock);
OSSafeRelease(prelinkedKexts);
OSSafeRelease(kextIterator);
OSSafeRelease(prelinkIterator);
return;
}
void
OSKext::setKextdActive(Boolean active)
{
IORecursiveLockLock(sKextLock);
sKextdActive = active;
if (sKernelRequests->getCount()) {
OSKext::pingKextd();
}
IORecursiveLockUnlock(sKextLock);
return;
}
extern "C" {
extern void ipc_port_release_send(ipc_port_t);
};
OSReturn
OSKext::pingKextd(void)
{
OSReturn result = kOSReturnError;
#if !NO_KEXTD
mach_port_t kextd_port = IPC_PORT_NULL;
if (!sKextdActive) {
result = kOSKextReturnDisabled; goto finish;
}
result = host_get_kextd_port(host_priv_self(), &kextd_port);
if (result != KERN_SUCCESS || !IPC_PORT_VALID(kextd_port)) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogIPCFlag,
"Can't get kextd port.");
goto finish;
}
result = kextd_ping(kextd_port);
if (result != KERN_SUCCESS) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogIPCFlag,
"kextd ping failed (0x%x).", (int)result);
goto finish;
}
finish:
if (IPC_PORT_VALID(kextd_port)) {
ipc_port_release_send(kextd_port);
}
#endif
return result;
}
void
OSKext::setDeferredLoadSucceeded(Boolean succeeded)
{
IORecursiveLockLock(sKextLock);
sDeferredLoadSucceeded = succeeded;
IORecursiveLockUnlock(sKextLock);
return;
}
void
OSKext::willShutdown(void)
{
#if !NO_KEXTD
OSReturn checkResult = kOSReturnError;
#endif
OSDictionary * exitRequest = NULL;
IORecursiveLockLock(sKextLock);
OSKext::setLoadEnabled(false);
OSKext::setUnloadEnabled(false);
OSKext::setAutounloadsEnabled(false);
OSKext::setKernelRequestsEnabled(false);
#if !NO_KEXTD
OSKextLog( NULL,
kOSKextLogProgressLevel |
kOSKextLogGeneralFlag,
"System shutdown; requesting immediate kextd exit.");
checkResult = _OSKextCreateRequest(kKextRequestPredicateRequestKextdExit,
&exitRequest);
if (checkResult != kOSReturnSuccess) {
goto finish;
}
if (!sKernelRequests->setObject(exitRequest)) {
goto finish;
}
OSKext::pingKextd();
finish:
#endif
IORecursiveLockUnlock(sKextLock);
OSSafeRelease(exitRequest);
return;
}
bool
OSKext::getLoadEnabled(void)
{
bool result;
IORecursiveLockLock(sKextLock);
result = sLoadEnabled;
IORecursiveLockUnlock(sKextLock);
return result;
}
bool
OSKext::setLoadEnabled(bool flag)
{
bool result;
IORecursiveLockLock(sKextLock);
result = sLoadEnabled;
sLoadEnabled = (flag ? true : false);
if (sLoadEnabled != result) {
OSKextLog( NULL,
kOSKextLogBasicLevel |
kOSKextLogLoadFlag,
"Kext loading now %sabled.", sLoadEnabled ? "en" : "dis");
}
IORecursiveLockUnlock(sKextLock);
return result;
}
bool
OSKext::getUnloadEnabled(void)
{
bool result;
IORecursiveLockLock(sKextLock);
result = sUnloadEnabled;
IORecursiveLockUnlock(sKextLock);
return result;
}
bool
OSKext::setUnloadEnabled(bool flag)
{
bool result;
IORecursiveLockLock(sKextLock);
result = sUnloadEnabled;
sUnloadEnabled = (flag ? true : false);
IORecursiveLockUnlock(sKextLock);
if (sUnloadEnabled != result) {
OSKextLog( NULL,
kOSKextLogBasicLevel |
kOSKextLogGeneralFlag | kOSKextLogLoadFlag,
"Kext unloading now %sabled.", sUnloadEnabled ? "en" : "dis");
}
return result;
}
bool
OSKext::getAutounloadEnabled(void)
{
bool result;
IORecursiveLockLock(sKextInnerLock);
result = sAutounloadEnabled ? true : false;
IORecursiveLockUnlock(sKextInnerLock);
return result;
}
bool
OSKext::setAutounloadsEnabled(bool flag)
{
bool result;
IORecursiveLockLock(sKextInnerLock);
result = sAutounloadEnabled;
sAutounloadEnabled = (flag ? true : false);
if (!sAutounloadEnabled && sUnloadCallout) {
thread_call_cancel(sUnloadCallout);
}
if (sAutounloadEnabled != result) {
OSKextLog( NULL,
kOSKextLogBasicLevel |
kOSKextLogGeneralFlag | kOSKextLogLoadFlag,
"Kext autounloading now %sabled.",
sAutounloadEnabled ? "en" : "dis");
}
IORecursiveLockUnlock(sKextInnerLock);
return result;
}
bool
OSKext::setAutounloadEnabled(bool flag)
{
bool result = flags.autounloadEnabled ? true : false;
flags.autounloadEnabled = flag ? 1 : 0;
if (result != (flag ? true : false)) {
OSKextLog(this,
kOSKextLogProgressLevel |
kOSKextLogLoadFlag | kOSKextLogKextBookkeepingFlag,
"Autounloading for kext %s now %sabled.",
getIdentifierCString(),
flags.autounloadEnabled ? "en" : "dis");
}
return result;
}
bool
OSKext::setKernelRequestsEnabled(bool flag)
{
bool result;
IORecursiveLockLock(sKextLock);
result = sKernelRequestsEnabled;
sKernelRequestsEnabled = flag ? true : false;
if (sKernelRequestsEnabled != result) {
OSKextLog( NULL,
kOSKextLogBasicLevel |
kOSKextLogGeneralFlag,
"Kernel requests now %sabled.",
sKernelRequestsEnabled ? "en" : "dis");
}
IORecursiveLockUnlock(sKextLock);
return result;
}
bool
OSKext::getKernelRequestsEnabled(void)
{
bool result;
IORecursiveLockLock(sKextLock);
result = sKernelRequestsEnabled;
IORecursiveLockUnlock(sKextLock);
return result;
}
#if PRAGMA_MARK
#pragma mark Kext Life Cycle
#endif
OSKext *
OSKext::withPrelinkedInfoDict(
OSDictionary * anInfoDict)
{
OSKext * newKext = new OSKext;
if (newKext && !newKext->initWithPrelinkedInfoDict(anInfoDict)) {
newKext->release();
return NULL;
}
return newKext;
}
bool
OSKext::initWithPrelinkedInfoDict(
OSDictionary * anInfoDict)
{
bool result = false;
OSString * kextPath = NULL; OSNumber * addressNum = NULL; OSNumber * lengthNum = NULL; void * data = NULL; void * srcData = NULL; OSData * prelinkedExecutable = NULL; uint32_t length = 0;
if (!super::init()) {
goto finish;
}
kextPath = OSDynamicCast(OSString,
anInfoDict->getObject(kPrelinkBundlePathKey));
if (!setInfoDictionaryAndPath(anInfoDict, kextPath)) {
goto finish;
}
executableRelPath = OSDynamicCast(OSString,
anInfoDict->getObject(kPrelinkExecutableRelativePathKey));
if (executableRelPath) {
executableRelPath->retain();
}
anInfoDict->removeObject(kPrelinkBundlePathKey);
anInfoDict->removeObject(kPrelinkExecutableRelativePathKey);
addressNum = OSDynamicCast(OSNumber,
anInfoDict->getObject(kPrelinkExecutableLoadKey));
if (addressNum) {
lengthNum = OSDynamicCast(OSNumber,
anInfoDict->getObject(kPrelinkExecutableSizeKey));
if (!lengthNum) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
"Kext %s can't find prelinked kext executable size.",
getIdentifierCString());
goto finish;
}
data = (void *) (intptr_t) (addressNum->unsigned64BitValue());
length = (uint32_t) (lengthNum->unsigned32BitValue());
anInfoDict->removeObject(kPrelinkExecutableLoadKey);
anInfoDict->removeObject(kPrelinkExecutableSizeKey);
addressNum = OSDynamicCast(OSNumber, anInfoDict->getObject(kPrelinkExecutableSourceKey));
if (addressNum) {
srcData = (void *) (intptr_t) (addressNum->unsigned64BitValue());
if (data != srcData) {
#if __LP64__
kern_return_t alloc_result;
alloc_result = kext_alloc((vm_offset_t *)&data, length, TRUE);
if (alloc_result != KERN_SUCCESS) {
OSKextLog(this,
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());
goto finish;
#endif
}
anInfoDict->removeObject(kPrelinkExecutableSourceKey);
}
prelinkedExecutable = OSData::withBytesNoCopy(data, length);
if (!prelinkedExecutable) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogGeneralFlag | kOSKextLogArchiveFlag,
"Kext %s failed to create executable wrapper.",
getIdentifierCString());
goto finish;
}
prelinkedExecutable->setDeallocFunction(osdata_kext_free);
setLinkedExecutable(prelinkedExecutable);
addressNum = OSDynamicCast(OSNumber,
anInfoDict->getObject(kPrelinkKmodInfoKey));
if (!addressNum) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
"Kext %s can't find prelinked kext kmod_info address.",
getIdentifierCString());
goto finish;
}
kmod_info = (kmod_info_t *) (intptr_t) (addressNum->unsigned64BitValue());
anInfoDict->removeObject(kPrelinkKmodInfoKey);
}
if (isInterface()) {
interfaceUUID = OSDynamicCast(OSData,
anInfoDict->getObject(kPrelinkInterfaceUUIDKey));
if (interfaceUUID) {
interfaceUUID->retain();
anInfoDict->removeObject(kPrelinkInterfaceUUIDKey);
}
}
flags.prelinked = true;
sPrelinkBoot = true;
result = registerIdentifier();
finish:
OSSafeRelease(prelinkedExecutable);
return result;
}
OSKext *
OSKext::withBooterData(
OSString * deviceTreeName,
OSData * booterData)
{
OSKext * newKext = new OSKext;
if (newKext && !newKext->initWithBooterData(deviceTreeName, booterData)) {
newKext->release();
return NULL;
}
return newKext;
}
typedef struct _BooterKextFileInfo {
uint32_t infoDictPhysAddr;
uint32_t infoDictLength;
uint32_t executablePhysAddr;
uint32_t executableLength;
uint32_t bundlePathPhysAddr;
uint32_t bundlePathLength;
} _BooterKextFileInfo;
bool
OSKext::initWithBooterData(
OSString * deviceTreeName,
OSData * booterData)
{
bool result = false;
_BooterKextFileInfo * kextFileInfo = NULL; char * infoDictAddr = NULL; void * executableAddr = NULL; char * bundlePathAddr = NULL;
OSObject * parsedXML = NULL; OSDictionary * theInfoDict = NULL; OSString * kextPath = NULL; OSString * errorString = NULL; OSData * executable = NULL;
if (!super::init()) {
goto finish;
}
kextFileInfo = (_BooterKextFileInfo *)booterData->getBytesNoCopy();
if (!kextFileInfo) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogGeneralFlag,
"No booter-provided data for kext device tree entry %s.",
deviceTreeName->getCStringNoCopy());
goto finish;
}
if (!kextFileInfo->infoDictPhysAddr || !kextFileInfo->infoDictLength) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogGeneralFlag,
"No kext info dictionary for booter device tree entry %s.",
deviceTreeName->getCStringNoCopy());
goto finish;
}
infoDictAddr = (char *)ml_static_ptovirt(kextFileInfo->infoDictPhysAddr);
if (!infoDictAddr) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogGeneralFlag,
"Can't translate physical address 0x%x of kext info dictionary "
"for device tree entry %s.",
(int)kextFileInfo->infoDictPhysAddr,
deviceTreeName->getCStringNoCopy());
goto finish;
}
parsedXML = OSUnserializeXML(infoDictAddr, &errorString);
if (parsedXML) {
theInfoDict = OSDynamicCast(OSDictionary, parsedXML);
}
if (!theInfoDict) {
const char * errorCString = "(unknown error)";
if (errorString && errorString->getCStringNoCopy()) {
errorCString = errorString->getCStringNoCopy();
} else if (parsedXML) {
errorCString = "not a dictionary";
}
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogGeneralFlag,
"Error unserializing info dictionary for device tree entry %s: %s.",
deviceTreeName->getCStringNoCopy(), errorCString);
goto finish;
}
if (kextFileInfo->bundlePathPhysAddr && kextFileInfo->bundlePathLength) {
bundlePathAddr = (char *)ml_static_ptovirt(kextFileInfo->bundlePathPhysAddr);
if (!bundlePathAddr) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogGeneralFlag,
"Can't translate physical address 0x%x of kext bundle path "
"for device tree entry %s.",
(int)kextFileInfo->bundlePathPhysAddr,
deviceTreeName->getCStringNoCopy());
goto finish;
}
bundlePathAddr[kextFileInfo->bundlePathLength-1] = '\0';
kextPath = OSString::withCString(bundlePathAddr);
if (!kextPath) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogGeneralFlag,
"Failed to create wrapper for device tree entry %s kext path %s.",
deviceTreeName->getCStringNoCopy(), bundlePathAddr);
goto finish;
}
}
if (!setInfoDictionaryAndPath(theInfoDict, kextPath)) {
goto finish;
}
if (kextFileInfo->executablePhysAddr && kextFileInfo->executableLength) {
executableAddr = (void *)ml_static_ptovirt(kextFileInfo->executablePhysAddr);
if (!executableAddr) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogGeneralFlag,
"Can't translate physical address 0x%x of kext executable "
"for device tree entry %s.",
(int)kextFileInfo->executablePhysAddr,
deviceTreeName->getCStringNoCopy());
goto finish;
}
executable = OSData::withBytesNoCopy(executableAddr,
kextFileInfo->executableLength);
if (!executable) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogGeneralFlag,
"Failed to create executable wrapper for device tree entry %s.",
deviceTreeName->getCStringNoCopy());
goto finish;
}
if (!setExecutable(executable, booterData)) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogGeneralFlag,
"Failed to set kext executable for device tree entry %s.",
deviceTreeName->getCStringNoCopy());
goto finish;
}
}
result = registerIdentifier();
finish:
OSSafeRelease(parsedXML);
OSSafeRelease(kextPath);
OSSafeRelease(errorString);
OSSafeRelease(executable);
return result;
}
bool
OSKext::registerIdentifier(void)
{
bool result = false;
OSKext * existingKext = NULL; bool existingIsLoaded = false;
bool existingIsPrelinked = false;
OSKextVersion newVersion = -1;
OSKextVersion existingVersion = -1;
char newVersionCString[kOSKextVersionMaxLength];
char existingVersionCString[kOSKextVersionMaxLength];
OSData * newUUID = NULL; OSData * existingUUID = NULL;
IORecursiveLockLock(sKextLock);
newVersion = getVersion();
OSKextVersionGetString(newVersion, newVersionCString,
kOSKextVersionMaxLength);
existingKext = OSDynamicCast(OSKext, sKextsByID->getObject(bundleID));
if (!existingKext) {
sKextsByID->setObject(bundleID, this);
result = true;
goto finish;
}
existingVersion = existingKext->getVersion();
OSKextVersionGetString(existingVersion,
existingVersionCString, kOSKextVersionMaxLength);
existingIsLoaded = existingKext->isLoaded();
existingIsPrelinked = existingKext->isPrelinked();
if (existingIsLoaded || existingIsPrelinked) {
bool sameVersion = (newVersion == existingVersion);
bool sameExecutable = true;
newUUID = copyUUID();
existingUUID = existingKext->copyUUID();
if (newUUID && existingUUID) {
sameExecutable = newUUID->isEqualTo(existingUUID);
} else if (newUUID || existingUUID) {
sameExecutable = false;
}
if (!newUUID && !existingUUID) {
OSKextVersionGetString(version, newVersionCString,
sizeof(newVersionCString));
OSKextLog(this,
kOSKextLogWarningLevel |
kOSKextLogKextBookkeepingFlag,
"Notice - new kext %s, v%s matches %s kext "
"but can't determine if executables are the same (no UUIDs).",
getIdentifierCString(),
newVersionCString,
(existingIsLoaded ? "loaded" : "prelinked"));
}
if (sameVersion && sameExecutable) {
OSKextLog(this,
(existingIsLoaded ? kOSKextLogWarningLevel : kOSKextLogStepLevel) |
kOSKextLogKextBookkeepingFlag,
"Refusing new kext %s, v%s: a %s copy is already present "
"(same version and executable).",
getIdentifierCString(), newVersionCString,
(existingIsLoaded ? "loaded" : "prelinked"));
} else {
if (!sameVersion) {
OSKextLog(this,
kOSKextLogWarningLevel |
kOSKextLogKextBookkeepingFlag,
"Refusing new kext %s, v%s: already have %s v%s.",
getIdentifierCString(),
newVersionCString,
(existingIsLoaded ? "loaded" : "prelinked"),
existingVersionCString);
} else {
OSKextLog(this,
kOSKextLogWarningLevel | kOSKextLogKextBookkeepingFlag,
"Refusing new kext %s, v%s: a %s copy with a different "
"executable UUID is already present.",
getIdentifierCString(), newVersionCString,
(existingIsLoaded ? "loaded" : "prelinked"));
}
}
goto finish;
}
if (sUserLoadsActive) {
sKextsByID->setObject(bundleID, this);
result = true;
OSKextLog(this,
kOSKextLogStepLevel |
kOSKextLogKextBookkeepingFlag,
"Dropping old copy of kext %s (v%s) for newly-added (v%s).",
getIdentifierCString(),
existingVersionCString,
newVersionCString);
goto finish;
}
if (newVersion > existingVersion) {
sKextsByID->setObject(bundleID, this);
result = true;
OSKextLog(this,
kOSKextLogStepLevel |
kOSKextLogKextBookkeepingFlag,
"Dropping lower version (v%s) of registered kext %s for higher (v%s).",
existingVersionCString,
getIdentifierCString(),
newVersionCString);
} else {
OSKextLog(this,
kOSKextLogStepLevel |
kOSKextLogKextBookkeepingFlag,
"Kext %s is already registered with a higher/same version (v%s); "
"dropping newly-added (v%s).",
getIdentifierCString(),
existingVersionCString,
newVersionCString);
}
finish:
IORecursiveLockUnlock(sKextLock);
if (result) {
OSKextLog(this,
kOSKextLogStepLevel |
kOSKextLogKextBookkeepingFlag,
"Kext %s, v%s registered and available for loading.",
getIdentifierCString(), newVersionCString);
}
OSSafeRelease(newUUID);
OSSafeRelease(existingUUID);
return result;
}
bool
OSKext::setInfoDictionaryAndPath(
OSDictionary * aDictionary,
OSString * aPath)
{
bool result = false;
OSString * bundleIDString = NULL; OSString * versionString = NULL; OSString * compatibleVersionString = NULL; const char * versionCString = NULL; const char * compatibleVersionCString = NULL; OSBoolean * scratchBool = NULL; OSDictionary * scratchDict = NULL;
if (infoDict) {
panic("Attempt to set info dictionary on a kext "
"that already has one (%s).",
getIdentifierCString());
}
if (!aDictionary || !OSDynamicCast(OSDictionary, aDictionary)) {
goto finish;
}
infoDict = aDictionary;
infoDict->retain();
scratchBool = OSDynamicCast(OSBoolean,
getPropertyForHostArch(kOSBundleEnableKextLoggingKey));
if (scratchBool == kOSBooleanTrue) {
flags.loggingEnabled = 1;
}
bundleIDString = OSDynamicCast(OSString,
getPropertyForHostArch(kCFBundleIdentifierKey));
if (!bundleIDString) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogValidationFlag,
"CFBundleIdentifier missing/invalid type in kext %s.",
aPath ? aPath->getCStringNoCopy() : "(unknown)");
goto finish;
}
bundleID = OSSymbol::withString(bundleIDString);
if (!bundleID) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogValidationFlag,
"Can't copy bundle identifier as symbol for kext %s.",
bundleIDString->getCStringNoCopy());
goto finish;
}
if (aPath) {
path = aPath;
path->retain();
}
if (bundleID->getLength() >= KMOD_MAX_NAME) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogValidationFlag,
"Kext %s error - CFBundleIdentifier over max length %d.",
getIdentifierCString(), KMOD_MAX_NAME - 1);
goto finish;
}
version = compatibleVersion = -1;
versionString = OSDynamicCast(OSString,
getPropertyForHostArch(kCFBundleVersionKey));
if (!versionString) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogValidationFlag,
"Kext %s error - CFBundleVersion missing/invalid type.",
getIdentifierCString());
goto finish;
}
versionCString = versionString->getCStringNoCopy();
version = OSKextParseVersionString(versionCString);
if (version < 0) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogValidationFlag,
"Kext %s error - CFBundleVersion bad value '%s'.",
getIdentifierCString(), versionCString);
goto finish;
}
compatibleVersion = -1;
compatibleVersionString = OSDynamicCast(OSString,
getPropertyForHostArch(kOSBundleCompatibleVersionKey));
if (compatibleVersionString) {
compatibleVersionCString = compatibleVersionString->getCStringNoCopy();
compatibleVersion = OSKextParseVersionString(compatibleVersionCString);
if (compatibleVersion < 0) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogValidationFlag,
"Kext %s error - OSBundleCompatibleVersion bad value '%s'.",
getIdentifierCString(), compatibleVersionCString);
goto finish;
}
if (compatibleVersion > version) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogValidationFlag,
"Kext %s error - %s %s > %s %s (must be <=).",
getIdentifierCString(),
kOSBundleCompatibleVersionKey, compatibleVersionCString,
kCFBundleVersionKey, versionCString);
goto finish;
}
}
scratchBool = OSDynamicCast(OSBoolean,
getPropertyForHostArch(kOSBundleIsInterfaceKey));
if (scratchBool == kOSBooleanTrue) {
flags.interface = 1;
}
scratchBool = OSDynamicCast(OSBoolean,
getPropertyForHostArch(kOSKernelResourceKey));
if (scratchBool == kOSBooleanTrue) {
flags.kernelComponent = 1;
flags.interface = 1; flags.started = 1;
flags.hasAllDependencies = 1;
}
scratchDict = OSDynamicCast(OSDictionary,
getPropertyForHostArch(kIOKitPersonalitiesKey));
if (scratchDict) {
uniquePersonalityProperties(scratchDict);
}
result = true;
finish:
return result;
}
bool
OSKext::setExecutable(
OSData * anExecutable,
OSData * externalData,
bool externalDataIsMkext)
{
bool result = false;
const char * executableKey = NULL;
if (!anExecutable) {
infoDict->removeObject(_kOSKextExecutableKey);
infoDict->removeObject(_kOSKextMkextExecutableReferenceKey);
infoDict->removeObject(_kOSKextExecutableExternalDataKey);
result = true;
goto finish;
}
if (infoDict->getObject(_kOSKextExecutableKey) ||
infoDict->getObject(_kOSKextMkextExecutableReferenceKey)) {
panic("Attempt to set an executable on a kext "
"that already has one (%s).",
getIdentifierCString());
goto finish;
}
if (externalDataIsMkext) {
executableKey = _kOSKextMkextExecutableReferenceKey;
} else {
executableKey = _kOSKextExecutableKey;
}
if (anExecutable) {
infoDict->setObject(executableKey, anExecutable);
if (externalData) {
infoDict->setObject(_kOSKextExecutableExternalDataKey, externalData);
}
}
result = true;
finish:
return result;
}
static void
uniqueStringPlistProperty(OSDictionary * dict, const char * key)
{
OSString * stringValue = NULL; const OSSymbol * symbolValue = NULL;
stringValue = OSDynamicCast(OSString, dict->getObject(key));
if (!stringValue) {
goto finish;
}
symbolValue = OSSymbol::withString(stringValue);
if (!symbolValue) {
goto finish;
}
dict->setObject(key, symbolValue);
finish:
if (symbolValue) symbolValue->release();
return;
}
static void
uniqueStringPlistProperty(OSDictionary * dict, const OSString * key)
{
OSString * stringValue = NULL; const OSSymbol * symbolValue = NULL;
stringValue = OSDynamicCast(OSString, dict->getObject(key));
if (!stringValue) {
goto finish;
}
symbolValue = OSSymbol::withString(stringValue);
if (!symbolValue) {
goto finish;
}
dict->setObject(key, symbolValue);
finish:
if (symbolValue) symbolValue->release();
return;
}
void
OSKext::uniquePersonalityProperties(OSDictionary * personalityDict)
{
uniqueStringPlistProperty(personalityDict, kCFBundleIdentifierKey);
uniqueStringPlistProperty(personalityDict, kIOProviderClassKey);
uniqueStringPlistProperty(personalityDict, gIOClassKey);
uniqueStringPlistProperty(personalityDict, gIOMatchCategoryKey);
uniqueStringPlistProperty(personalityDict, gIOResourceMatchKey);
uniqueStringPlistProperty(personalityDict, gIOUserClientClassKey);
uniqueStringPlistProperty(personalityDict, "HIDDefaultBehavior");
uniqueStringPlistProperty(personalityDict, "HIDPointerAccelerationType");
uniqueStringPlistProperty(personalityDict, "HIDRemoteControlType");
uniqueStringPlistProperty(personalityDict, "HIDScrollAccelerationType");
uniqueStringPlistProperty(personalityDict, "IOPersonalityPublisher");
uniqueStringPlistProperty(personalityDict, "Physical Interconnect");
uniqueStringPlistProperty(personalityDict, "Physical Interconnect Location");
uniqueStringPlistProperty(personalityDict, "Vendor");
uniqueStringPlistProperty(personalityDict, "Vendor Identification");
uniqueStringPlistProperty(personalityDict, "Vendor Name");
uniqueStringPlistProperty(personalityDict, "bConfigurationValue");
uniqueStringPlistProperty(personalityDict, "bInterfaceNumber");
uniqueStringPlistProperty(personalityDict, "idProduct");
return;
}
void
OSKext::free(void)
{
if (isLoaded()) {
panic("Attempt to free loaded kext %s.", getIdentifierCString());
}
OSSafeRelease(infoDict);
OSSafeRelease(bundleID);
OSSafeRelease(path);
OSSafeRelease(executableRelPath);
OSSafeRelease(dependencies);
OSSafeRelease(linkedExecutable);
OSSafeRelease(metaClasses);
OSSafeRelease(interfaceUUID);
if (isInterface() && kmod_info) {
kfree(kmod_info, sizeof(kmod_info_t));
}
super::free();
return;
}
#if PRAGMA_MARK
#pragma mark Mkext files
#endif
OSReturn
OSKext::readMkextArchive(OSData * mkextData,
uint32_t * checksumPtr)
{
OSReturn result = kOSKextReturnBadData;
uint32_t mkextLength = 0;
mkext_header * mkextHeader = 0; uint32_t mkextVersion = 0;
mkextLength = mkextData->getLength();
if (mkextLength < sizeof(mkext_basic_header)) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
"Mkext archive too small to be valid.");
goto finish;
}
mkextHeader = (mkext_header *)mkextData->getBytesNoCopy();
if (MKEXT_GET_MAGIC(mkextHeader) != MKEXT_MAGIC ||
MKEXT_GET_SIGNATURE(mkextHeader) != MKEXT_SIGN) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
"Mkext archive has invalid magic or signature.");
goto finish;
}
if (MKEXT_GET_LENGTH(mkextHeader) != mkextLength) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
"Mkext archive recorded length doesn't match actual file length.");
goto finish;
}
mkextVersion = MKEXT_GET_VERSION(mkextHeader);
if (mkextVersion == MKEXT_VERS_2) {
result = OSKext::readMkext2Archive(mkextData, NULL, checksumPtr);
} else if (mkextVersion == MKEXT_VERS_1) {
result = OSKext::readMkext1Archive(mkextData, checksumPtr);
} else {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
"Mkext archive of unsupported mkext version 0x%x.", mkextVersion);
result = kOSKextReturnUnsupported;
}
finish:
return result;
}
OSReturn
OSKext::readMkext1Archive(
OSData * mkextData,
uint32_t * checksumPtr)
{
OSReturn result = kOSReturnError;
uint32_t mkextLength;
mkext1_header * mkextHeader = 0; void * mkextEnd = 0; uint32_t mkextVersion;
uint8_t * crc_address = 0;
uint32_t checksum;
uint32_t numKexts = 0;
OSData * infoDictDataObject = NULL; OSObject * parsedXML = NULL; OSDictionary * infoDict = NULL; OSString * errorString = NULL; OSData * mkextExecutableInfo = NULL; OSKext * theKext = NULL;
mkextLength = mkextData->getLength();
mkextHeader = (mkext1_header *)mkextData->getBytesNoCopy();
mkextEnd = (char *)mkextHeader + mkextLength;
mkextVersion = OSSwapBigToHostInt32(mkextHeader->version);
crc_address = (u_int8_t *)&mkextHeader->version;
checksum = mkext_adler32(crc_address,
(uintptr_t)mkextHeader +
OSSwapBigToHostInt32(mkextHeader->length) - (uintptr_t)crc_address);
if (OSSwapBigToHostInt32(mkextHeader->adler32) != checksum) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"Kext archive has a bad checksum.");
result = kOSKextReturnBadData;
goto finish;
}
if (checksumPtr) {
*checksumPtr = checksum;
}
if (OSSwapBigToHostInt32(mkextHeader->cputype) != (UInt32)CPU_TYPE_ANY) {
if ((UInt32)_mh_execute_header.cputype !=
OSSwapBigToHostInt32(mkextHeader->cputype)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"Kext archive doesn't contain software "
"for this computer's CPU type.");
result = kOSKextReturnArchNotFound;
goto finish;
}
}
numKexts = OSSwapBigToHostInt32(mkextHeader->numkexts);
for (uint32_t i = 0; i < numKexts; i++) {
OSSafeReleaseNULL(infoDictDataObject);
OSSafeReleaseNULL(infoDict);
OSSafeReleaseNULL(mkextExecutableInfo);
OSSafeReleaseNULL(errorString);
OSSafeReleaseNULL(theKext);
mkext_kext * kextEntry = &mkextHeader->kext[i];
mkext_file * infoDictPtr = &kextEntry->plist;
mkext_file * executablePtr = &kextEntry->module;
if (kextEntry >= mkextEnd) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"Mkext file overrun.");
result = kOSKextReturnBadData;
goto finish;
}
infoDictDataObject = OSKext::extractMkext1Entry(
mkextHeader, infoDictPtr);
if (!infoDictDataObject) {
OSKextLog( 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( 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( NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"Kext allocation failure.");
continue;
}
if ((OSSwapBigToHostInt32(executablePtr->offset) ||
OSSwapBigToHostInt32(executablePtr->compsize) ||
OSSwapBigToHostInt32(executablePtr->realsize) ||
OSSwapBigToHostInt32(executablePtr->modifiedsecs))) {
MkextEntryRef entryRef;
mkextExecutableInfo = OSData::withCapacity(sizeof(entryRef));
if (!mkextExecutableInfo) {
panic("Error: Couldn't allocate data object "
"for mkext archive entry %d.\n", i);
}
entryRef.mkext = (mkext_basic_header *)mkextHeader;
entryRef.fileinfo = (uint8_t *)executablePtr;
if (!mkextExecutableInfo->appendBytes(&entryRef,
sizeof(entryRef))) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"Couldn't record executable info "
"for mkext archive entry %d.", i);
continue;
}
}
if (!theKext->initWithMkext1Info(infoDict, mkextExecutableInfo,
mkextData)) {
continue;
}
result = kOSReturnSuccess;
}
finish:
OSSafeRelease(infoDictDataObject);
OSSafeRelease(parsedXML);
OSSafeRelease(errorString);
OSSafeRelease(mkextExecutableInfo);
OSSafeRelease(theKext);
return result;
}
bool
OSKext::initWithMkext1Info(
OSDictionary * anInfoDict,
OSData * executableWrapper,
OSData * mkextData)
{
bool result = false;
if (!setInfoDictionaryAndPath(anInfoDict, NULL)) {
goto finish;
}
if (!registerIdentifier()) {
goto finish;
}
if (!setExecutable(executableWrapper, mkextData, true)) {
goto finish;
}
result = true;
finish:
if (!result) {
OSKext::removeKext(this, false);
}
return result;
}
OSData *
OSKext::extractMkext1Entry(
const void * mkextFileBase,
const void * entry)
{
OSData * result = NULL;
OSData * uncompressedData = NULL; const char * errmsg = NULL;
mkext_file * fileinfo;
uint8_t * uncompressedDataBuffer = 0; 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);
size_t alloc_size = expected_size + 1;
time_t modifiedsecs = OSSwapBigToHostInt32(fileinfo->modifiedsecs);
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) {
panic(ALLOC_FAIL);
goto finish;
}
uncompressedData->setDeallocFunction(&osdata_kmem_free);
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);
}
uncompressedDataBuffer[expected_size] = '\0';
result = uncompressedData;
errmsg = NULL;
finish:
if (!result) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"%s", errmsg);
if (uncompressedData) {
uncompressedData->release();
}
}
return result;
}
OSReturn
OSKext::readMkext2Archive(
OSData * mkextData,
OSDictionary ** mkextPlistOut,
uint32_t * checksumPtr)
{
OSReturn result = kOSReturnError;
uint32_t mkextLength;
mkext2_header * mkextHeader = NULL; void * mkextEnd = NULL; uint32_t mkextVersion;
uint8_t * crc_address = NULL;
uint32_t checksum;
uint32_t mkextPlistOffset;
uint32_t mkextPlistCompressedSize;
char * mkextPlistEnd = NULL; uint32_t mkextPlistFullSize;
OSString * errorString = NULL; OSData * mkextPlistUncompressedData = NULL; const char * mkextPlistDataBuffer = NULL; OSObject * parsedXML = NULL; OSDictionary * mkextPlist = NULL; OSArray * mkextInfoDictArray = NULL; 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( NULL,
kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
"Mkext archive has bad checksum.");
result = kOSKextReturnBadData;
goto finish;
}
if (checksumPtr) {
*checksumPtr = checksum;
}
if (MKEXT_GET_CPUTYPE(mkextHeader) == (UInt32)CPU_TYPE_ANY) {
OSKextLog( 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( 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( 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;
}
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( NULL,
kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
"Error unserializing mkext plist: %s.", errorCString);
goto finish;
}
if (mkextPlistOut) {
*mkextPlistOut = mkextPlist;
(*mkextPlistOut)->retain();
}
mkextInfoDictArray = OSDynamicCast(OSArray,
mkextPlist->getObject(kMKEXTInfoDictionariesKey));
if (!mkextInfoDictArray) {
OSKextLog( 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));
OSKext * newKext = OSKext::withMkext2Info(infoDict, mkextData);
OSSafeRelease(newKext);
}
result = kOSReturnSuccess;
finish:
OSSafeRelease(parsedXML);
OSSafeRelease(mkextPlistUncompressedData);
OSSafeRelease(errorString);
return result;
}
OSKext *
OSKext::withMkext2Info(
OSDictionary * anInfoDict,
OSData * mkextData)
{
OSKext * newKext = new OSKext;
if (newKext && !newKext->initWithMkext2Info(anInfoDict, mkextData)) {
newKext->release();
return NULL;
}
return newKext;
}
bool
OSKext::initWithMkext2Info(
OSDictionary * anInfoDict,
OSData * mkextData)
{
bool result = false;
OSString * kextPath = NULL; OSNumber * executableOffsetNum = NULL; OSCollectionIterator * iterator = NULL; OSData * executable = NULL;
if (!super::init()) {
goto finish;
}
kextPath = OSDynamicCast(OSString,
anInfoDict->getObject(kMKEXTBundlePathKey));
if (!setInfoDictionaryAndPath(anInfoDict, kextPath)) {
goto finish;
}
executableRelPath = OSDynamicCast(OSString,
anInfoDict->getObject(kMKEXTExecutableRelativePathKey));
if (executableRelPath) {
executableRelPath->retain();
}
anInfoDict->removeObject(kMKEXTBundlePathKey);
anInfoDict->removeObject(kMKEXTExecutableRelativePathKey);
executableOffsetNum = OSDynamicCast(OSNumber,
infoDict->getObject(kMKEXTExecutableKey));
if (executableOffsetNum) {
executable = createMkext2FileEntry(mkextData,
executableOffsetNum, "executable");
infoDict->removeObject(kMKEXTExecutableKey);
if (!executable) {
goto finish;
}
if (!setExecutable(executable, mkextData, true)) {
goto finish;
}
}
result = registerIdentifier();
finish:
OSSafeRelease(executable);
OSSafeRelease(iterator);
return result;
}
OSData *
OSKext::createMkext2FileEntry(
OSData * mkextData,
OSNumber * offsetNum,
const char * name)
{
OSData * result = NULL;
MkextEntryRef entryRef;
uint8_t * mkextBuffer = (uint8_t *)mkextData->getBytesNoCopy();
uint32_t entryOffset = offsetNum->unsigned32BitValue();
result = OSData::withCapacity(sizeof(entryRef));
if (!result) {
goto finish;
}
entryRef.mkext = (mkext_basic_header *)mkextBuffer;
entryRef.fileinfo = mkextBuffer + entryOffset;
if (!result->appendBytes(&entryRef, sizeof(entryRef))) {
OSSafeReleaseNULL(result);
goto finish;
}
finish:
if (!result) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
"Can't create wrapper for mkext file entry '%s' of kext %s.",
name, getIdentifierCString());
}
return result;
}
extern "C" {
static void * z_alloc(void *, u_int items, u_int size);
static void z_free(void *, void *ptr);
typedef struct z_mem {
uint32_t alloc_size;
uint8_t data[0];
} z_mem;
void *
z_alloc(void * notused __unused, u_int num_items, u_int size)
{
void * result = NULL;
z_mem * zmem = NULL;
uint32_t total = num_items * size;
uint32_t allocSize = total + sizeof(zmem);
zmem = (z_mem *)kalloc(allocSize);
if (!zmem) {
goto finish;
}
zmem->alloc_size = allocSize;
result = (void *)&(zmem->data);
finish:
return result;
}
void
z_free(void * notused __unused, void * ptr)
{
uint32_t * skipper = (uint32_t *)ptr - 1;
z_mem * zmem = (z_mem *)skipper;
kfree((void *)zmem, zmem->alloc_size);
return;
}
};
OSData *
OSKext::extractMkext2FileData(
UInt8 * data,
const char * name,
uint32_t compressedSize,
uint32_t fullSize)
{
OSData * result = NULL;
OSData * uncompressedData = NULL;
uint8_t * uncompressedDataBuffer = 0; unsigned long uncompressedSize;
z_stream zstream;
bool zstream_inited = false;
int zlib_result;
if (!compressedSize) {
uncompressedData = OSData::withBytes(data, fullSize);
result = uncompressedData;
goto finish;
}
if (KERN_SUCCESS != kmem_alloc(kernel_map,
(vm_offset_t*)&uncompressedDataBuffer, fullSize)) {
if (isKernel()) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
"Allocation failure extracting %s from mkext.", name);
} else {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
"Allocation failure extracting %s from mkext for kext %s.",
name, getIdentifierCString());
}
goto finish;
}
uncompressedData = OSData::withBytesNoCopy(uncompressedDataBuffer, fullSize);
if (!uncompressedData) {
if (isKernel()) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
"Allocation failure extracting %s from mkext.", name);
} else {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
"Allocation failure extracting %s from mkext for kext %s.",
name, getIdentifierCString());
}
goto finish;
}
uncompressedData->setDeallocFunction(&osdata_kmem_free);
if (isKernel()) {
OSKextLog(this,
kOSKextLogDetailLevel |
kOSKextLogArchiveFlag,
"Kernel extracted %s from mkext - compressed size %d, uncompressed size %d.",
name, compressedSize, fullSize);
} else {
OSKextLog(this,
kOSKextLogDetailLevel |
kOSKextLogArchiveFlag,
"Kext %s extracted %s from mkext - compressed size %d, uncompressed size %d.",
getIdentifierCString(), name, compressedSize, fullSize);
}
bzero(&zstream, sizeof(zstream));
zstream.next_in = (UInt8 *)data;
zstream.avail_in = compressedSize;
zstream.next_out = uncompressedDataBuffer;
zstream.avail_out = fullSize;
zstream.zalloc = z_alloc;
zstream.zfree = z_free;
zlib_result = inflateInit(&zstream);
if (Z_OK != zlib_result) {
if (isKernel()) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
"Mkext error; zlib inflateInit failed (%d) for %s.",
zlib_result, name);
} else {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
"Kext %s - mkext error; zlib inflateInit failed (%d) for %s .",
getIdentifierCString(), zlib_result, name);
}
goto finish;
} else {
zstream_inited = true;
}
zlib_result = inflate(&zstream, Z_FINISH);
if (zlib_result == Z_STREAM_END || zlib_result == Z_OK) {
uncompressedSize = zstream.total_out;
} else {
if (isKernel()) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
"Mkext error; zlib inflate failed (%d) for %s.",
zlib_result, name);
} else {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
"Kext %s - mkext error; zlib inflate failed (%d) for %s .",
getIdentifierCString(), zlib_result, name);
}
if (zstream.msg) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
"zlib error: %s.", zstream.msg);
}
goto finish;
}
if (uncompressedSize != fullSize) {
if (isKernel()) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
"Mkext error; zlib inflate discrepancy for %s, "
"uncompressed size != original size.", name);
} else {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
"Kext %s - mkext error; zlib inflate discrepancy for %s, "
"uncompressed size != original size.",
getIdentifierCString(), name);
}
goto finish;
}
result = uncompressedData;
finish:
if (zstream_inited) inflateEnd(&zstream);
if (!result) {
OSSafeRelease(uncompressedData);
}
return result;
}
OSReturn
OSKext::loadFromMkext(
OSKextLogSpec clientLogFilter,
char * mkextBuffer,
uint32_t mkextBufferLength,
char ** logInfoOut,
uint32_t * logInfoLengthOut)
{
OSReturn result = kOSReturnError;
OSReturn tempResult = kOSReturnError;
OSData * mkextData = NULL; OSDictionary * mkextPlist = NULL;
OSArray * logInfoArray = NULL; OSSerialize * serializer = NULL;
OSString * predicate = NULL; OSDictionary * requestArgs = NULL;
OSString * kextIdentifier = NULL; OSNumber * startKextExcludeNum = NULL; OSNumber * startMatchingExcludeNum = NULL; OSBoolean * delayAutounloadBool = NULL; OSArray * personalityNames = NULL;
Boolean delayAutounload = false;
OSKextExcludeLevel startKextExcludeLevel = kOSKextExcludeNone;
OSKextExcludeLevel startMatchingExcludeLevel = kOSKextExcludeAll;
IORecursiveLockLock(sKextLock);
if (logInfoOut) {
*logInfoOut = NULL;
*logInfoLengthOut = 0;
}
OSKext::setUserSpaceLogFilter(clientLogFilter, logInfoOut ? true : false);
OSKextLog( NULL,
kOSKextLogDebugLevel |
kOSKextLogIPCFlag,
"Received kext load request from user space.");
if (!sUserLoadsActive) {
OSKextLog( NULL,
kOSKextLogProgressLevel |
kOSKextLogGeneralFlag | kOSKextLogLoadFlag,
"Switching to late startup (user-space) kext loading policy.");
sUserLoadsActive = true;
}
if (!sLoadEnabled) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Kext loading is disabled.");
result = kOSKextReturnDisabled;
goto finish;
}
mkextData = OSData::withBytesNoCopy(mkextBuffer,
mkextBufferLength);
if (!mkextData) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Failed to create wrapper for kext load request.");
result = kOSKextReturnNoMemory;
goto finish;
}
result = readMkext2Archive(mkextData, &mkextPlist, NULL);
if (result != kOSReturnSuccess) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Failed to read kext load request.");
goto finish;
}
predicate = _OSKextGetRequestPredicate(mkextPlist);
if (!predicate || !predicate->isEqualTo(kKextRequestPredicateLoad)) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Received kext load request with no predicate; skipping.");
result = kOSKextReturnInvalidArgument;
goto finish;
}
requestArgs = OSDynamicCast(OSDictionary,
mkextPlist->getObject(kKextRequestArgumentsKey));
if (!requestArgs || !requestArgs->getCount()) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Received kext load request with no arguments.");
result = kOSKextReturnInvalidArgument;
goto finish;
}
kextIdentifier = OSDynamicCast(OSString,
requestArgs->getObject(kKextRequestArgumentBundleIdentifierKey));
if (!kextIdentifier) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Received kext load request with no kext identifier.");
result = kOSKextReturnInvalidArgument;
goto finish;
}
startKextExcludeNum = OSDynamicCast(OSNumber,
requestArgs->getObject(kKextRequestArgumentStartExcludeKey));
startMatchingExcludeNum = OSDynamicCast(OSNumber,
requestArgs->getObject(kKextRequestArgumentStartMatchingExcludeKey));
delayAutounloadBool = OSDynamicCast(OSBoolean,
requestArgs->getObject(kKextRequestArgumentDelayAutounloadKey));
personalityNames = OSDynamicCast(OSArray,
requestArgs->getObject(kKextRequestArgumentPersonalityNamesKey));
if (delayAutounloadBool) {
delayAutounload = delayAutounloadBool->getValue();
}
if (startKextExcludeNum) {
startKextExcludeLevel = startKextExcludeNum->unsigned8BitValue();
}
if (startMatchingExcludeNum) {
startMatchingExcludeLevel = startMatchingExcludeNum->unsigned8BitValue();
}
OSKextLog( NULL,
kOSKextLogProgressLevel |
kOSKextLogIPCFlag,
"Received request from user space to load kext %s.",
kextIdentifier->getCStringNoCopy());
result = OSKext::loadKextWithIdentifier(
kextIdentifier,
false,
delayAutounload,
startKextExcludeLevel,
startMatchingExcludeLevel,
personalityNames);
if (result != kOSReturnSuccess) {
goto finish;
}
finish:
logInfoArray = OSKext::clearUserSpaceLogFilter();
if (logInfoArray && logInfoOut && logInfoLengthOut) {
tempResult = OSKext::serializeLogInfo(logInfoArray,
logInfoOut, logInfoLengthOut);
if (tempResult != kOSReturnSuccess) {
result = tempResult;
}
}
OSKext::flushNonloadedKexts( false);
if (mkextData && mkextData->getRetainCount() > 1) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Kext load request buffer from user space still retained by a kext; "
"probable memory leak.");
}
IORecursiveLockUnlock(sKextLock);
OSSafeRelease(mkextData);
OSSafeRelease(mkextPlist);
OSSafeRelease(serializer);
OSSafeRelease(logInfoArray);
return result;
}
OSReturn
OSKext::serializeLogInfo(
OSArray * logInfoArray,
char ** logInfoOut,
uint32_t * logInfoLengthOut)
{
OSReturn result = kOSReturnError;
char * buffer = NULL;
kern_return_t kmem_result = KERN_FAILURE;
OSSerialize * serializer = NULL; char * logInfo = NULL; uint32_t logInfoLength = 0;
if (!logInfoArray || !logInfoOut || !logInfoLengthOut) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogIPCFlag,
"Internal error; invalid arguments to OSKext::serializeLogInfo().");
result = kOSKextReturnInvalidArgument;
goto finish;
}
serializer = OSSerialize::withCapacity(0);
if (!serializer) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogIPCFlag,
"Failed to create serializer on log info for request from user space.");
}
if (!logInfoArray->serialize(serializer)) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogIPCFlag,
"Failed to serialize log info for request from user space.");
} else {
logInfo = serializer->text();
logInfoLength = serializer->getLength();
kmem_result = kmem_alloc(kernel_map, (vm_offset_t *)&buffer, logInfoLength);
if (kmem_result != KERN_SUCCESS) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogIPCFlag,
"Failed to copy log info for request from user space.");
} else {
memcpy(buffer, logInfo, logInfoLength);
*logInfoOut = buffer;
*logInfoLengthOut = logInfoLength;
}
}
result = kOSReturnSuccess;
finish:
OSSafeRelease(serializer);
return result;
}
#if PRAGMA_MARK
#pragma mark Instance Management Methods
#endif
OSKext *
OSKext::lookupKextWithIdentifier(const char * kextIdentifier)
{
OSKext * foundKext = NULL;
IORecursiveLockLock(sKextLock);
foundKext = OSDynamicCast(OSKext, sKextsByID->getObject(kextIdentifier));
if (foundKext) {
foundKext->retain();
}
IORecursiveLockUnlock(sKextLock);
return foundKext;
}
OSKext *
OSKext::lookupKextWithIdentifier(OSString * kextIdentifier)
{
return OSKext::lookupKextWithIdentifier(kextIdentifier->getCStringNoCopy());
}
OSKext *
OSKext::lookupKextWithLoadTag(uint32_t aTag)
{
OSKext * foundKext = NULL; uint32_t count, i;
IORecursiveLockLock(sKextLock);
count = sLoadedKexts->getCount();
for (i = 0; i < count; i++) {
OSKext * thisKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i));
if (thisKext->getLoadTag() == aTag) {
foundKext = thisKext;
foundKext->retain();
goto finish;
}
}
finish:
IORecursiveLockUnlock(sKextLock);
return foundKext;
}
OSKext *
OSKext::lookupKextWithAddress(vm_address_t address)
{
OSKext * foundKext = NULL; uint32_t count, i;
IORecursiveLockLock(sKextLock);
count = sLoadedKexts->getCount();
for (i = 0; i < count; i++) {
OSKext * thisKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i));
if (thisKext->linkedExecutable) {
vm_address_t kext_start =
(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();
goto finish;
}
}
}
finish:
IORecursiveLockUnlock(sKextLock);
return foundKext;
}
bool OSKext::isKextWithIdentifierLoaded(const char * kextIdentifier)
{
bool result = false;
OSKext * foundKext = NULL;
IORecursiveLockLock(sKextLock);
foundKext = OSDynamicCast(OSKext, sKextsByID->getObject(kextIdentifier));
if (foundKext && foundKext->isLoaded()) {
result = true;
}
IORecursiveLockUnlock(sKextLock);
return result;
}
OSReturn
OSKext::removeKext(
OSKext * aKext,
bool terminateServicesAndRemovePersonalitiesFlag)
{
OSReturn result = kOSKextReturnInUse;
OSKext * checkKext = NULL;
IORecursiveLockLock(sKextLock);
if (!aKext->getIdentifier()) {
result = kOSReturnSuccess;
goto finish;
}
checkKext = OSDynamicCast(OSKext,
sKextsByID->getObject(aKext->getIdentifier()));
if (checkKext != aKext) {
result = kOSKextReturnNotFound;
goto finish;
}
if (aKext->isLoaded()) {
if (terminateServicesAndRemovePersonalitiesFlag) {
result = gIOCatalogue->terminateDriversForModule(
aKext->getIdentifierCString(), false);
if (result != kOSReturnSuccess) {
OSKextLog(aKext,
kOSKextLogErrorLevel |
kOSKextLogKextBookkeepingFlag,
"Can't remove kext %s; services failed to terminate - 0x%x.",
aKext->getIdentifierCString(), result);
goto finish;
}
}
result = aKext->unload();
if (result != kOSReturnSuccess) {
goto finish;
}
}
if (terminateServicesAndRemovePersonalitiesFlag) {
aKext->removePersonalitiesFromCatalog();
}
OSKextLog(aKext,
kOSKextLogProgressLevel |
kOSKextLogKextBookkeepingFlag,
"Removing kext %s.",
aKext->getIdentifierCString());
sKextsByID->removeObject(aKext->getIdentifier());
result = kOSReturnSuccess;
finish:
IORecursiveLockUnlock(sKextLock);
return result;
}
OSReturn
OSKext::removeKextWithIdentifier(
const char * kextIdentifier,
bool terminateServicesAndRemovePersonalitiesFlag)
{
OSReturn result = kOSReturnError;
IORecursiveLockLock(sKextLock);
OSKext * aKext = OSDynamicCast(OSKext,
sKextsByID->getObject(kextIdentifier));
if (!aKext) {
result = kOSKextReturnNotFound;
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogKextBookkeepingFlag,
"Can't remove kext %s - not found.",
kextIdentifier);
goto finish;
}
result = OSKext::removeKext(aKext,
terminateServicesAndRemovePersonalitiesFlag);
finish:
IORecursiveLockUnlock(sKextLock);
return result;
}
OSReturn
OSKext::removeKextWithLoadTag(
OSKextLoadTag loadTag,
bool terminateServicesAndRemovePersonalitiesFlag)
{
OSReturn result = kOSReturnError;
OSKext * foundKext = NULL;
uint32_t count, i;
IORecursiveLockLock(sKextLock);
count = sLoadedKexts->getCount();
for (i = 0; i < count; i++) {
OSKext * thisKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i));
if (thisKext->loadTag == loadTag) {
foundKext = thisKext;
break;
}
}
if (!foundKext) {
result = kOSKextReturnNotFound;
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag | kOSKextLogKextBookkeepingFlag,
"Can't remove kext with load tag %d - not found.",
loadTag);
goto finish;
}
result = OSKext::removeKext(foundKext,
terminateServicesAndRemovePersonalitiesFlag);
finish:
IORecursiveLockUnlock(sKextLock);
return result;
}
OSDictionary *
OSKext::copyKexts(void)
{
OSDictionary * result;
IORecursiveLockLock(sKextLock);
result = OSDynamicCast(OSDictionary, sKextsByID->copyCollection());
IORecursiveLockUnlock(sKextLock);
return result;
}
#if PRAGMA_MARK
#pragma mark Accessors
#endif
const OSSymbol *
OSKext::getIdentifier(void)
{
return bundleID;
}
const char *
OSKext::getIdentifierCString(void)
{
return bundleID->getCStringNoCopy();
}
OSKextVersion
OSKext::getVersion(void)
{
return version;
}
OSKextVersion
OSKext::getCompatibleVersion(void)
{
return compatibleVersion;
}
bool
OSKext::isLibrary(void)
{
return (getCompatibleVersion() > 0);
}
bool
OSKext::isCompatibleWithVersion(OSKextVersion aVersion)
{
if ((compatibleVersion > -1 && version > -1) &&
(compatibleVersion <= version && aVersion <= version)) {
return true;
}
return false;
}
bool
OSKext::declaresExecutable(void)
{
return (getPropertyForHostArch(kCFBundleExecutableKey) != NULL);
}
OSData *
OSKext::getExecutable(void)
{
OSData * result = NULL;
OSData * extractedExecutable = NULL; OSData * mkextExecutableRef = NULL;
result = OSDynamicCast(OSData, infoDict->getObject(_kOSKextExecutableKey));
if (result) {
goto finish;
}
mkextExecutableRef = OSDynamicCast(OSData,
getPropertyForHostArch(_kOSKextMkextExecutableReferenceKey));
if (mkextExecutableRef) {
MkextEntryRef * mkextEntryRef = (MkextEntryRef *)
mkextExecutableRef->getBytesNoCopy();
uint32_t mkextVersion = MKEXT_GET_VERSION(mkextEntryRef->mkext);
if (mkextVersion == MKEXT_VERS_2) {
mkext2_file_entry * fileinfo =
(mkext2_file_entry *)mkextEntryRef->fileinfo;
uint32_t compressedSize = MKEXT2_GET_ENTRY_COMPSIZE(fileinfo);
uint32_t fullSize = MKEXT2_GET_ENTRY_FULLSIZE(fileinfo);
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,
"Kext %s - unknown mkext version 0x%x for executable.",
getIdentifierCString(), mkextVersion);
}
infoDict->removeObject(_kOSKextMkextExecutableReferenceKey);
infoDict->removeObject(_kOSKextExecutableExternalDataKey);
if (extractedExecutable && extractedExecutable->getLength()) {
if (!setExecutable(extractedExecutable)) {
goto finish;
}
result = extractedExecutable;
} else {
goto finish;
}
}
finish:
OSSafeRelease(extractedExecutable);
return result;
}
bool
OSKext::isInterface(void)
{
return flags.interface;
}
bool
OSKext::isKernel(void)
{
return (this == sKernelKext);
}
bool
OSKext::isKernelComponent(void)
{
return flags.kernelComponent ? true : false;
}
bool
OSKext::isExecutable(void)
{
return (!isKernel() && !isInterface() && declaresExecutable());
}
bool
OSKext::isLoadableInSafeBoot(void)
{
bool result = false;
OSString * required = NULL;
if (isKernel()) {
result = true;
goto finish;
}
required = OSDynamicCast(OSString,
getPropertyForHostArch(kOSBundleRequiredKey));
if (!required) {
goto finish;
}
if (required->isEqualTo(kOSBundleRequiredRoot) ||
required->isEqualTo(kOSBundleRequiredLocalRoot) ||
required->isEqualTo(kOSBundleRequiredNetworkRoot) ||
required->isEqualTo(kOSBundleRequiredSafeBoot) ||
required->isEqualTo(kOSBundleRequiredConsole)) {
result = true;
}
finish:
return result;
}
bool
OSKext::isPrelinked(void)
{
return flags.prelinked ? true : false;
}
bool OSKext::isLoaded(void)
{
return flags.loaded ? true : false;
}
bool
OSKext::isStarted(void)
{
return flags.started ? true : false;
}
bool
OSKext::isCPPInitialized(void)
{
return flags.CPPInitialized;
}
void
OSKext::setCPPInitialized(bool initialized)
{
flags.CPPInitialized = initialized;
}
uint32_t
OSKext::getLoadTag(void)
{
return loadTag;
}
void OSKext::getSizeInfo(uint32_t *loadSize, uint32_t *wiredSize)
{
if (linkedExecutable) {
*loadSize = linkedExecutable->getLength();
if (kmod_info) {
*wiredSize = *loadSize - kmod_info->hdr_size;
} else {
*wiredSize = *loadSize;
}
}
else {
*wiredSize = 0;
*loadSize = 0;
}
}
OSData *
OSKext::copyUUID(void)
{
OSData * result = NULL;
OSData * theExecutable = NULL; const kernel_mach_header_t * header = NULL;
const struct load_command * load_cmd = NULL;
const struct uuid_command * uuid_cmd = NULL;
uint32_t i;
if (interfaceUUID) {
result = interfaceUUID;
result->retain();
goto finish;
}
theExecutable = linkedExecutable;
if (!theExecutable) {
theExecutable = getExecutable();
}
if (!theExecutable) {
goto finish;
}
header = (const kernel_mach_header_t *)theExecutable->getBytesNoCopy();
load_cmd = (const struct load_command *)&header[1];
for (i = 0; i < header->ncmds; i++) {
if (load_cmd->cmd == LC_UUID) {
uuid_cmd = (struct uuid_command *)load_cmd;
result = OSData::withBytes(uuid_cmd->uuid, sizeof(uuid_cmd->uuid));
goto finish;
}
load_cmd = (struct load_command *)((caddr_t)load_cmd + load_cmd->cmdsize);
}
finish:
return result;
}
#if defined (__i386__)
#define ARCHNAME "i386"
#elif defined (__x86_64__)
#define ARCHNAME "x86_64"
#else
#error architecture not supported
#endif
#define ARCH_SEPARATOR_CHAR '_'
static char * makeHostArchKey(const char * key, uint32_t * keySizeOut)
{
char * result = NULL;
uint32_t keyLength = strlen(key);
uint32_t keySize;
keySize = 1 + 1 + strlen(key) + strlen(ARCHNAME);
result = (char *)kalloc(keySize);
if (!result) {
goto finish;
}
strlcpy(result, key, keySize);
result[keyLength++] = ARCH_SEPARATOR_CHAR;
result[keyLength] = '\0';
strlcat(result, ARCHNAME, keySize);
*keySizeOut = keySize;
finish:
return result;
}
OSObject *
OSKext::getPropertyForHostArch(const char * key)
{
OSObject * result = NULL; uint32_t hostArchKeySize = 0;
char * hostArchKey = NULL;
if (!key || !infoDict) {
goto finish;
}
if (STRING_HAS_PREFIX(key, "OS") ||
STRING_HAS_PREFIX(key, "IO")) {
hostArchKey = makeHostArchKey(key, &hostArchKeySize);
if (!hostArchKey) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Allocation failure.");
goto finish;
}
result = infoDict->getObject(hostArchKey);
}
if (!result) {
result = infoDict->getObject(key);
}
finish:
if (hostArchKey) kfree(hostArchKey, hostArchKeySize);
return result;
}
#if PRAGMA_MARK
#pragma mark Load/Start/Stop/Unload
#endif
OSReturn
OSKext::loadKextWithIdentifier(
const char * kextIdentifierCString,
Boolean allowDeferFlag,
Boolean delayAutounloadFlag,
OSKextExcludeLevel startOpt,
OSKextExcludeLevel startMatchingOpt,
OSArray * personalityNames)
{
OSReturn result = kOSReturnError;
OSString * kextIdentifier = NULL;
kextIdentifier = OSString::withCString(kextIdentifierCString);
if (!kextIdentifier) {
result = kOSKextReturnNoMemory;
goto finish;
}
result = OSKext::loadKextWithIdentifier(kextIdentifier,
allowDeferFlag, delayAutounloadFlag,
startOpt, startMatchingOpt, personalityNames);
finish:
OSSafeRelease(kextIdentifier);
return result;
}
OSReturn
OSKext::loadKextWithIdentifier(
OSString * kextIdentifier,
Boolean allowDeferFlag,
Boolean delayAutounloadFlag,
OSKextExcludeLevel startOpt,
OSKextExcludeLevel startMatchingOpt,
OSArray * personalityNames)
{
OSReturn result = kOSReturnError;
OSReturn pingResult = kOSReturnError;
OSKext * theKext = NULL; OSDictionary * loadRequest = NULL; const OSSymbol * kextIdentifierSymbol = NULL;
IORecursiveLockLock(sKextLock);
if (!kextIdentifier) {
result = kOSKextReturnInvalidArgument;
goto finish;
}
OSKext::recordIdentifierRequest(kextIdentifier);
theKext = OSDynamicCast(OSKext, sKextsByID->getObject(kextIdentifier));
if (!theKext) {
if (!allowDeferFlag) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Can't load kext %s - not found.",
kextIdentifier->getCStringNoCopy());
goto finish;
}
if (!sKernelRequestsEnabled) {
OSKextLog(theKext,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Can't load kext %s - requests to user space are disabled.",
kextIdentifier->getCStringNoCopy());
result = kOSKextReturnDisabled;
goto finish;
}
kextIdentifierSymbol = OSSymbol::withString(kextIdentifier);
if (!sPostedKextLoadIdentifiers->containsObject(kextIdentifierSymbol)) {
result = _OSKextCreateRequest(kKextRequestPredicateRequestLoad,
&loadRequest);
if (result != kOSReturnSuccess) {
goto finish;
}
if (!_OSKextSetRequestArgument(loadRequest,
kKextRequestArgumentBundleIdentifierKey, kextIdentifier)) {
result = kOSKextReturnNoMemory;
goto finish;
}
if (!sKernelRequests->setObject(loadRequest)) {
result = kOSKextReturnNoMemory;
goto finish;
}
if (!sPostedKextLoadIdentifiers->setObject(kextIdentifierSymbol)) {
result = kOSKextReturnNoMemory;
goto finish;
}
OSKextLog(theKext,
kOSKextLogDebugLevel |
kOSKextLogLoadFlag,
"Kext %s not found; queued load request to user space.",
kextIdentifier->getCStringNoCopy());
}
pingResult = OSKext::pingKextd();
if (pingResult == kOSKextReturnDisabled) {
OSKextLog( NULL,
((sPrelinkBoot) ? kOSKextLogDebugLevel : kOSKextLogErrorLevel) |
kOSKextLogLoadFlag,
"Kext %s might not load - kextd is currently unavailable.",
kextIdentifier->getCStringNoCopy());
}
result = kOSKextReturnDeferred;
goto finish;
}
result = theKext->load(startOpt, startMatchingOpt, personalityNames);
if (result != kOSReturnSuccess) {
OSKextLog(theKext,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Failed to load kext %s (error 0x%x).",
kextIdentifier->getCStringNoCopy(), (int)result);
OSKext::removeKext(theKext,
true);
goto finish;
}
if (delayAutounloadFlag) {
OSKextLog(theKext,
kOSKextLogProgressLevel |
kOSKextLogLoadFlag | kOSKextLogKextBookkeepingFlag,
"Setting delayed autounload for %s.",
kextIdentifier->getCStringNoCopy());
theKext->flags.delayAutounload = 1;
}
finish:
OSSafeRelease(loadRequest);
OSSafeRelease(kextIdentifierSymbol);
IORecursiveLockUnlock(sKextLock);
return result;
}
void
OSKext::recordIdentifierRequest(
OSString * kextIdentifier)
{
const OSSymbol * kextIdentifierSymbol = NULL; bool fail = false;
if (!sAllKextLoadIdentifiers || !kextIdentifier) {
goto finish;
}
kextIdentifierSymbol = OSSymbol::withString(kextIdentifier);
if (!kextIdentifierSymbol) {
fail = true;
goto finish;
}
if (!sAllKextLoadIdentifiers->containsObject(kextIdentifierSymbol)) {
if (!sAllKextLoadIdentifiers->setObject(kextIdentifierSymbol)) {
fail = true;
} else {
OSKextLog( NULL,
kOSKextLogStepLevel |
kOSKextLogArchiveFlag,
"Recorded kext %s as a candidate for inclusion in prelinked kernel.",
kextIdentifier->getCStringNoCopy());
}
}
finish:
if (fail) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogArchiveFlag,
"Failed to record kext %s as a candidate for inclusion in prelinked kernel.",
kextIdentifier->getCStringNoCopy());
}
OSSafeRelease(kextIdentifierSymbol);
return;
}
OSReturn
OSKext::load(
OSKextExcludeLevel startOpt,
OSKextExcludeLevel startMatchingOpt,
OSArray * personalityNames)
{
OSReturn result = kOSReturnError;
kern_return_t kxldResult;
OSKextExcludeLevel dependenciesStartOpt = startOpt;
OSKextExcludeLevel dependenciesStartMatchingOpt = startMatchingOpt;
unsigned int i, count;
Boolean alreadyLoaded = false;
OSKext * lastLoadedKext = NULL;
if (isLoaded()) {
alreadyLoaded = true;
result = kOSReturnSuccess;
OSKextLog(this,
kOSKextLogDebugLevel |
kOSKextLogLoadFlag | kOSKextLogKextBookkeepingFlag,
"Kext %s is already loaded.",
getIdentifierCString());
goto loaded;
}
if (!sLoadEnabled) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Kext loading is disabled (attempt to load kext %s).",
getIdentifierCString());
result = kOSKextReturnDisabled;
goto finish;
}
if (sNextLoadTag == kOSKextInvalidLoadTag) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Can't load kext %s - no more load tags to assign.",
getIdentifierCString());
result = kOSKextReturnNoResources;
goto finish;
}
if (!declaresExecutable()) {
result = kOSReturnSuccess;
goto loaded;
}
if (sSafeBoot && !isLoadableInSafeBoot()) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Can't load kext %s - not loadable during safe boot.",
getIdentifierCString());
result = kOSKextReturnBootLevel;
goto finish;
}
OSKextLog(this,
kOSKextLogProgressLevel | kOSKextLogLoadFlag,
"Loading kext %s.",
getIdentifierCString());
if (!sKxldContext) {
kxldResult = kxld_create_context(&sKxldContext, &kern_allocate,
&kxld_log_callback, (KXLDFlags) 0,
0, 0);
if (kxldResult) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag | kOSKextLogLinkFlag,
"Can't load kext %s - failed to create link context.",
getIdentifierCString());
result = kOSKextReturnNoMemory;
goto finish;
}
}
if (!resolveDependencies()) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag | kOSKextLogDependenciesFlag,
"Can't load kext %s - failed to resolve library dependencies.",
getIdentifierCString());
result = kOSKextReturnDependencies;
goto finish;
}
if (dependenciesStartOpt == kOSKextExcludeKext) {
dependenciesStartOpt = kOSKextExcludeNone;
}
if (dependenciesStartMatchingOpt == kOSKextExcludeKext) {
dependenciesStartMatchingOpt = kOSKextExcludeNone;
}
count = getNumDependencies();
for (i = 0; i < count; i++) {
OSKext * dependency = OSDynamicCast(OSKext,
dependencies->getObject(i));
if (dependency == NULL) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag | kOSKextLogDependenciesFlag,
"Internal error loading kext %s; dependency disappeared.",
getIdentifierCString());
result = kOSKextReturnInternalError;
goto finish;
}
result = dependency->load(dependenciesStartOpt,
dependenciesStartMatchingOpt,
NULL);
if (result != KERN_SUCCESS) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag | kOSKextLogDependenciesFlag,
"Dependency %s of kext %s failed to load.",
dependency->getIdentifierCString(),
getIdentifierCString());
OSKext::removeKext(dependency,
true);
result = kOSKextReturnDependencyLoadError;
goto finish;
}
}
result = loadExecutable();
if (result != KERN_SUCCESS) {
goto finish;
}
flags.loaded = true;
lastLoadedKext = OSDynamicCast(OSKext, sLoadedKexts->getLastObject());
sLoadedKexts->setObject(this);
if (lastLoadedKext->isKernel()) {
lastLoadedKext = NULL;
}
if (lastLoadedKext) {
kmod_info->next = lastLoadedKext->kmod_info;
}
notifyKextLoadObservers(this, kmod_info);
kmod = kmod_info;
OSKext::saveLoadedKextPanicList();
if (isExecutable()) {
OSKext::updateLoadedKextSummaries();
savePanicString( true);
#if CONFIG_DTRACE
registerWithDTrace();
#else
jettisonLinkeditSegment();
#endif
}
loaded:
if (isExecutable() && !flags.started) {
if (startOpt == kOSKextExcludeNone) {
result = start();
if (result != kOSReturnSuccess) {
OSKextLog(this,
kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"Kext %s start failed (result 0x%x).",
getIdentifierCString(), result);
result = kOSKextReturnStartStopError;
}
}
}
if (result == kOSReturnSuccess && startMatchingOpt == kOSKextExcludeNone) {
result = sendPersonalitiesToCatalog(true, personalityNames);
}
finish:
if (!declaresExecutable()) {
OSKextLog(this,
kOSKextLogStepLevel | kOSKextLogLoadFlag,
"Kext %s has no executable; removing any personalities naming it.",
getIdentifierCString());
removePersonalitiesFromCatalog();
}
if (result != kOSReturnSuccess) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Kext %s failed to load (0x%x).",
getIdentifierCString(), (int)result);
} else if (!alreadyLoaded) {
OSKextLog(this,
kOSKextLogProgressLevel |
kOSKextLogLoadFlag,
"Kext %s loaded.",
getIdentifierCString());
queueKextNotification(kKextRequestPredicateLoadNotification,
OSDynamicCast(OSString, bundleID));
}
return result;
}
static char * strdup(const char * string)
{
char * result = NULL;
size_t size;
if (!string) {
goto finish;
}
size = 1 + strlen(string);
result = (char *)kalloc(size);
if (!result) {
goto finish;
}
memcpy(result, string, size);
finish:
return result;
}
OSReturn
OSKext::loadExecutable()
{
OSReturn result = kOSReturnError;
kern_return_t kxldResult;
KXLDDependency * kxlddeps = NULL; uint32_t num_kxlddeps = 0;
OSArray * linkDependencies = NULL; uint32_t numDirectDependencies = 0;
uint32_t num_kmod_refs = 0;
struct mach_header ** kxldHeaderPtr = NULL; struct mach_header * kxld_header = NULL; OSData * theExecutable = NULL; OSString * versString = NULL; const char * versCString = NULL; const char * string = NULL; unsigned int i;
versString = OSDynamicCast(OSString,
getPropertyForHostArch(kCFBundleVersionKey));
if (!versString) {
goto finish;
}
versCString = versString->getCStringNoCopy();
if (isKernelComponent()) {
if (STRING_HAS_PREFIX(versCString, KERNEL_LIB_PREFIX)) {
if (strncmp(versCString, KERNEL6_VERSION, strlen(KERNEL6_VERSION))) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Kernel component %s has incorrect version %s; "
"expected %s.",
getIdentifierCString(),
versCString, KERNEL6_VERSION);
result = kOSKextReturnInternalError;
goto finish;
} else if (strcmp(versCString, osrelease)) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Kernel component %s has incorrect version %s; "
"expected %s.",
getIdentifierCString(),
versCString, osrelease);
result = kOSKextReturnInternalError;
goto finish;
}
}
}
if (isPrelinked()) {
goto register_kmod;
}
theExecutable = getExecutable();
if (!theExecutable) {
if (declaresExecutable()) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Can't load kext %s - executable is missing.",
getIdentifierCString());
result = kOSKextReturnValidation;
goto finish;
}
goto register_kmod;
}
if (isInterface()) {
OSData *executableCopy = OSData::withData(theExecutable);
setLinkedExecutable(executableCopy);
executableCopy->release();
goto register_kmod;
}
numDirectDependencies = getNumDependencies();
if (flags.hasBleedthrough) {
linkDependencies = dependencies;
linkDependencies->retain();
} else {
linkDependencies = OSArray::withArray(dependencies);
if (!linkDependencies) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag | kOSKextLogLinkFlag,
"Can't allocate link dependencies to load kext %s.",
getIdentifierCString());
goto finish;
}
for (i = 0; i < numDirectDependencies; ++i) {
OSKext * dependencyKext = OSDynamicCast(OSKext,
dependencies->getObject(i));
dependencyKext->addBleedthroughDependencies(linkDependencies);
}
}
num_kxlddeps = linkDependencies->getCount();
if (!num_kxlddeps) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag | kOSKextLogDependenciesFlag,
"Can't load kext %s - it has no library dependencies.",
getIdentifierCString());
goto finish;
}
kxlddeps = (KXLDDependency *)kalloc(num_kxlddeps * sizeof(*kxlddeps));
if (!kxlddeps) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag | kOSKextLogLinkFlag,
"Can't allocate link context to load kext %s.",
getIdentifierCString());
goto finish;
}
bzero(kxlddeps, num_kxlddeps * sizeof(*kxlddeps));
for (i = 0; i < num_kxlddeps; ++i ) {
OSKext * dependency = OSDynamicCast(OSKext, linkDependencies->getObject(i));
if (dependency->isInterface()) {
OSKext *interfaceTargetKext = NULL;
OSData * interfaceTarget = NULL;
if (dependency->isKernelComponent()) {
interfaceTargetKext = sKernelKext;
interfaceTarget = sKernelKext->linkedExecutable;
} else {
interfaceTargetKext = OSDynamicCast(OSKext,
dependency->dependencies->getObject(0));
interfaceTarget = interfaceTargetKext->linkedExecutable;
}
if (!interfaceTarget) {
goto finish;
}
kxlddeps[i].kext = (u_char *) interfaceTarget->getBytesNoCopy();
kxlddeps[i].kext_size = interfaceTarget->getLength();
kxlddeps[i].kext_name = strdup(interfaceTargetKext->getIdentifierCString());
kxlddeps[i].interface = (u_char *) dependency->linkedExecutable->getBytesNoCopy();
kxlddeps[i].interface_size = dependency->linkedExecutable->getLength();
kxlddeps[i].interface_name = strdup(dependency->getIdentifierCString());
} else {
kxlddeps[i].kext = (u_char *) dependency->linkedExecutable->getBytesNoCopy();
kxlddeps[i].kext_size = dependency->linkedExecutable->getLength();
kxlddeps[i].kext_name = strdup(dependency->getIdentifierCString());
}
kxlddeps[i].is_direct_dependency = (i < numDirectDependencies);
}
kxldHeaderPtr = &kxld_header;
#if DEBUG
OSKextLog(this,
kOSKextLogExplicitLevel |
kOSKextLogLoadFlag | kOSKextLogLinkFlag,
"Kext %s - calling kxld_link_file:\n"
" kxld_context: %p\n"
" executable: %p executable_length: %d\n"
" user_data: %p\n"
" kxld_dependencies: %p num_dependencies: %d\n"
" kxld_header_ptr: %p kmod_info_ptr: %p\n",
getIdentifierCString(), sKxldContext,
theExecutable->getBytesNoCopy(), theExecutable->getLength(),
this, kxlddeps, num_kxlddeps,
kxldHeaderPtr, &kmod_info);
#endif
kxldResult = kxld_link_file(sKxldContext,
(u_char *)theExecutable->getBytesNoCopy(),
theExecutable->getLength(),
getIdentifierCString(), this, kxlddeps, num_kxlddeps,
(u_char **)kxldHeaderPtr, (kxld_addr_t *)&kmod_info);
if (kxldResult != KERN_SUCCESS) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Can't load kext %s - link failed.",
getIdentifierCString());
result = kOSKextReturnLinkError;
goto finish;
}
#if !defined(__i386__) && !defined(__x86_64__)
flush_dcache(kmod_info->address, kmod_info->size, false);
invalidate_icache(kmod_info->address, kmod_info->size, false);
#endif
register_kmod:
if (isInterface()) {
kmod_info = (kmod_info_t *)kalloc(sizeof(kmod_info_t));
if (!kmod_info) {
result = KERN_MEMORY_ERROR;
goto finish;
}
bzero(kmod_info, sizeof(kmod_info_t));
kmod_info->info_version = KMOD_INFO_VERSION;
interfaceUUID = copyUUID();
}
kmod_info->id = loadTag = sNextLoadTag++;
kmod_info->reference_count = 0;
string = getIdentifierCString();
strlcpy(kmod_info->name, string, sizeof(kmod_info->name));
string = versCString;
strlcpy(kmod_info->version, string, sizeof(kmod_info->version));
num_kmod_refs = getNumDependencies();
if (num_kmod_refs) {
kmod_info->reference_list = (kmod_reference_t *)kalloc(
num_kmod_refs * sizeof(kmod_reference_t));
if (!kmod_info->reference_list) {
result = KERN_MEMORY_ERROR;
goto finish;
}
bzero(kmod_info->reference_list,
num_kmod_refs * sizeof(kmod_reference_t));
for (uint32_t refIndex = 0; refIndex < num_kmod_refs; refIndex++) {
kmod_reference_t * ref = &(kmod_info->reference_list[refIndex]);
OSKext * refKext = OSDynamicCast(OSKext, dependencies->getObject(refIndex));
ref->info = refKext->kmod_info;
ref->info->reference_count++;
if (refIndex + 1 < num_kmod_refs) {
ref->next = kmod_info->reference_list + refIndex + 1;
}
}
}
if (!isInterface() && linkedExecutable) {
OSKextLog(this,
kOSKextLogProgressLevel |
kOSKextLogLoadFlag,
"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)kmod_info->id);
}
result = setVMProtections();
if (result != KERN_SUCCESS) {
goto finish;
}
result = kOSReturnSuccess;
finish:
OSSafeRelease(linkDependencies);
for (i = 0; i < num_kxlddeps; ++i ) {
size_t size;
if (kxlddeps[i].kext_name) {
size = 1 + strlen(kxlddeps[i].kext_name);
kfree(kxlddeps[i].kext_name, size);
}
if (kxlddeps[i].interface_name) {
size = 1 + strlen(kxlddeps[i].interface_name);
kfree(kxlddeps[i].interface_name, size);
}
}
if (kxlddeps) kfree(kxlddeps, (num_kxlddeps * sizeof(*kxlddeps)));
setExecutable(NULL);
if (result != kOSReturnSuccess) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Failed to load executable for kext %s.",
getIdentifierCString());
if (kmod_info && kmod_info->reference_list) {
kfree(kmod_info->reference_list,
num_kmod_refs * sizeof(kmod_reference_t));
}
if (isInterface()) {
kfree(kmod_info, sizeof(kmod_info_t));
}
kmod_info = NULL;
if (linkedExecutable) {
linkedExecutable->release();
linkedExecutable = NULL;
}
}
return result;
}
void
OSKext::jettisonLinkeditSegment(void)
{
kernel_mach_header_t * machhdr = (kernel_mach_header_t *)kmod_info->address;
kernel_segment_command_t * linkedit = NULL;
vm_size_t linkeditsize, kextsize;
vm_offset_t linkeditaddr = 0;
OSData * data = NULL;
if (sKeepSymbols || isLibrary() || !isExecutable() || !linkedExecutable) {
goto finish;
}
linkedit = getsegbynamefromheader(machhdr, SEG_LINKEDIT);
if (!linkedit) {
goto finish;
}
if (round_page(kmod_info->address + kmod_info->size) !=
round_page(linkedit->vmaddr + linkedit->vmsize))
{
goto finish;
}
linkeditsize = round_page(linkedit->vmsize);
kextsize = kmod_info->size - linkeditsize;
linkeditaddr = trunc_page(linkedit->vmaddr);
data = OSData::withBytesNoCopy((void *)kmod_info->address, kextsize);
if (!data) {
goto finish;
}
data->setDeallocFunction(osdata_kext_free);
if (KERN_SUCCESS != removeLinkeditHeaders(linkedit)) {
goto finish;
}
kmod_info->size = kextsize;
linkedExecutable->setDeallocFunction(NULL);
linkedExecutable->release();
linkedExecutable = data;
kext_free(linkeditaddr, linkeditsize);
finish:
return;
}
OSReturn
OSKext::removeLinkeditHeaders(kernel_segment_command_t *linkedit)
{
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;
kext_map = kext_get_vm_map(kmod_info);
if (!kext_map) {
result = KERN_MEMORY_ERROR;
goto finish;
}
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;
}
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;
switch (lc->cmd) {
case LC_SEGMENT:
case LC_SEGMENT_64:
if (src != (u_char *)linkedit) break;
case LC_SYMTAB:
case LC_DYSYMTAB:
bzero(src, cmdsize);
machhdr->ncmds--;
machhdr->sizeofcmds -= cmdsize;
continue;
}
memmove(dst, src, cmdsize);
dst += cmdsize;
}
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;
}
result = KERN_SUCCESS;
finish:
return result;
}
void
OSKext::setLinkedExecutable(OSData * anExecutable)
{
if (linkedExecutable) {
panic("Attempt to set linked executable on kext "
"that already has one (%s).\n",
getIdentifierCString());
}
linkedExecutable = anExecutable;
linkedExecutable->retain();
return;
}
#if CONFIG_DTRACE
void
OSKext::registerKextsWithDTrace(void)
{
uint32_t count = sLoadedKexts->getCount();
uint32_t i;
IORecursiveLockLock(sKextLock);
for (i = 0; i < count; i++) {
OSKext * thisKext = NULL;
thisKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i));
if (!thisKext || !thisKext->isExecutable()) {
continue;
}
thisKext->registerWithDTrace();
}
IORecursiveLockUnlock(sKextLock);
return;
}
extern "C" {
extern int (*dtrace_modload)(struct kmod_info *);
extern int (*dtrace_modunload)(struct kmod_info *);
};
void
OSKext::registerWithDTrace(void)
{
if (!flags.dtraceInitialized && (dtrace_modload != NULL)) {
(void)(*dtrace_modload)(kmod_info);
flags.dtraceInitialized = true;
jettisonLinkeditSegment();
}
return;
}
void
OSKext::unregisterWithDTrace(void)
{
if (flags.dtraceInitialized && (dtrace_modunload != NULL)) {
(void)(*dtrace_modunload)(kmod_info);
flags.dtraceInitialized = false;
}
return;
}
#endif
OSReturn
OSKext::setVMProtections(void)
{
vm_map_t kext_map = NULL;
kernel_segment_command_t * seg = NULL;
vm_map_offset_t start = 0;
vm_map_offset_t end = 0;
OSReturn result = kOSReturnError;
if (!kmod_info->address && !kmod_info->size) {
result = kOSReturnSuccess;
goto finish;
}
kext_map = kext_get_vm_map(kmod_info);
if (!kext_map) {
result = KERN_MEMORY_ERROR;
goto finish;
}
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;
}
seg = firstsegfromheader((kernel_mach_header_t *)kmod_info->address);
while (seg) {
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;
}
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;
}
if (segmentShouldBeWired(seg)) {
result = vm_map_wire(kext_map, start, end, seg->initprot, FALSE);
if (result != KERN_SUCCESS) {
goto finish;
}
}
seg = nextsegfromheader((kernel_mach_header_t *) kmod_info->address, seg);
}
finish:
return result;
}
boolean_t
OSKext::segmentShouldBeWired(kernel_segment_command_t *seg)
{
return (sKeepSymbols || strncmp(seg->segname, SEG_LINKEDIT, sizeof(seg->segname)));
}
OSReturn
OSKext::validateKextMapping(bool startFlag)
{
OSReturn result = kOSReturnError;
const char * whichOp = startFlag ? "start" : "stop";
kern_return_t kern_result = 0;
vm_map_t kext_map = NULL;
kernel_segment_command_t * seg = NULL;
mach_vm_address_t address = 0;
mach_vm_size_t size = 0;
uint32_t depth = 0;
mach_msg_type_number_t count;
vm_region_submap_short_info_data_64_t info;
count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
bzero(&info, sizeof(info));
if (!kmod_info) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Kext %s - NULL kmod_info pointer.",
getIdentifierCString());
result = kOSKextReturnBadData;
goto finish;
}
if (startFlag) {
address = (mach_vm_address_t)kmod_info->start;
} else {
address = (mach_vm_address_t)kmod_info->stop;
}
if (!address) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Kext %s - NULL module %s pointer.",
getIdentifierCString(), whichOp);
result = kOSKextReturnBadData;
goto finish;
}
kext_map = kext_get_vm_map(kmod_info);
depth = (kernel_map == kext_map) ? 1 : 2;
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 (startFlag) {
kern_result = mach_vm_region_recurse(kernel_map, &address, &size, &depth,
(vm_region_recurse_info_t)&info, &count);
if (kern_result != KERN_SUCCESS) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Kext %s - bad %s pointer %p.",
getIdentifierCString(),
whichOp, (void *)address);
result = kOSKextReturnBadData;
goto finish;
}
if (!(info.protection & VM_PROT_EXECUTE)) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Kext %s - memory region containing module %s function "
"is not executable.",
getIdentifierCString(), whichOp);
result = kOSKextReturnBadData;
goto finish;
}
seg = firstsegfromheader((kernel_mach_header_t *)kmod_info->address);
while (seg) {
if (!verifySegmentMapping(seg)) {
result = kOSKextReturnBadData;
goto finish;
}
seg = nextsegfromheader((kernel_mach_header_t *) kmod_info->address, seg);
}
}
result = kOSReturnSuccess;
finish:
return result;
}
boolean_t
OSKext::verifySegmentMapping(kernel_segment_command_t *seg)
{
mach_vm_address_t address = 0;
if (!segmentShouldBeWired(seg)) return true;
for (address = seg->vmaddr;
address < round_page(seg->vmaddr + seg->vmsize);
address += PAGE_SIZE)
{
if (!pmap_find_phys(kernel_pmap, (vm_offset_t)address)) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Kext %s - page %p is not backed by physical memory.",
getIdentifierCString(),
(void *)address);
return false;
}
}
return true;
}
OSReturn
OSKext::start(bool startDependenciesFlag)
{
OSReturn result = kOSReturnError;
kern_return_t (* startfunc)(kmod_info_t *, void *);
unsigned int i, count;
void * kmodStartData = NULL; #if CONFIG_MACF_KEXT
mach_msg_type_number_t kmodStartDataCount = 0;
#endif
if (isStarted() || isInterface() || isKernelComponent()) {
result = kOSReturnSuccess;
goto finish;
}
if (!isLoaded()) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Attempt to start nonloaded kext %s.",
getIdentifierCString());
result = kOSKextReturnInvalidArgument;
goto finish;
}
if (!sLoadEnabled) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Kext loading is disabled (attempt to start kext %s).",
getIdentifierCString());
result = kOSKextReturnDisabled;
goto finish;
}
result = validateKextMapping( true);
if (result != kOSReturnSuccess) {
goto finish;
}
startfunc = kmod_info->start;
count = getNumDependencies();
for (i = 0; i < count; i++) {
OSKext * dependency = OSDynamicCast(OSKext, dependencies->getObject(i));
if (dependency == NULL) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Kext %s start - internal error, dependency disappeared.",
getIdentifierCString());
goto finish;
}
if (!dependency->isStarted()) {
if (startDependenciesFlag) {
OSReturn dependencyResult =
dependency->start(startDependenciesFlag);
if (dependencyResult != KERN_SUCCESS) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Kext %s start - dependency %s failed to start (error 0x%x).",
getIdentifierCString(),
dependency->getIdentifierCString(),
dependencyResult);
goto finish;
}
} else {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Not starting %s - dependency %s not started yet.",
getIdentifierCString(),
dependency->getIdentifierCString());
result = kOSKextReturnStartStopError; goto finish;
}
}
}
#if CONFIG_MACF_KEXT
kmodStartData = MACFCopyModuleDataForKext(this, &kmodStartDataCount);
#endif
OSKextLog(this,
kOSKextLogDetailLevel |
kOSKextLogLoadFlag,
"Kext %s calling module start function.",
getIdentifierCString());
flags.starting = 1;
#if !CONFIG_STATIC_CPPINIT
result = OSRuntimeInitializeCPP(kmod_info, NULL);
if (result == KERN_SUCCESS) {
#endif
result = startfunc(kmod_info, kmodStartData);
#if !CONFIG_STATIC_CPPINIT
if (result != KERN_SUCCESS) {
(void) OSRuntimeFinalizeCPP(kmod_info, NULL);
}
}
#endif
flags.starting = 0;
if (result == KERN_SUCCESS) {
flags.started = 1;
OSKextLog(this,
kOSKextLogProgressLevel |
kOSKextLogLoadFlag,
"Kext %s is now started.",
getIdentifierCString());
} else {
invokeOrCancelRequestCallbacks(
kOSKextReturnStartStopError,
false);
OSKextLog(this,
kOSKextLogProgressLevel |
kOSKextLogLoadFlag,
"Kext %s did not start (return code 0x%x).",
getIdentifierCString(), result);
}
finish:
#if CONFIG_MACF_KEXT
if (kmodStartData) {
kmem_free(kernel_map, (vm_offset_t)kmodStartData, kmodStartDataCount);
}
#endif
return result;
}
bool OSKext::canUnloadKextWithIdentifier(
OSString * kextIdentifier,
bool checkClassesFlag)
{
bool result = false;
OSKext * aKext = NULL;
IORecursiveLockLock(sKextLock);
aKext = OSDynamicCast(OSKext, sKextsByID->getObject(kextIdentifier));
if (!aKext) {
goto finish; }
if (aKext->isLoaded()) {
if (aKext->getRetainCount() > kOSKextMinLoadedRetainCount) {
goto finish;
}
if (checkClassesFlag && aKext->hasOSMetaClassInstances()) {
goto finish;
}
}
result = true;
finish:
IORecursiveLockUnlock(sKextLock);
return result;
}
OSReturn
OSKext::stop(void)
{
OSReturn result = kOSReturnError;
kern_return_t (*stopfunc)(kmod_info_t *, void *);
if (!isStarted() || isInterface()) {
result = kOSReturnSuccess;
goto finish;
}
if (!isLoaded()) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Attempt to stop nonloaded kext %s.",
getIdentifierCString());
result = kOSKextReturnInvalidArgument;
goto finish;
}
if (getRetainCount() > kOSKextMinLoadedRetainCount) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Kext %s - C++ instances; can't stop.",
getIdentifierCString());
result = kOSKextReturnInUse;
goto finish;
}
if (getRetainCount() > kOSKextMinLoadedRetainCount) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Kext %s - has references (linkage or tracking object); "
"can't stop.",
getIdentifierCString());
result = kOSKextReturnInUse;
goto finish;
}
result = validateKextMapping( false);
if (result != kOSReturnSuccess) {
goto finish;
}
stopfunc = kmod_info->stop;
if (stopfunc) {
OSKextLog(this,
kOSKextLogDetailLevel |
kOSKextLogLoadFlag,
"Kext %s calling module stop function.",
getIdentifierCString());
flags.stopping = 1;
result = stopfunc(kmod_info, NULL);
#if !CONFIG_STATIC_CPPINIT
if (result == KERN_SUCCESS) {
result = OSRuntimeFinalizeCPP(kmod_info, NULL);
}
#endif
flags.stopping = 0;
if (result == KERN_SUCCESS) {
flags.started = 0;
OSKextLog(this,
kOSKextLogDetailLevel |
kOSKextLogLoadFlag,
"Kext %s is now stopped and ready to unload.",
getIdentifierCString());
} else {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Kext %s did not stop (return code 0x%x).",
getIdentifierCString(), result);
result = kOSKextReturnStartStopError;
}
}
finish:
return result;
}
OSReturn
OSKext::unload(void)
{
OSReturn result = kOSReturnError;
unsigned int index;
uint32_t num_kmod_refs = 0;
if (!sUnloadEnabled) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Kext unloading is disabled (%s).",
this->getIdentifierCString());
result = kOSKextReturnDisabled;
goto finish;
}
if (getRetainCount() > kOSKextMinLoadedRetainCount) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogKextBookkeepingFlag,
"Can't unload kext %s; outstanding references (linkage or tracking object).",
getIdentifierCString());
result = kOSKextReturnInUse;
goto finish;
}
if (hasOSMetaClassInstances()) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag | kOSKextLogKextBookkeepingFlag,
"Can't unload kext %s; classes have instances:",
getIdentifierCString());
reportOSMetaClassInstances(kOSKextLogErrorLevel |
kOSKextLogLoadFlag | kOSKextLogKextBookkeepingFlag);
result = kOSKextReturnInUse;
goto finish;
}
if (!isLoaded()) {
result = kOSReturnSuccess;
goto finish;
}
if (isKernelComponent()) {
result = kOSKextReturnInvalidArgument;
goto finish;
}
flags.unloading = 1;
savePanicString( false);
if (isStarted()) {
result = stop();
if (result != KERN_SUCCESS) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Kext %s can't unload - module stop returned 0x%x.",
getIdentifierCString(), (unsigned)result);
result = kOSKextReturnStartStopError;
goto finish;
}
}
OSKextLog(this,
kOSKextLogProgressLevel |
kOSKextLogLoadFlag,
"Kext %s unloading.",
getIdentifierCString());
if (metaClasses) {
metaClasses->flushCollection();
}
index = sLoadedKexts->getNextIndexOfObject(this, 0);
if (index != (unsigned int)-1) {
sLoadedKexts->removeObject(index);
OSKext * nextKext = OSDynamicCast(OSKext,
sLoadedKexts->getObject(index));
if (nextKext) {
if (index > 0) {
OSKext * gapKext = OSDynamicCast(OSKext,
sLoadedKexts->getObject(index - 1));
nextKext->kmod_info->next = gapKext->kmod_info;
} else {
nextKext->kmod_info->next = NULL;
}
}
OSKext * lastKext = OSDynamicCast(OSKext, sLoadedKexts->getLastObject());
if (lastKext && !lastKext->isKernel()) {
kmod = lastKext->kmod_info;
} else {
kmod = NULL; }
}
num_kmod_refs = getNumDependencies();
if (num_kmod_refs && kmod_info && kmod_info->reference_list) {
for (uint32_t refIndex = 0; refIndex < num_kmod_refs; refIndex++) {
kmod_reference_t * ref = &(kmod_info->reference_list[refIndex]);
ref->info->reference_count--;
}
kfree(kmod_info->reference_list,
num_kmod_refs * sizeof(kmod_reference_t));
}
#if CONFIG_DTRACE
unregisterWithDTrace();
#endif
notifyKextUnloadObservers(this);
if (linkedExecutable) {
if (!isInterface()) {
kernel_segment_command_t *seg = NULL;
vm_map_t kext_map = kext_get_vm_map(kmod_info);
if (!kext_map) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Failed to free kext %s; couldn't find the kext map.",
getIdentifierCString());
result = kOSKextReturnInternalError;
goto finish;
}
OSKextLog(this,
kOSKextLogProgressLevel |
kOSKextLogLoadFlag,
"Kext %s unwiring and unmapping linked executable.",
getIdentifierCString());
seg = firstsegfromheader((kernel_mach_header_t *)kmod_info->address);
while (seg) {
if (segmentShouldBeWired(seg)) {
result = vm_map_unwire(kext_map, seg->vmaddr,
seg->vmaddr + seg->vmsize, FALSE);
if (result != KERN_SUCCESS) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Failed to unwire kext %s.",
getIdentifierCString());
result = kOSKextReturnInternalError;
goto finish;
}
}
seg = nextsegfromheader((kernel_mach_header_t *) kmod_info->address, seg);
}
}
OSSafeReleaseNULL(linkedExecutable);
}
if (isInterface()) {
kfree(kmod_info, sizeof(kmod_info_t));
}
kmod_info = NULL;
flags.loaded = false;
flushDependencies();
if (isPrelinked()) {
sUnloadedPrelinkedKexts->setObject(bundleID);
}
OSKextLog(this,
kOSKextLogProgressLevel | kOSKextLogLoadFlag,
"Kext %s unloaded.", getIdentifierCString());
queueKextNotification(kKextRequestPredicateUnloadNotification,
OSDynamicCast(OSString, bundleID));
finish:
OSKext::saveLoadedKextPanicList();
OSKext::updateLoadedKextSummaries();
flags.unloading = 0;
return result;
}
OSReturn
OSKext::queueKextNotification(
const char * notificationName,
OSString * kextIdentifier)
{
OSReturn result = kOSReturnError;
OSDictionary * loadRequest = NULL;
if (!kextIdentifier) {
result = kOSKextReturnInvalidArgument;
goto finish;
}
result = _OSKextCreateRequest(notificationName, &loadRequest);
if (result != kOSReturnSuccess) {
goto finish;
}
if (!_OSKextSetRequestArgument(loadRequest,
kKextRequestArgumentBundleIdentifierKey, kextIdentifier)) {
result = kOSKextReturnNoMemory;
goto finish;
}
if (!sKernelRequests->setObject(loadRequest)) {
result = kOSKextReturnNoMemory;
goto finish;
}
OSKext::pingKextd();
result = kOSReturnSuccess;
finish:
OSSafeRelease(loadRequest);
return result;
}
static void
_OSKextConsiderDestroyingLinkContext(
__unused thread_call_param_t p0,
__unused thread_call_param_t p1)
{
IORecursiveLockLock(sKextLock);
IORecursiveLockLock(sKextInnerLock);
if (sConsiderUnloadsCalled && sKxldContext) {
kxld_destroy_context(sKxldContext);
sKxldContext = NULL;
}
if (sDestroyLinkContextThread) {
if (!thread_call_free(sDestroyLinkContextThread)) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogGeneralFlag,
"thread_call_free() failed for kext link context.");
}
sDestroyLinkContextThread = 0;
}
IORecursiveLockUnlock(sKextInnerLock);
IORecursiveLockUnlock(sKextLock);
return;
}
void
OSKext::considerDestroyingLinkContext(void)
{
IORecursiveLockLock(sKextInnerLock);
if (sDestroyLinkContextThread) {
goto finish;
}
sDestroyLinkContextThread = thread_call_allocate(
&_OSKextConsiderDestroyingLinkContext, 0);
if (!sDestroyLinkContextThread) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag | kOSKextLogLinkFlag,
"Can't create thread to destroy kext link context.");
goto finish;
}
thread_call_enter(sDestroyLinkContextThread);
finish:
IORecursiveLockUnlock(sKextInnerLock);
return;
}
#if PRAGMA_MARK
#pragma mark Autounload
#endif
OSReturn
OSKext::autounloadKext(OSKext * aKext)
{
OSReturn result = kOSKextReturnInUse;
if ((aKext->getRetainCount() > kOSKextMinLoadedRetainCount) ||
!aKext->flags.autounloadEnabled ||
aKext->isKernelComponent()) {
goto finish;
}
if (aKext->flags.delayAutounload) {
OSKextLog(aKext,
kOSKextLogProgressLevel |
kOSKextLogLoadFlag | kOSKextLogKextBookkeepingFlag,
"Kext %s has delayed autounload set; skipping and clearing flag.",
aKext->getIdentifierCString());
aKext->flags.delayAutounload = 0;
goto finish;
}
if (aKext->hasOSMetaClassInstances() ||
aKext->countRequestCallbacks()) {
goto finish;
}
result = OSKext::removeKext(aKext);
finish:
return result;
}
void
_OSKextConsiderUnloads(
__unused thread_call_param_t p0,
__unused thread_call_param_t p1)
{
bool didUnload = false;
unsigned int count, i;
IORecursiveLockLock(sKextLock);
IORecursiveLockLock(sKextInnerLock);
OSKext::flushNonloadedKexts( true);
IOLockLock(sKextSummariesLock);
if (sPrevLoadedKextSummaries) {
kmem_free(kernel_map, (vm_offset_t)sPrevLoadedKextSummaries,
sPrevLoadedKextSummariesAllocSize);
sPrevLoadedKextSummaries = NULL;
sPrevLoadedKextSummariesAllocSize = 0;
}
IOLockUnlock(sKextSummariesLock);
if (sSystemSleep) {
goto finish;
}
OSKextLog( NULL,
kOSKextLogProgressLevel |
kOSKextLogLoadFlag,
"Checking for unused kexts to autounload.");
count = sRequestCallbackRecords->getCount();
if (count) {
i = count - 1;
do {
OSDictionary * callbackRecord = OSDynamicCast(OSDictionary,
sRequestCallbackRecords->getObject(i));
OSBoolean * stale = OSDynamicCast(OSBoolean,
callbackRecord->getObject(kKextRequestStaleKey));
if (stale == kOSBooleanTrue) {
OSKext::invokeRequestCallback(callbackRecord,
kOSKextReturnTimeout);
} else {
callbackRecord->setObject(kKextRequestStaleKey,
kOSBooleanTrue);
}
} while (i--);
}
do {
didUnload = false;
count = sLoadedKexts->getCount();
if (count) {
i = count - 1;
do {
OSKext * thisKext = OSDynamicCast(OSKext,
sLoadedKexts->getObject(i));
didUnload = (kOSReturnSuccess == OSKext::autounloadKext(thisKext));
} while (i--);
}
} while (didUnload);
finish:
sConsiderUnloadsPending = false;
sConsiderUnloadsExecuted = true;
(void) OSKext::considerRebuildOfPrelinkedKernel(NULL);
IORecursiveLockUnlock(sKextInnerLock);
IORecursiveLockUnlock(sKextLock);
return;
}
void OSKext::considerUnloads(Boolean rescheduleOnlyFlag)
{
AbsoluteTime when;
IORecursiveLockLock(sKextInnerLock);
if (!sUnloadCallout) {
sUnloadCallout = thread_call_allocate(&_OSKextConsiderUnloads, 0);
}
if (rescheduleOnlyFlag && !sConsiderUnloadsPending) {
goto finish;
}
thread_call_cancel(sUnloadCallout);
if (OSKext::getAutounloadEnabled() && !sSystemSleep) {
clock_interval_to_deadline(sConsiderUnloadDelay,
1000 * 1000 * 1000, &when);
OSKextLog( NULL,
kOSKextLogProgressLevel |
kOSKextLogLoadFlag,
"%scheduling %sscan for unused kexts in %lu seconds.",
sConsiderUnloadsPending ? "Res" : "S",
sConsiderUnloadsCalled ? "" : "initial ",
(unsigned long)sConsiderUnloadDelay);
sConsiderUnloadsPending = true;
thread_call_enter_delayed(sUnloadCallout, when);
}
finish:
if (!sConsiderUnloadsCalled) {
sConsiderUnloadsCalled = true;
OSKext::considerDestroyingLinkContext();
}
IORecursiveLockUnlock(sKextInnerLock);
return;
}
extern "C" {
IOReturn OSKextSystemSleepOrWake(UInt32 messageType)
{
IORecursiveLockLock(sKextInnerLock);
if (messageType == kIOMessageSystemWillSleep) {
if (sUnloadCallout) {
thread_call_cancel(sUnloadCallout);
}
sSystemSleep = true;
} else if (messageType == kIOMessageSystemHasPoweredOn) {
sSystemSleep = false;
}
IORecursiveLockUnlock(sKextInnerLock);
return kIOReturnSuccess;
}
};
#if PRAGMA_MARK
#pragma mark Prelinked Kernel
#endif
void
OSKext::considerRebuildOfPrelinkedKernel(OSString * moduleName)
{
OSReturn checkResult = kOSReturnError;
static bool requestedPrelink = false;
OSDictionary * prelinkRequest = NULL;
IORecursiveLockLock(sKextLock);
if (moduleName) {
int count = sUnloadedPrelinkedKexts->getCount();
int i;
for (i = 0; i < count; i++) {
const OSSymbol * myBundleID;
myBundleID = OSDynamicCast(OSSymbol, sUnloadedPrelinkedKexts->getObject(i));
if (!myBundleID) continue;
if (moduleName->isEqualTo(myBundleID->getCStringNoCopy())) {
OSKextLog( NULL,
kOSKextLogDetailLevel |
kOSKextLogArchiveFlag,
"bundleID %s already in cache skipping rebuild.",
myBundleID->getCStringNoCopy());
goto finish;
}
}
(void) OSKext::setDeferredLoadSucceeded();
}
if (!sDeferredLoadSucceeded || !sConsiderUnloadsExecuted ||
sSafeBoot || requestedPrelink)
{
goto finish;
}
OSKextLog( NULL,
kOSKextLogProgressLevel |
kOSKextLogArchiveFlag,
"Requesting build of prelinked kernel.");
checkResult = _OSKextCreateRequest(kKextRequestPredicateRequestPrelink,
&prelinkRequest);
if (checkResult != kOSReturnSuccess) {
goto finish;
}
if (!sKernelRequests->setObject(prelinkRequest)) {
goto finish;
}
OSKext::pingKextd();
requestedPrelink = true;
finish:
IORecursiveLockUnlock(sKextLock);
OSSafeRelease(prelinkRequest);
return;
}
#if PRAGMA_MARK
#pragma mark Dependencies
#endif
bool
OSKext::resolveDependencies(
OSArray * loopStack)
{
bool result = false;
OSArray * localLoopStack = NULL; bool addedToLoopStack = false;
OSDictionary * libraries = NULL; OSCollectionIterator * libraryIterator = NULL; OSString * libraryID = NULL; OSString * infoString = NULL; OSString * readableString = NULL; OSKext * libraryKext = NULL; bool hasRawKernelDependency = false;
bool hasKernelDependency = false;
bool hasKPIDependency = false;
bool hasPrivateKPIDependency = false;
unsigned int count;
if (flags.hasAllDependencies) {
result = true;
goto finish;
}
if (loopStack) {
if (loopStack->getNextIndexOfObject(this, 0) != (unsigned int)-1) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogDependenciesFlag,
"Kext %s has a dependency loop; can't resolve dependencies.",
getIdentifierCString());
goto finish;
}
} else {
OSKextLog(this,
kOSKextLogStepLevel |
kOSKextLogDependenciesFlag,
"Kext %s resolving dependencies.",
getIdentifierCString());
loopStack = OSArray::withCapacity(6); if (!loopStack) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogDependenciesFlag,
"Kext %s can't create bookkeeping stack to resolve dependencies.",
getIdentifierCString());
goto finish;
}
localLoopStack = loopStack;
}
if (!loopStack->setObject(this)) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogDependenciesFlag,
"Kext %s - internal error resolving dependencies.",
getIdentifierCString());
goto finish;
}
addedToLoopStack = true;
flushDependencies();
if (dependencies) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogDependenciesFlag,
"Kext %s - internal error resolving dependencies.",
getIdentifierCString());
}
libraries = OSDynamicCast(OSDictionary,
getPropertyForHostArch(kOSBundleLibrariesKey));
if (libraries == NULL || libraries->getCount() == 0) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogValidationFlag | kOSKextLogDependenciesFlag,
"Kext %s - can't resolve dependencies; %s missing/invalid type.",
getIdentifierCString(), kOSBundleLibrariesKey);
goto finish;
}
dependencies = OSArray::withCapacity(libraries->getCount());
if (!dependencies) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogDependenciesFlag,
"Kext %s - can't allocate dependencies array.",
getIdentifierCString());
goto finish;
}
libraryIterator = OSCollectionIterator::withCollection(libraries);
if (!libraryIterator) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogDependenciesFlag,
"Kext %s - can't allocate dependencies iterator.",
getIdentifierCString());
goto finish;
}
while ((libraryID = OSDynamicCast(OSString,
libraryIterator->getNextObject()))) {
const char * library_id = libraryID->getCStringNoCopy();
OSString * libraryVersion = OSDynamicCast(OSString,
libraries->getObject(libraryID));
if (libraryVersion == NULL) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogValidationFlag | kOSKextLogDependenciesFlag,
"Kext %s - illegal type in OSBundleLibraries.",
getIdentifierCString());
goto finish;
}
OSKextVersion libraryVers =
OSKextParseVersionString(libraryVersion->getCStringNoCopy());
if (libraryVers == -1) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogValidationFlag | kOSKextLogDependenciesFlag,
"Kext %s - invalid library version %s.",
getIdentifierCString(),
libraryVersion->getCStringNoCopy());
goto finish;
}
libraryKext = OSDynamicCast(OSKext, sKextsByID->getObject(libraryID));
if (libraryKext == NULL) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogDependenciesFlag,
"Kext %s - library kext %s not found.",
getIdentifierCString(), library_id);
goto finish;
}
if (!libraryKext->isCompatibleWithVersion(libraryVers)) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogDependenciesFlag,
"Kext %s - library kext %s not compatible "
"with requested version %s.",
getIdentifierCString(), library_id,
libraryVersion->getCStringNoCopy());
goto finish;
}
if (this->isPrelinked() &&
libraryKext->declaresExecutable() &&
!libraryKext->isPrelinked()) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogDependenciesFlag,
"Kext %s (prelinked) - library kext %s (v%s) not prelinked.",
getIdentifierCString(), library_id,
libraryVersion->getCStringNoCopy());
goto finish;
}
if (!libraryKext->resolveDependencies(loopStack)) {
goto finish;
}
if (libraryKext->declaresExecutable() ||
libraryKext->isInterface()) {
if (dependencies->getNextIndexOfObject(libraryKext, 0) == (unsigned)-1) {
dependencies->setObject(libraryKext);
OSKextLog(this,
kOSKextLogDetailLevel |
kOSKextLogDependenciesFlag,
"Kext %s added dependency %s.",
getIdentifierCString(),
libraryKext->getIdentifierCString());
}
} else {
int numLibDependencies = libraryKext->getNumDependencies();
OSArray * libraryDependencies = libraryKext->getDependencies();
int index;
if (numLibDependencies) {
OSKextLog(this,
kOSKextLogDetailLevel |
kOSKextLogDependenciesFlag,
"Kext %s pulling %d dependencies from codeless library %s.",
getIdentifierCString(),
numLibDependencies,
libraryKext->getIdentifierCString());
}
for (index = 0; index < numLibDependencies; index++) {
OSKext * thisLibDependency = OSDynamicCast(OSKext,
libraryDependencies->getObject(index));
if (dependencies->getNextIndexOfObject(thisLibDependency, 0) == (unsigned)-1) {
dependencies->setObject(thisLibDependency);
OSKextLog(this,
kOSKextLogDetailLevel |
kOSKextLogDependenciesFlag,
"Kext %s added dependency %s from codeless library %s.",
getIdentifierCString(),
thisLibDependency->getIdentifierCString(),
libraryKext->getIdentifierCString());
}
}
}
if ((strlen(library_id) == strlen(KERNEL_LIB)) &&
0 == strncmp(library_id, KERNEL_LIB, sizeof(KERNEL_LIB)-1)) {
hasRawKernelDependency = true;
} else if (STRING_HAS_PREFIX(library_id, KERNEL_LIB_PREFIX)) {
hasKernelDependency = true;
} else if (STRING_HAS_PREFIX(library_id, KPI_LIB_PREFIX)) {
hasKPIDependency = true;
if (!strncmp(library_id, PRIVATE_KPI, sizeof(PRIVATE_KPI)-1)) {
hasPrivateKPIDependency = true;
}
}
}
if (hasRawKernelDependency) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogValidationFlag | kOSKextLogDependenciesFlag,
"Error - kext %s declares a dependency on %s, which is not permitted.",
getIdentifierCString(), KERNEL_LIB);
goto finish;
}
#if __LP64__
if (hasKernelDependency) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogValidationFlag | kOSKextLogDependenciesFlag,
"Error - kext %s declares %s dependencies. "
"Only %s* dependencies are supported for 64-bit kexts.",
getIdentifierCString(), KERNEL_LIB, KPI_LIB_PREFIX);
goto finish;
}
if (!hasKPIDependency) {
OSKextLog(this,
kOSKextLogWarningLevel |
kOSKextLogDependenciesFlag,
"Warning - kext %s declares no %s* dependencies. "
"If it uses any KPIs, the link may fail with undefined symbols.",
getIdentifierCString(), KPI_LIB_PREFIX);
}
#else
if (hasKernelDependency && hasKPIDependency) {
OSKextLog(this,
kOSKextLogWarningLevel |
kOSKextLogDependenciesFlag,
"Warning - kext %s has immediate dependencies on both "
"%s* and %s* components; use only one style.",
getIdentifierCString(), KERNEL_LIB, KPI_LIB_PREFIX);
}
if (!hasKernelDependency && !hasKPIDependency) {
OSKextLog(this,
kOSKextLogWarningLevel |
kOSKextLogDependenciesFlag,
"Warning - %s declares no kernel dependencies; using %s.",
getIdentifierCString(), KERNEL6_LIB);
OSKext * kernelKext = OSDynamicCast(OSKext,
sKextsByID->getObject(KERNEL6_LIB));
if (kernelKext) {
dependencies->setObject(kernelKext);
} else {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogDependenciesFlag,
"Error - Library %s not found for %s.",
KERNEL6_LIB, getIdentifierCString());
}
}
if (!hasKPIDependency) {
unsigned int i;
flags.hasBleedthrough = true;
count = getNumDependencies();
for (i = 0; i < count; i++) {
OSKext * dependencyKext = OSDynamicCast(OSKext,
dependencies->getObject(i));
dependencyKext->addBleedthroughDependencies(dependencies);
}
}
#endif
if (hasPrivateKPIDependency) {
bool hasApplePrefix = false;
bool infoCopyrightIsValid = false;
bool readableCopyrightIsValid = false;
hasApplePrefix = STRING_HAS_PREFIX(getIdentifierCString(),
APPLE_KEXT_PREFIX);
infoString = OSDynamicCast(OSString,
getPropertyForHostArch("CFBundleGetInfoString"));
if (infoString) {
infoCopyrightIsValid =
kxld_validate_copyright_string(infoString->getCStringNoCopy());
}
readableString = OSDynamicCast(OSString,
getPropertyForHostArch("NSHumanReadableCopyright"));
if (readableString) {
readableCopyrightIsValid =
kxld_validate_copyright_string(readableString->getCStringNoCopy());
}
if (!hasApplePrefix || (!infoCopyrightIsValid && !readableCopyrightIsValid)) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogDependenciesFlag,
"Error - kext %s declares a dependency on %s. "
"Only Apple kexts may declare a dependency on %s.",
getIdentifierCString(), PRIVATE_KPI, PRIVATE_KPI);
goto finish;
}
}
result = true;
flags.hasAllDependencies = 1;
finish:
if (addedToLoopStack) {
count = loopStack->getCount();
if (count > 0 && (this == loopStack->getObject(count - 1))) {
loopStack->removeObject(count - 1);
} else {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogDependenciesFlag,
"Kext %s - internal error resolving dependencies.",
getIdentifierCString());
}
}
if (result && localLoopStack) {
OSKextLog(this,
kOSKextLogStepLevel |
kOSKextLogDependenciesFlag,
"Kext %s successfully resolved dependencies.",
getIdentifierCString());
}
OSSafeRelease(localLoopStack);
OSSafeRelease(libraryIterator);
return result;
}
bool
OSKext::addBleedthroughDependencies(OSArray * anArray)
{
bool result = false;
unsigned int dependencyIndex, dependencyCount;
dependencyCount = getNumDependencies();
for (dependencyIndex = 0;
dependencyIndex < dependencyCount;
dependencyIndex++) {
OSKext * dependency = OSDynamicCast(OSKext,
dependencies->getObject(dependencyIndex));
if (!dependency) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogDependenciesFlag,
"Kext %s - internal error propagating compatibility dependencies.",
getIdentifierCString());
goto finish;
}
if (anArray->getNextIndexOfObject(dependency, 0) == (unsigned int)-1) {
anArray->setObject(dependency);
}
dependency->addBleedthroughDependencies(anArray);
}
result = true;
finish:
return result;
}
bool
OSKext::flushDependencies(bool forceFlag)
{
bool result = false;
if (!isLoaded() || forceFlag) {
if (dependencies) {
OSKextLog(this,
kOSKextLogProgressLevel |
kOSKextLogDependenciesFlag,
"Kext %s flushing dependencies.",
getIdentifierCString());
OSSafeReleaseNULL(dependencies);
}
if (!isKernelComponent()) {
flags.hasAllDependencies = 0;
}
result = true;
}
return result;
}
uint32_t
OSKext::getNumDependencies(void)
{
if (!dependencies) {
return 0;
}
return dependencies->getCount();
}
OSArray *
OSKext::getDependencies(void)
{
return dependencies;
}
#if PRAGMA_MARK
#pragma mark OSMetaClass Support
#endif
OSReturn
OSKext::addClass(
OSMetaClass * aClass,
uint32_t numClasses)
{
OSReturn result = kOSMetaClassNoInsKModSet;
if (!metaClasses) {
metaClasses = OSSet::withCapacity(numClasses);
if (!metaClasses) {
goto finish;
}
}
if (metaClasses->containsObject(aClass)) {
OSKextLog(this,
kOSKextLogWarningLevel |
kOSKextLogLoadFlag,
"Notice - kext %s has already registered class %s.",
getIdentifierCString(),
aClass->getClassName());
result = kOSReturnSuccess;
goto finish;
}
if (!metaClasses->setObject(aClass)) {
goto finish;
} else {
OSKextLog(this,
kOSKextLogDetailLevel |
kOSKextLogLoadFlag,
"Kext %s registered class %s.",
getIdentifierCString(),
aClass->getClassName());
}
if (!flags.autounloadEnabled) {
const OSMetaClass * metaScan = NULL;
for (metaScan = aClass; metaScan; metaScan = metaScan->getSuperClass()) {
if (metaScan == OSTypeID(IOService)) {
OSKextLog(this,
kOSKextLogProgressLevel |
kOSKextLogLoadFlag,
"Kext %s has IOService subclass %s; enabling autounload.",
getIdentifierCString(),
aClass->getClassName());
flags.autounloadEnabled = 1;
break;
}
}
}
notifyAddClassObservers(this, aClass, flags);
result = kOSReturnSuccess;
finish:
if (result != kOSReturnSuccess) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Kext %s failed to register class %s.",
getIdentifierCString(),
aClass->getClassName());
}
return result;
}
OSReturn
OSKext::removeClass(
OSMetaClass * aClass)
{
OSReturn result = kOSMetaClassNoKModSet;
if (!metaClasses) {
goto finish;
}
if (!metaClasses->containsObject(aClass)) {
OSKextLog(this,
kOSKextLogWarningLevel |
kOSKextLogLoadFlag,
"Notice - kext %s asked to unregister unknown class %s.",
getIdentifierCString(),
aClass->getClassName());
result = kOSReturnSuccess;
goto finish;
}
OSKextLog(this,
kOSKextLogDetailLevel |
kOSKextLogLoadFlag,
"Kext %s unregistering class %s.",
getIdentifierCString(),
aClass->getClassName());
metaClasses->removeObject(aClass);
notifyRemoveClassObservers(this, aClass, flags);
result = kOSReturnSuccess;
finish:
if (result != kOSReturnSuccess) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Failed to unregister kext %s class %s.",
getIdentifierCString(),
aClass->getClassName());
}
return result;
}
OSSet *
OSKext::getMetaClasses(void)
{
return metaClasses;
}
bool
OSKext::hasOSMetaClassInstances(void)
{
bool result = false;
OSCollectionIterator * classIterator = NULL; OSMetaClass * checkClass = NULL;
if (!metaClasses) {
goto finish;
}
classIterator = OSCollectionIterator::withCollection(metaClasses);
if (!classIterator) {
goto finish;
}
while ((checkClass = (OSMetaClass *)classIterator->getNextObject())) {
if (checkClass->getInstanceCount()) {
result = true;
goto finish;
}
}
finish:
OSSafeRelease(classIterator);
return result;
}
void
OSKext::reportOSMetaClassInstances(
const char * kextIdentifier,
OSKextLogSpec msgLogSpec)
{
OSKext * theKext = NULL;
theKext = OSKext::lookupKextWithIdentifier(kextIdentifier);
if (!theKext) {
goto finish;
}
theKext->reportOSMetaClassInstances(msgLogSpec);
finish:
OSSafeRelease(theKext);
return;
}
void
OSKext::reportOSMetaClassInstances(OSKextLogSpec msgLogSpec)
{
OSCollectionIterator * classIterator = NULL; OSMetaClass * checkClass = NULL;
if (!metaClasses) {
goto finish;
}
classIterator = OSCollectionIterator::withCollection(metaClasses);
if (!classIterator) {
goto finish;
}
while ((checkClass = (OSMetaClass *)classIterator->getNextObject())) {
if (checkClass->getInstanceCount()) {
OSKextLog(this,
msgLogSpec,
" Kext %s class %s has %d instance%s.",
getIdentifierCString(),
checkClass->getClassName(),
checkClass->getInstanceCount(),
checkClass->getInstanceCount() == 1 ? "" : "s");
}
}
finish:
OSSafeRelease(classIterator);
return;
}
#if PRAGMA_MARK
#pragma mark User-Space Requests
#endif
OSReturn
OSKext::handleRequest(
host_priv_t hostPriv,
OSKextLogSpec clientLogFilter,
char * requestBuffer,
uint32_t requestLength,
char ** responseOut,
uint32_t * responseLengthOut,
char ** logInfoOut,
uint32_t * logInfoLengthOut)
{
OSReturn result = kOSReturnError;
kern_return_t kmem_result = KERN_FAILURE;
char * response = NULL; uint32_t responseLength = 0;
OSObject * parsedXML = NULL; OSDictionary * requestDict = NULL; OSString * errorString = NULL;
OSData * responseData = NULL; OSObject * responseObject = NULL;
OSSerialize * serializer = NULL;
OSArray * logInfoArray = NULL;
OSString * predicate = NULL; OSString * kextIdentifier = NULL; OSArray * kextIdentifiers = NULL; OSKext * theKext = NULL; OSBoolean * boolArg = NULL;
IORecursiveLockLock(sKextLock);
if (responseOut) {
*responseOut = NULL;
*responseLengthOut = 0;
}
if (logInfoOut) {
*logInfoOut = NULL;
*logInfoLengthOut = 0;
}
OSKext::setUserSpaceLogFilter(clientLogFilter, logInfoOut ? true : false);
if (requestBuffer[requestLength - 1] != '\0') {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogIPCFlag,
"Invalid request from user space (not nul-terminated).");
result = kOSKextReturnBadData;
goto finish;
}
parsedXML = OSUnserializeXML((const char *)requestBuffer, &errorString);
if (parsedXML) {
requestDict = OSDynamicCast(OSDictionary, parsedXML);
}
if (!requestDict) {
const char * errorCString = "(unknown error)";
if (errorString && errorString->getCStringNoCopy()) {
errorCString = errorString->getCStringNoCopy();
} else if (parsedXML) {
errorCString = "not a dictionary";
}
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogIPCFlag,
"Error unserializing request from user space: %s.",
errorCString);
result = kOSKextReturnSerialization;
goto finish;
}
predicate = _OSKextGetRequestPredicate(requestDict);
if (!predicate) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogIPCFlag,
"Recieved kext request from user space with no predicate.");
result = kOSKextReturnInvalidArgument;
goto finish;
}
OSKextLog( NULL,
kOSKextLogDebugLevel |
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;
}
}
kextIdentifier = OSDynamicCast(OSString, _OSKextGetRequestArgument(
requestDict, kKextRequestArgumentBundleIdentifierKey));
kextIdentifiers = OSDynamicCast(OSArray, _OSKextGetRequestArgument(
requestDict, kKextRequestArgumentBundleIdentifierKey));
if (kextIdentifier) {
theKext = OSDynamicCast(OSKext, sKextsByID->getObject(kextIdentifier));
}
boolArg = OSDynamicCast(OSBoolean, _OSKextGetRequestArgument(
requestDict, kKextRequestArgumentValueKey));
result = kOSKextReturnInvalidArgument;
if (predicate->isEqualTo(kKextRequestPredicateStart)) {
if (!kextIdentifier) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogIPCFlag,
"Invalid arguments to kext start request.");
} else if (!theKext) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogIPCFlag,
"Kext %s not found for start request.",
kextIdentifier->getCStringNoCopy());
result = kOSKextReturnNotFound;
} else {
result = theKext->start();
}
} else if (predicate->isEqualTo(kKextRequestPredicateStop)) {
if (!kextIdentifier) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogIPCFlag,
"Invalid arguments to kext stop request.");
} else if (!theKext) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogIPCFlag,
"Kext %s not found for stop request.",
kextIdentifier->getCStringNoCopy());
result = kOSKextReturnNotFound;
} else {
result = theKext->stop();
}
} else if (predicate->isEqualTo(kKextRequestPredicateUnload)) {
if (!kextIdentifier) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogIPCFlag,
"Invalid arguments to kext unload request.");
} else if (!theKext) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogIPCFlag,
"Kext %s not found for unload request.",
kextIdentifier->getCStringNoCopy());
result = kOSKextReturnNotFound;
} else {
OSBoolean * terminateFlag = OSDynamicCast(OSBoolean,
_OSKextGetRequestArgument(requestDict,
kKextRequestArgumentTerminateIOServicesKey));
result = OSKext::removeKext(theKext, terminateFlag == kOSBooleanTrue);
}
} else if (predicate->isEqualTo(kKextRequestPredicateSendResource)) {
result = OSKext::dispatchResource(requestDict);
} else if (predicate->isEqualTo(kKextRequestPredicateGetLoaded)) {
OSBoolean * delayAutounloadBool = NULL;
OSObject * infoKeysRaw = NULL;
OSArray * infoKeys = NULL;
uint32_t infoKeysCount = 0;
delayAutounloadBool = OSDynamicCast(OSBoolean,
_OSKextGetRequestArgument(requestDict,
kKextRequestArgumentDelayAutounloadKey));
if (delayAutounloadBool == kOSBooleanTrue) {
OSKext::considerUnloads( true);
}
infoKeysRaw = _OSKextGetRequestArgument(requestDict,
kKextRequestArgumentInfoKeysKey);
infoKeys = OSDynamicCast(OSArray, infoKeysRaw);
if (infoKeysRaw && !infoKeys) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogIPCFlag,
"Invalid arguments to kext info request.");
goto finish;
}
if (infoKeys) {
infoKeysCount = infoKeys->getCount();
for (uint32_t i = 0; i < infoKeysCount; i++) {
if (!OSDynamicCast(OSString, infoKeys->getObject(i))) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogIPCFlag,
"Invalid arguments to kext info request.");
goto finish;
}
}
}
responseObject = OSKext::copyLoadedKextInfo(kextIdentifiers, infoKeys);
if (!responseObject) {
result = kOSKextReturnInternalError;
} else {
OSKextLog( NULL,
kOSKextLogDebugLevel |
kOSKextLogIPCFlag,
"Returning loaded kext info.");
result = kOSReturnSuccess;
}
} else if (predicate->isEqualTo(kKextRequestPredicateGetKernelLoadAddress)) {
OSNumber * addressNum = NULL; kernel_segment_command_t * textseg = getsegbyname("__TEXT");
if (!textseg) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogGeneralFlag | kOSKextLogIPCFlag,
"Can't find text segment for kernel load address.");
result = kOSReturnError;
goto finish;
}
OSKextLog( 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( NULL,
kOSKextLogDebugLevel |
kOSKextLogIPCFlag,
"Returning kernel image.");
responseData = OSKext::copySanitizedKernelImage();
result = kOSReturnSuccess;
} else if (predicate->isEqualTo(kKextRequestPredicateGetKernelRequests)) {
responseObject = sKernelRequests;
sKernelRequests = OSArray::withCapacity(0);
sPostedKextLoadIdentifiers->flushCollection();
OSKextLog( NULL,
kOSKextLogDebugLevel |
kOSKextLogIPCFlag,
"Returning kernel requests.");
result = kOSReturnSuccess;
} else if (predicate->isEqualTo(kKextRequestPredicateGetAllLoadRequests)) {
responseObject = sAllKextLoadIdentifiers;
responseObject->retain();
OSKextLog( NULL,
kOSKextLogDebugLevel |
kOSKextLogIPCFlag,
"Returning load requests.");
result = kOSReturnSuccess;
}
if (requestDict->getRetainCount() > 1) {
OSKextLog( NULL,
kOSKextLogWarningLevel |
kOSKextLogIPCFlag,
"Request from user space still retained by a kext; "
"probable memory leak.");
}
if (responseData && responseObject) {
OSKextLog( 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) {
serializer = OSSerialize::withCapacity(0);
if (!serializer) {
result = kOSKextReturnNoMemory;
goto finish;
}
if (!responseObject->serialize(serializer)) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogIPCFlag,
"Failed to serialize response to request from user space.");
result = kOSKextReturnSerialization;
goto finish;
}
response = (char *)serializer->text();
responseLength = serializer->getLength();
}
if (responseOut && response) {
char * buffer;
kmem_result = kmem_alloc(kernel_map, (vm_offset_t *)&buffer,
responseLength);
if (kmem_result != KERN_SUCCESS) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogIPCFlag,
"Failed to copy response to request from user space.");
result = kmem_result;
goto finish;
} else {
memcpy(buffer, response, responseLength);
*responseOut = buffer;
*responseLengthOut = responseLength;
}
}
finish:
logInfoArray = OSKext::clearUserSpaceLogFilter();
if (logInfoArray && logInfoOut && logInfoLengthOut) {
(void)OSKext::serializeLogInfo(logInfoArray,
logInfoOut, logInfoLengthOut);
}
IORecursiveLockUnlock(sKextLock);
OSSafeRelease(parsedXML);
OSSafeRelease(errorString);
OSSafeRelease(responseData);
OSSafeRelease(responseObject);
OSSafeRelease(serializer);
OSSafeRelease(logInfoArray);
return result;
}
OSDictionary *
OSKext::copyLoadedKextInfo(
OSArray * kextIdentifiers,
OSArray * infoKeys)
{
OSDictionary * result = NULL;
OSDictionary * kextInfo = NULL; uint32_t count, i;
uint32_t idCount = 0;
uint32_t idIndex = 0;
IORecursiveLockLock(sKextLock);
if (kextIdentifiers && !kextIdentifiers->getCount()) {
kextIdentifiers = NULL;
} else if (kextIdentifiers) {
idCount = kextIdentifiers->getCount();
}
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; Boolean includeThis = true;
if (kextInfo) {
kextInfo->release();
kextInfo = NULL;
}
thisKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i));
if (!thisKext) {
continue;
}
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;
}
#define _OSKextLoadInfoDictCapacity (12)
OSDictionary *
OSKext::copyInfo(OSArray * infoKeys)
{
OSDictionary * result = NULL;
bool success = false;
OSData * headerData = NULL; OSNumber * cpuTypeNumber = NULL; OSNumber * cpuSubtypeNumber = NULL; OSString * versionString = NULL; uint32_t executablePathCStringSize = 0;
char * executablePathCString = NULL; OSString * executablePathString = NULL; OSData * uuid = NULL; OSNumber * scratchNumber = NULL; OSArray * dependencyLoadTags = NULL; OSCollectionIterator * metaClassIterator = NULL; OSArray * metaClassInfo = NULL; OSDictionary * metaClassDict = NULL; OSMetaClass * thisMetaClass = NULL; OSString * metaClassName = NULL; OSString * superclassName = NULL; uint32_t count, i;
result = OSDictionary::withCapacity(_OSKextLoadInfoDictCapacity);
if (!result) {
goto finish;
}
if (infoKeys && !infoKeys->getCount()) {
infoKeys = NULL;
}
if (!infoKeys ||
_OSArrayContainsCString(infoKeys, kOSBundleMachOHeadersKey) ||
_OSArrayContainsCString(infoKeys, kOSBundleCPUTypeKey) ||
_OSArrayContainsCString(infoKeys, kOSBundleCPUSubtypeKey))
{
if (linkedExecutable && !isInterface()) {
kernel_mach_header_t *kext_mach_hdr = (kernel_mach_header_t *)
linkedExecutable->getBytesNoCopy();
if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleMachOHeadersKey)) {
headerData = OSData::withBytes(kext_mach_hdr,
(u_int) (sizeof(*kext_mach_hdr) + kext_mach_hdr->sizeofcmds));
if (!headerData) {
goto finish;
}
result->setObject(kOSBundleMachOHeadersKey, headerData);
}
if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleCPUTypeKey)) {
cpuTypeNumber = OSNumber::withNumber(
(uint64_t) kext_mach_hdr->cputype,
8 * sizeof(kext_mach_hdr->cputype));
if (!cpuTypeNumber) {
goto finish;
}
result->setObject(kOSBundleCPUTypeKey, cpuTypeNumber);
}
if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleCPUSubtypeKey)) {
cpuSubtypeNumber = OSNumber::withNumber(
(uint64_t) kext_mach_hdr->cpusubtype,
8 * sizeof(kext_mach_hdr->cpusubtype));
if (!cpuSubtypeNumber) {
goto finish;
}
result->setObject(kOSBundleCPUSubtypeKey, cpuSubtypeNumber);
}
}
}
result->setObject(kCFBundleIdentifierKey, bundleID);
if (!infoKeys || _OSArrayContainsCString(infoKeys, kCFBundleVersionKey)) {
versionString = OSDynamicCast(OSString,
getPropertyForHostArch(kCFBundleVersionKey));
if (versionString) {
result->setObject(kCFBundleVersionKey, versionString);
}
}
if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleCompatibleVersionKey)) {
versionString = OSDynamicCast(OSString,
getPropertyForHostArch(kOSBundleCompatibleVersionKey));
if (versionString) {
result->setObject(kOSBundleCompatibleVersionKey, versionString);
}
}
if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundlePathKey)) {
if (path) {
result->setObject(kOSBundlePathKey, path);
}
}
if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleExecutablePathKey)) {
if (path && executableRelPath) {
uint32_t pathLength = path->getLength();
executablePathCStringSize = pathLength + executableRelPath->getLength() + 2;
executablePathCString = (char *)kalloc((executablePathCStringSize) *
sizeof(char)); if (!executablePathCString) {
goto finish;
}
strlcpy(executablePathCString, path->getCStringNoCopy(),
executablePathCStringSize);
executablePathCString[pathLength++] = '/';
executablePathCString[pathLength++] = '\0';
strlcat(executablePathCString, executableRelPath->getCStringNoCopy(),
executablePathCStringSize);
executablePathString = OSString::withCString(executablePathCString);
if (!executablePathCString) {
goto finish;
}
result->setObject(kOSBundleExecutablePathKey, executablePathString);
}
}
if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleUUIDKey)) {
uuid = copyUUID();
if (uuid) {
result->setObject(kOSBundleUUIDKey, uuid);
}
}
if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleUUIDKey)) {
result->setObject(kOSKernelResourceKey,
isKernelComponent() ? kOSBooleanTrue : kOSBooleanFalse);
}
if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleIsInterfaceKey)) {
result->setObject(kOSBundleIsInterfaceKey,
isInterface() ? kOSBooleanTrue : kOSBooleanFalse);
}
if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundlePrelinkedKey)) {
result->setObject(kOSBundlePrelinkedKey,
isPrelinked() ? kOSBooleanTrue : kOSBooleanFalse);
}
if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleStartedKey)) {
result->setObject(kOSBundleStartedKey,
isStarted() ? kOSBooleanTrue : kOSBooleanFalse);
}
if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleLoadTagKey)) {
scratchNumber = OSNumber::withNumber((unsigned long long)loadTag,
8 * sizeof(loadTag));
if (!scratchNumber) {
goto finish;
}
result->setObject(kOSBundleLoadTagKey, scratchNumber);
OSSafeReleaseNULL(scratchNumber);
}
if (!infoKeys ||
_OSArrayContainsCString(infoKeys, kOSBundleLoadAddressKey) ||
_OSArrayContainsCString(infoKeys, kOSBundleLoadSizeKey) ||
_OSArrayContainsCString(infoKeys, kOSBundleWiredSizeKey))
{
if (isInterface() || linkedExecutable) {
uint64_t loadAddress = 0;
uint32_t loadSize = 0;
uint32_t wiredSize = 0;
if (linkedExecutable ) {
loadAddress = (uint64_t)linkedExecutable->getBytesNoCopy();
loadSize = linkedExecutable->getLength();
if (kmod_info) {
wiredSize = loadSize - kmod_info->hdr_size;
} else {
wiredSize = loadSize;
}
}
if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleLoadAddressKey)) {
scratchNumber = OSNumber::withNumber(
(unsigned long long)(loadAddress),
8 * sizeof(loadAddress));
if (!scratchNumber) {
goto finish;
}
result->setObject(kOSBundleLoadAddressKey, scratchNumber);
OSSafeReleaseNULL(scratchNumber);
}
if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleLoadSizeKey)) {
scratchNumber = OSNumber::withNumber(
(unsigned long long)(loadSize),
8 * sizeof(loadSize));
if (!scratchNumber) {
goto finish;
}
result->setObject(kOSBundleLoadSizeKey, scratchNumber);
OSSafeReleaseNULL(scratchNumber);
}
if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleWiredSizeKey)) {
scratchNumber = OSNumber::withNumber(
(unsigned long long)(wiredSize),
8 * sizeof(wiredSize));
if (!scratchNumber) {
goto finish;
}
result->setObject(kOSBundleWiredSizeKey, scratchNumber);
OSSafeReleaseNULL(scratchNumber);
}
}
}
if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleDependenciesKey)) {
if ((count = getNumDependencies())) {
dependencyLoadTags = OSArray::withCapacity(count);
result->setObject(kOSBundleDependenciesKey, dependencyLoadTags);
i = count - 1;
do {
OSKext * dependency = OSDynamicCast(OSKext,
dependencies->getObject(i));
OSSafeReleaseNULL(scratchNumber);
if (!dependency) {
continue;
}
scratchNumber = OSNumber::withNumber(
(unsigned long long)dependency->getLoadTag(),
8 * sizeof(loadTag));
if (!scratchNumber) {
goto finish;
}
dependencyLoadTags->setObject(scratchNumber);
} while (i--);
}
}
OSSafeReleaseNULL(scratchNumber);
if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleClassesKey)) {
if (metaClasses && metaClasses->getCount()) {
metaClassIterator = OSCollectionIterator::withCollection(metaClasses);
metaClassInfo = OSArray::withCapacity(metaClasses->getCount());
if (!metaClassIterator || !metaClassInfo) {
goto finish;
}
result->setObject(kOSBundleClassesKey, metaClassInfo);
while ( (thisMetaClass = OSDynamicCast(OSMetaClass,
metaClassIterator->getNextObject())) ) {
OSSafeReleaseNULL(metaClassDict);
OSSafeReleaseNULL(scratchNumber);
OSSafeReleaseNULL(metaClassName);
OSSafeReleaseNULL(superclassName);
metaClassDict = OSDictionary::withCapacity(3);
if (!metaClassDict) {
goto finish;
}
metaClassName = OSString::withCString(thisMetaClass->getClassName());
if (thisMetaClass->getSuperClass()) {
superclassName = OSString::withCString(
thisMetaClass->getSuperClass()->getClassName());
}
scratchNumber = OSNumber::withNumber(thisMetaClass->getInstanceCount(),
8 * sizeof(unsigned int));
if (!metaClassDict || !metaClassName || !scratchNumber) {
goto finish;
}
metaClassInfo->setObject(metaClassDict);
metaClassDict->setObject(kOSMetaClassNameKey, metaClassName);
if (superclassName) {
metaClassDict->setObject(kOSMetaClassSuperclassNameKey, superclassName);
}
metaClassDict->setObject(kOSMetaClassTrackingCountKey, scratchNumber);
}
}
}
if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleRetainCountKey)) {
OSSafeReleaseNULL(scratchNumber);
{
int kextRetainCount = getRetainCount() - 1;
if (isLoaded()) {
kextRetainCount--;
}
scratchNumber = OSNumber::withNumber(
(int)kextRetainCount,
8 * sizeof(int));
if (scratchNumber) {
result->setObject(kOSBundleRetainCountKey, scratchNumber);
}
}
}
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);
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;
}
OSData *
OSKext::copySanitizedKernelImage(void)
{
OSData * result = NULL;
kernel_mach_header_t * kernelHeader = NULL;
uint32_t sizeofcmds = 0;
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( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Can't provide kernel image for linking; missing component.");
goto finish;
}
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);
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);
scan_cmd = (struct load_command *)&kernelHeader[1]; memcpy(scan_cmd, text_seg, text_seg->cmdsize);
kernelHeader->ncmds++;
text_seg = (kernel_segment_command_t *)scan_cmd; 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;
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);
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; 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;
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);
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; 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);
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; stroff_shift = symtab_cmd->stroff - symtab_cmd->symoff;
symtab_cmd->symoff = linkedit_seg->fileoff;
symtab_cmd->stroff = symtab_cmd->symoff + stroff_shift;
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;
}
OSReturn
OSKext::requestResource(
const char * kextIdentifierCString,
const char * resourceNameCString,
OSKextRequestResourceCallback callback,
void * context,
OSKextRequestTag * requestTagOut)
{
OSReturn result = kOSReturnError;
OSKext * callbackKext = NULL;
OSKextRequestTag requestTag = -1;
OSNumber * requestTagNum = NULL;
OSDictionary * requestDict = NULL; OSString * kextIdentifier = NULL; OSString * resourceName = NULL;
OSDictionary * callbackRecord = NULL; OSData * callbackWrapper = NULL;
OSData * contextWrapper = NULL;
IORecursiveLockLock(sKextLock);
if (requestTagOut) {
*requestTagOut = kOSKextRequestTagInvalid;
}
if (!sKernelRequestsEnabled) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Can't request resource %s for %s - requests to user space are disabled.",
resourceNameCString,
kextIdentifierCString);
result = kOSKextReturnDisabled;
goto finish;
}
if (!kextIdentifierCString || !resourceNameCString || !callback) {
result = kOSKextReturnInvalidArgument;
goto finish;
}
callbackKext = OSKext::lookupKextWithAddress((vm_address_t)callback);
if (!callbackKext) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Resource request has bad callback address.");
result = kOSKextReturnInvalidArgument;
goto finish;
}
if (!callbackKext->flags.starting && !callbackKext->flags.started) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Resource request callback is in a kext that is not started.");
result = kOSKextReturnInvalidArgument;
goto finish;
}
if (callbackKext->flags.stopping) {
result = kOSKextReturnStopping;
goto finish;
}
if (sNextRequestTag == kOSKextRequestTagInvalid) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"No more request tags available; restart required.");
result = kOSKextReturnNoResources;
goto finish;
}
requestTag = sNextRequestTag++;
result = _OSKextCreateRequest(kKextRequestPredicateRequestResource,
&requestDict);
if (result != kOSReturnSuccess) {
goto finish;
}
kextIdentifier = OSString::withCString(kextIdentifierCString);
resourceName = OSString::withCString(resourceNameCString);
requestTagNum = OSNumber::withNumber((long long unsigned int)requestTag,
8 * sizeof(requestTag));
if (!kextIdentifier ||
!resourceName ||
!requestTagNum ||
!_OSKextSetRequestArgument(requestDict,
kKextRequestArgumentBundleIdentifierKey, kextIdentifier) ||
!_OSKextSetRequestArgument(requestDict,
kKextRequestArgumentNameKey, resourceName) ||
!_OSKextSetRequestArgument(requestDict,
kKextRequestArgumentRequestTagKey, requestTagNum)) {
result = kOSKextReturnNoMemory;
goto finish;
}
callbackRecord = OSDynamicCast(OSDictionary, requestDict->copyCollection());
if (!callbackRecord) {
result = kOSKextReturnNoMemory;
goto finish;
}
callbackWrapper = OSData::withBytes((void *)&callback, sizeof(void *));
if (context) {
contextWrapper = OSData::withBytes((void *)&context, sizeof(void *));
}
if (!callbackWrapper || !_OSKextSetRequestArgument(callbackRecord,
kKextRequestArgumentCallbackKey, callbackWrapper)) {
result = kOSKextReturnNoMemory;
goto finish;
}
if (context) {
if (!contextWrapper || !_OSKextSetRequestArgument(callbackRecord,
kKextRequestArgumentContextKey, contextWrapper)) {
result = kOSKextReturnNoMemory;
goto finish;
}
}
if (!sKernelRequests->setObject(requestDict) ||
!sRequestCallbackRecords->setObject(callbackRecord)) {
result = kOSKextReturnNoMemory;
goto finish;
}
OSKext::pingKextd();
result = kOSReturnSuccess;
if (requestTagOut) {
*requestTagOut = requestTag;
}
finish:
if (result != kOSReturnSuccess) {
unsigned int index;
index = sKernelRequests->getNextIndexOfObject(requestDict, 0);
if (index != (unsigned int)-1) {
sKernelRequests->removeObject(index);
}
index = sRequestCallbackRecords->getNextIndexOfObject(callbackRecord, 0);
if (index != (unsigned int)-1) {
sRequestCallbackRecords->removeObject(index);
}
}
OSKext::considerUnloads( true);
IORecursiveLockUnlock(sKextLock);
if (callbackKext) callbackKext->release();
if (requestTagNum) requestTagNum->release();
if (requestDict) requestDict->release();
if (kextIdentifier) kextIdentifier->release();
if (resourceName) resourceName->release();
if (callbackRecord) callbackRecord->release();
if (callbackWrapper) callbackWrapper->release();
if (contextWrapper) contextWrapper->release();
return result;
}
OSReturn
OSKext::dequeueCallbackForRequestTag(
OSKextRequestTag requestTag,
OSDictionary ** callbackRecordOut)
{
OSReturn result = kOSReturnError;
OSNumber * requestTagNum = NULL;
requestTagNum = OSNumber::withNumber((long long unsigned int)requestTag,
8 * sizeof(requestTag));
if (!requestTagNum) {
goto finish;
}
result = OSKext::dequeueCallbackForRequestTag(requestTagNum,
callbackRecordOut);
finish:
OSSafeRelease(requestTagNum);
return result;
}
OSReturn
OSKext::dequeueCallbackForRequestTag(
OSNumber * requestTagNum,
OSDictionary ** callbackRecordOut)
{
OSReturn result = kOSKextReturnInvalidArgument;
OSDictionary * callbackRecord = NULL; OSNumber * callbackTagNum = NULL; unsigned int count, i;
result = kOSReturnError;
count = sRequestCallbackRecords->getCount();
for (i = 0; i < count; i++) {
callbackRecord = OSDynamicCast(OSDictionary,
sRequestCallbackRecords->getObject(i));
if (!callbackRecord) {
goto finish;
}
callbackTagNum = OSDynamicCast(OSNumber, _OSKextGetRequestArgument(
callbackRecord, kKextRequestArgumentRequestTagKey));
if (!callbackTagNum) {
goto finish;
}
if (callbackTagNum->isEqualTo(requestTagNum)) {
if (callbackRecordOut) {
*callbackRecordOut = callbackRecord;
callbackRecord->retain();
}
sRequestCallbackRecords->removeObject(i);
result = kOSReturnSuccess;
goto finish;
}
}
result = kOSKextReturnNotFound;
finish:
return result;
}
OSReturn
OSKext::dispatchResource(OSDictionary * requestDict)
{
OSReturn result = kOSReturnError;
OSDictionary * callbackRecord = NULL; OSNumber * requestTag = NULL; OSNumber * requestResult = NULL; OSData * dataObj = NULL; uint32_t dataLength = 0;
const void * dataPtr = NULL; OSData * callbackWrapper = NULL; OSKextRequestResourceCallback callback = NULL;
OSData * contextWrapper = NULL; void * context = NULL; OSKext * callbackKext = NULL;
requestTag = OSDynamicCast(OSNumber, _OSKextGetRequestArgument(requestDict,
kKextRequestArgumentRequestTagKey));
requestResult = OSDynamicCast(OSNumber, _OSKextGetRequestArgument(requestDict,
kKextRequestArgumentResultKey));
if (!requestTag || !requestResult) {
result = kOSKextReturnInvalidArgument;
goto finish;
}
result = dequeueCallbackForRequestTag(requestTag, &callbackRecord);
if (result != kOSReturnSuccess) {
goto finish;
}
contextWrapper = OSDynamicCast(OSData, _OSKextGetRequestArgument(callbackRecord,
kKextRequestArgumentContextKey));
context = _OSKextExtractPointer(contextWrapper);
if (contextWrapper && !context) {
goto finish;
}
callbackWrapper = OSDynamicCast(OSData,
_OSKextGetRequestArgument(callbackRecord,
kKextRequestArgumentCallbackKey));
callback = (OSKextRequestResourceCallback)
_OSKextExtractPointer(callbackWrapper);
if (!callback) {
goto finish;
}
dataObj = OSDynamicCast(OSData, _OSKextGetRequestArgument(requestDict,
kKextRequestArgumentValueKey));
if (dataObj) {
dataPtr = dataObj->getBytesNoCopy();
dataLength = dataObj->getLength();
}
callbackKext = OSKext::lookupKextWithAddress((vm_address_t)callback);
if (!callbackKext) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Can't invoke callback for resource request; "
"no kext loaded at callback address %p.",
callback);
goto finish;
}
if (!callbackKext->flags.starting && !callbackKext->flags.started) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Can't invoke kext resource callback; "
"kext at callback address %p is not running.",
callback);
goto finish;
}
(void)callback(requestTag->unsigned32BitValue(),
(OSReturn)requestResult->unsigned32BitValue(),
dataPtr, dataLength, context);
result = kOSReturnSuccess;
finish:
if (callbackKext) callbackKext->release();
if (callbackRecord) callbackRecord->release();
return result;
}
void
OSKext::invokeRequestCallback(
OSDictionary * callbackRecord,
OSReturn callbackResult)
{
OSString * predicate = _OSKextGetRequestPredicate(callbackRecord);
OSNumber * resultNum = NULL;
if (!predicate) {
goto finish;
}
resultNum = OSNumber::withNumber((long long unsigned int)callbackResult,
8 * sizeof(callbackResult));
if (!resultNum) {
goto finish;
}
_OSKextSetRequestArgument(callbackRecord, kKextRequestArgumentResultKey,
resultNum);
if (predicate->isEqualTo(kKextRequestPredicateRequestResource)) {
OSKext::dispatchResource(callbackRecord);
}
finish:
if (resultNum) resultNum->release();
return;
}
OSReturn
OSKext::cancelRequest(
OSKextRequestTag requestTag,
void ** contextOut)
{
OSReturn result = kOSKextReturnNoMemory;
OSDictionary * callbackRecord = NULL; OSData * contextWrapper = NULL;
IORecursiveLockLock(sKextLock);
result = OSKext::dequeueCallbackForRequestTag(requestTag,
&callbackRecord);
IORecursiveLockUnlock(sKextLock);
if (result == kOSReturnSuccess && contextOut) {
contextWrapper = OSDynamicCast(OSData,
_OSKextGetRequestArgument(callbackRecord,
kKextRequestArgumentContextKey));
*contextOut = _OSKextExtractPointer(contextWrapper);
}
if (callbackRecord) callbackRecord->release();
return result;
}
void
OSKext::invokeOrCancelRequestCallbacks(
OSReturn callbackResult,
bool invokeFlag)
{
unsigned int count, i;
count = sRequestCallbackRecords->getCount();
if (!count) {
goto finish;
}
i = count - 1;
do {
OSDictionary * request = OSDynamicCast(OSDictionary,
sRequestCallbackRecords->getObject(i));
if (!request) {
continue;
}
OSData * callbackWrapper = OSDynamicCast(OSData,
_OSKextGetRequestArgument(request,
kKextRequestArgumentCallbackKey));
if (!callbackWrapper) {
sRequestCallbackRecords->removeObject(i);
continue;
}
vm_address_t callbackAddress = (vm_address_t)
_OSKextExtractPointer(callbackWrapper);
if ((kmod_info->address <= callbackAddress) &&
(callbackAddress < (kmod_info->address + kmod_info->size))) {
if (invokeFlag) {
invokeRequestCallback(request, callbackResult);
} else {
sRequestCallbackRecords->removeObject(i);
}
}
} while (i--);
finish:
return;
}
uint32_t
OSKext::countRequestCallbacks(void)
{
uint32_t result = 0;
unsigned int count, i;
count = sRequestCallbackRecords->getCount();
if (!count) {
goto finish;
}
i = count - 1;
do {
OSDictionary * request = OSDynamicCast(OSDictionary,
sRequestCallbackRecords->getObject(i));
if (!request) {
continue;
}
OSData * callbackWrapper = OSDynamicCast(OSData,
_OSKextGetRequestArgument(request,
kKextRequestArgumentCallbackKey));
if (!callbackWrapper) {
continue;
}
vm_address_t callbackAddress = (vm_address_t)
_OSKextExtractPointer(callbackWrapper);
if ((kmod_info->address <= callbackAddress) &&
(callbackAddress < (kmod_info->address + kmod_info->size))) {
result++;
}
} while (i--);
finish:
return result;
}
static OSReturn _OSKextCreateRequest(
const char * predicate,
OSDictionary ** requestP)
{
OSReturn result = kOSKextReturnNoMemory;
OSDictionary * request = NULL; OSDictionary * args = NULL;
request = OSDictionary::withCapacity(2);
if (!request) {
goto finish;
}
result = _OSDictionarySetCStringValue(request,
kKextRequestPredicateKey, predicate);
if (result != kOSReturnSuccess) {
goto finish;
}
result = kOSReturnSuccess;
finish:
if (result != kOSReturnSuccess) {
if (request) request->release();
} else {
*requestP = request;
}
if (args) args->release();
return result;
}
static OSString * _OSKextGetRequestPredicate(OSDictionary * requestDict)
{
return OSDynamicCast(OSString,
requestDict->getObject(kKextRequestPredicateKey));
}
static OSObject * _OSKextGetRequestArgument(
OSDictionary * requestDict,
const char * argName)
{
OSDictionary * args = OSDynamicCast(OSDictionary,
requestDict->getObject(kKextRequestArgumentsKey));
if (args) {
return args->getObject(argName);
}
return NULL;
}
static bool _OSKextSetRequestArgument(
OSDictionary * requestDict,
const char * argName,
OSObject * value)
{
OSDictionary * args = OSDynamicCast(OSDictionary,
requestDict->getObject(kKextRequestArgumentsKey));
if (!args) {
args = OSDictionary::withCapacity(2);
if (!args) {
goto finish;
}
requestDict->setObject(kKextRequestArgumentsKey, args);
args->release();
}
if (args) {
return args->setObject(argName, value);
}
finish:
return false;
}
static void * _OSKextExtractPointer(OSData * wrapper)
{
void * result = NULL;
const void * resultPtr = NULL;
if (!wrapper) {
goto finish;
}
resultPtr = wrapper->getBytesNoCopy();
result = *(void **)resultPtr;
finish:
return result;
}
static OSReturn _OSDictionarySetCStringValue(
OSDictionary * dict,
const char * cKey,
const char * cValue)
{
OSReturn result = kOSKextReturnNoMemory;
const OSSymbol * key = NULL; OSString * value = NULL;
key = OSSymbol::withCString(cKey);
value = OSString::withCString(cValue);
if (!key || !value) {
goto finish;
}
if (dict->setObject(key, value)) {
result = kOSReturnSuccess;
}
finish:
if (key) key->release();
if (value) value->release();
return result;
}
static bool _OSArrayContainsCString(
OSArray * array,
const char * cString)
{
bool result = false;
const OSSymbol * symbol = NULL;
uint32_t count, i;
if (!array || !cString) {
goto finish;
}
symbol = OSSymbol::withCStringNoCopy(cString);
if (!symbol) {
goto finish;
}
count = array->getCount();
for (i = 0; i < count; i++) {
OSObject * thisObject = array->getObject(i);
if (symbol->isEqualTo(thisObject)) {
result = true;
goto finish;
}
}
finish:
if (symbol) symbol->release();
return result;
}
#if PRAGMA_MARK
#pragma mark Personalities (IOKit Drivers)
#endif
OSArray *
OSKext::copyAllKextPersonalities(bool filterSafeBootFlag)
{
OSArray * result = NULL; OSCollectionIterator * kextIterator = NULL; OSArray * personalities = NULL; OSCollectionIterator * personalitiesIterator = NULL;
OSString * kextID = NULL; OSKext * theKext = NULL;
IORecursiveLockLock(sKextLock);
result = OSArray::withCapacity(sKextsByID->getCount() * 3);
if (!result) {
goto finish;
}
kextIterator = OSCollectionIterator::withCollection(sKextsByID);
if (!kextIterator) {
goto finish;
}
while ((kextID = OSDynamicCast(OSString, kextIterator->getNextObject()))) {
if (personalitiesIterator) {
personalitiesIterator->release();
personalitiesIterator = NULL;
}
if (personalities) {
personalities->release();
personalities = NULL;
}
theKext = OSDynamicCast(OSKext, sKextsByID->getObject(kextID));
if (!sSafeBoot || !filterSafeBootFlag || theKext->isLoadableInSafeBoot()) {
personalities = theKext->copyPersonalitiesArray();
if (!personalities) {
continue;
}
result->merge(personalities);
} else {
OSKextLog(theKext,
kOSKextLogWarningLevel |
kOSKextLogLoadFlag,
"Kext %s is not loadable during safe boot; "
"omitting its personalities.",
theKext->getIdentifierCString());
}
}
finish:
IORecursiveLockUnlock(sKextLock);
if (kextIterator) kextIterator->release();
if (personalitiesIterator) personalitiesIterator->release();
if (personalities) personalities->release();
return result;
}
void
OSKext::sendAllKextPersonalitiesToCatalog(bool startMatching)
{
int numPersonalities = 0;
OSKextLog( NULL,
kOSKextLogStepLevel |
kOSKextLogLoadFlag,
"Sending all eligible registered kexts' personalities "
"to the IOCatalogue %s.",
startMatching ? "and starting matching" : "but not starting matching");
OSArray * personalities = OSKext::copyAllKextPersonalities(
true);
if (personalities) {
gIOCatalogue->addDrivers(personalities, startMatching);
numPersonalities = personalities->getCount();
personalities->release();
}
OSKextLog( NULL,
kOSKextLogStepLevel |
kOSKextLogLoadFlag,
"%d kext personalit%s sent to the IOCatalogue; %s.",
numPersonalities, numPersonalities > 0 ? "ies" : "y",
startMatching ? "matching started" : "matching not started");
return;
}
OSArray *
OSKext::copyPersonalitiesArray(void)
{
OSArray * result = NULL;
OSDictionary * personalities = NULL; OSCollectionIterator * personalitiesIterator = NULL;
OSString * personalityName = NULL; OSString * personalityBundleIdentifier = NULL;
personalities = OSDynamicCast(OSDictionary,
getPropertyForHostArch(kIOKitPersonalitiesKey));
if (!personalities) {
goto finish;
}
result = OSArray::withCapacity(personalities->getCount());
if (!result) {
goto finish;
}
personalitiesIterator =
OSCollectionIterator::withCollection(personalities);
if (!personalitiesIterator) {
goto finish;
}
while ((personalityName = OSDynamicCast(OSString,
personalitiesIterator->getNextObject()))) {
OSDictionary * personality = OSDynamicCast(OSDictionary,
personalities->getObject(personalityName));
personalityBundleIdentifier = OSDynamicCast(OSString,
personality->getObject(kCFBundleIdentifierKey));
if (!personalityBundleIdentifier) {
personality->setObject(kCFBundleIdentifierKey, bundleID);
} else if (!personalityBundleIdentifier->isEqualTo(bundleID)) {
personality->setObject(kIOPersonalityPublisherKey, bundleID);
}
result->setObject(personality);
}
finish:
if (personalitiesIterator) personalitiesIterator->release();
return result;
}
OSReturn
OSKext::sendPersonalitiesToCatalog(
bool startMatching,
OSArray * personalityNames)
{
OSReturn result = kOSReturnSuccess;
OSArray * personalitiesToSend = NULL; OSDictionary * kextPersonalities = NULL; int count, i;
if (!sLoadEnabled) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Kext loading is disabled (attempt to start matching for kext %s).",
getIdentifierCString());
result = kOSKextReturnDisabled;
goto finish;
}
if (sSafeBoot && !isLoadableInSafeBoot()) {
OSKextLog(this,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag,
"Kext %s is not loadable during safe boot; "
"not sending personalities to the IOCatalogue.",
getIdentifierCString());
result = kOSKextReturnNotLoadable;
goto finish;
}
if (!personalityNames || !personalityNames->getCount()) {
personalitiesToSend = copyPersonalitiesArray();
} else {
kextPersonalities = OSDynamicCast(OSDictionary,
getPropertyForHostArch(kIOKitPersonalitiesKey));
if (!kextPersonalities || !kextPersonalities->getCount()) {
goto finish;
}
personalitiesToSend = OSArray::withCapacity(0);
if (!personalitiesToSend) {
result = kOSKextReturnNoMemory;
goto finish;
}
count = personalityNames->getCount();
for (i = 0; i < count; i++) {
OSString * name = OSDynamicCast(OSString,
personalityNames->getObject(i));
if (!name) {
continue;
}
OSDictionary * personality = OSDynamicCast(OSDictionary,
kextPersonalities->getObject(name));
if (personality) {
personalitiesToSend->setObject(personality);
}
}
}
if (personalitiesToSend) {
unsigned numPersonalities = personalitiesToSend->getCount();
OSKextLog(this,
kOSKextLogStepLevel |
kOSKextLogLoadFlag,
"Kext %s sending %d personalit%s to the IOCatalogue%s.",
getIdentifierCString(),
numPersonalities,
numPersonalities > 1 ? "ies" : "y",
startMatching ? " and starting matching" : " but not starting matching");
gIOCatalogue->addDrivers(personalitiesToSend, startMatching);
}
finish:
if (personalitiesToSend) {
personalitiesToSend->release();
}
return result;
}
void
OSKext::removePersonalitiesFromCatalog(void)
{
OSDictionary * personality = NULL;
personality = OSDictionary::withCapacity(1);
if (!personality) {
goto finish;
}
personality->setObject(kCFBundleIdentifierKey, getIdentifier());
OSKextLog(this,
kOSKextLogStepLevel |
kOSKextLogLoadFlag,
"Kext %s removing all personalities naming it from the IOCatalogue.",
getIdentifierCString());
gIOCatalogue->removeDrivers(personality, true);
finish:
if (personality) personality->release();
return;
}
#if PRAGMA_MARK
#pragma mark Logging
#endif
OSKextLogSpec
OSKext::setUserSpaceLogFilter(
OSKextLogSpec newUserLogFilter,
bool captureFlag)
{
OSKextLogSpec result;
bool allocError = false;
IOLockLock(sKextLoggingLock);
result = sUserSpaceKextLogFilter;
sUserSpaceKextLogFilter = newUserLogFilter;
if (newUserLogFilter && captureFlag &&
!sUserSpaceLogSpecArray && !sUserSpaceLogMessageArray) {
sUserSpaceLogSpecArray = OSArray::withCapacity(0);
sUserSpaceLogMessageArray = OSArray::withCapacity(0);
if (!sUserSpaceLogSpecArray || !sUserSpaceLogMessageArray) {
OSSafeReleaseNULL(sUserSpaceLogSpecArray);
OSSafeReleaseNULL(sUserSpaceLogMessageArray);
allocError = true;
}
}
IOLockUnlock(sKextLoggingLock);
if (result != newUserLogFilter) {
OSKextLog( NULL,
kOSKextLogDebugLevel |
kOSKextLogGeneralFlag,
"User-space log flags changed from 0x%x to 0x%x.",
result, newUserLogFilter);
}
if (allocError) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogGeneralFlag,
"Failed to allocate user-space log message arrays.");
}
return result;
}
OSArray *
OSKext::clearUserSpaceLogFilter(void)
{
OSArray * result = NULL;
OSKextLogSpec oldLogFilter;
OSKextLogSpec newLogFilter = kOSKextLogSilentFilter;
IOLockLock(sKextLoggingLock);
result = OSArray::withCapacity(2);
if (result) {
result->setObject(sUserSpaceLogSpecArray);
result->setObject(sUserSpaceLogMessageArray);
}
OSSafeReleaseNULL(sUserSpaceLogSpecArray);
OSSafeReleaseNULL(sUserSpaceLogMessageArray);
oldLogFilter = sUserSpaceKextLogFilter;
sUserSpaceKextLogFilter = newLogFilter;
IOLockUnlock(sKextLoggingLock);
if (oldLogFilter != newLogFilter) {
OSKextLog( NULL,
kOSKextLogDebugLevel |
kOSKextLogGeneralFlag,
"User-space log flags changed from 0x%x to 0x%x.",
oldLogFilter, newLogFilter);
}
return result;
}
OSKextLogSpec
OSKext::getUserSpaceLogFilter(void)
{
OSKextLogSpec result;
IOLockLock(sKextLoggingLock);
result = sUserSpaceKextLogFilter;
IOLockUnlock(sKextLoggingLock);
return result;
}
#define VTRESET "\033[0m"
#define VTBOLD "\033[1m"
#define VTUNDER "\033[4m"
#define VTRED "\033[31m"
#define VTGREEN "\033[32m"
#define VTYELLOW "\033[33m"
#define VTBLUE "\033[34m"
#define VTMAGENTA "\033[35m"
#define VTCYAN "\033[36m"
inline const char * colorForFlags(OSKextLogSpec flags)
{
OSKextLogSpec logLevel = flags & kOSKextLogLevelMask;
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 ""; break;
}
return "";
}
inline bool logSpecMatch(
OSKextLogSpec msgLogSpec,
OSKextLogSpec logFilter)
{
OSKextLogSpec filterKextGlobal = logFilter & kOSKextLogKextOrGlobalMask;
OSKextLogSpec filterLevel = logFilter & kOSKextLogLevelMask;
OSKextLogSpec filterFlags = logFilter & kOSKextLogFlagsMask;
OSKextLogSpec msgKextGlobal = msgLogSpec & kOSKextLogKextOrGlobalMask;
OSKextLogSpec msgLevel = msgLogSpec & kOSKextLogLevelMask;
OSKextLogSpec msgFlags = msgLogSpec & kOSKextLogFlagsMask;
if (msgLevel == kOSKextLogExplicitLevel) {
return true;
}
if (msgLevel <= kOSKextLogBasicLevel && (msgLevel <= filterLevel)) {
return true;
}
if (!msgKextGlobal && !filterKextGlobal) {
return false;
}
if ((msgFlags & filterFlags) && (msgLevel <= filterLevel)) {
return true;
}
return false;
}
extern "C" {
void
OSKextLog(
OSKext * aKext,
OSKextLogSpec msgLogSpec,
const char * format, ...)
{
va_list argList;
va_start(argList, format);
OSKextVLog(aKext, msgLogSpec, format, argList);
va_end(argList);
}
void
OSKextVLog(
OSKext * aKext,
OSKextLogSpec msgLogSpec,
const char * format,
va_list srcArgList)
{
extern int disableConsoleOutput;
bool logForKernel = false;
bool logForUser = false;
va_list argList;
char stackBuffer[120];
uint32_t length = 0;
char * allocBuffer = NULL; OSNumber * logSpecNum = NULL; OSString * logString = NULL; char * buffer = stackBuffer;
IOLockLock(sKextLoggingLock);
if (!aKext || aKext->flags.loggingEnabled) {
msgLogSpec = msgLogSpec | kOSKextLogKextOrGlobalMask;
}
logForKernel = logSpecMatch(msgLogSpec, sKernelLogFilter);
if (sUserSpaceLogSpecArray && sUserSpaceLogMessageArray) {
logForUser = logSpecMatch(msgLogSpec, sUserSpaceKextLogFilter);
}
if (! (logForKernel || logForUser) ) {
goto finish;
}
va_copy(argList, srcArgList);
length = vsnprintf(stackBuffer, sizeof(stackBuffer), format, argList);
va_end(argList);
if (length + 1 >= sizeof(stackBuffer)) {
allocBuffer = (char *)kalloc((length + 1) * sizeof(char));
if (!allocBuffer) {
goto finish;
}
va_copy(argList, srcArgList);
vsnprintf(allocBuffer, length + 1, format, argList);
va_end(argList);
buffer = allocBuffer;
}
if (logForUser && sUserSpaceLogSpecArray && sUserSpaceLogMessageArray) {
logSpecNum = OSNumber::withNumber(msgLogSpec, 8 * sizeof(msgLogSpec));
logString = OSString::withCString(buffer);
if (logSpecNum && logString) {
sUserSpaceLogSpecArray->setObject(logSpecNum);
sUserSpaceLogMessageArray->setObject(logString);
}
}
if (logForKernel) {
if (!disableConsoleOutput && sBootArgLogFilterFound) {
const char * color = ""; color = colorForFlags(msgLogSpec);
printf("%s%s%s\n", colorForFlags(msgLogSpec),
buffer, color[0] ? VTRESET : "");
} else {
printf("%s\n", buffer);
}
}
finish:
IOLockUnlock(sKextLoggingLock);
if (allocBuffer) {
kfree(allocBuffer, (length + 1) * sizeof(char));
}
OSSafeRelease(logString);
OSSafeRelease(logSpecNum);
return;
}
};
#if PRAGMA_MARK
#pragma mark Backtrace Dump & kmod_get_info() support
#endif
void
OSKext::printKextsInBacktrace(
vm_offset_t * addr,
unsigned int cnt,
int (* printf_func)(const char *fmt, ...),
bool lockFlag)
{
addr64_t summary_page = 0;
addr64_t last_summary_page = 0;
bool found_kmod = false;
u_int i = 0;
if (lockFlag) {
IOLockLock(sKextSummariesLock);
}
if (!gLoadedKextSummaries) {
(*printf_func)(" can't perform kext scan: no kext summary");
goto finish;
}
summary_page = trunc_page((addr64_t)(uintptr_t)gLoadedKextSummaries);
last_summary_page = round_page(summary_page + sLoadedKextSummariesAllocSize);
for (; summary_page < last_summary_page; summary_page += PAGE_SIZE) {
if (pmap_find_phys(kernel_pmap, summary_page) == 0) {
(*printf_func)(" can't perform kext scan: "
"missing kext summary page %p", summary_page);
goto finish;
}
}
for (i = 0; i < gLoadedKextSummaries->numSummaries; ++i) {
OSKextLoadedKextSummary * summary;
summary = gLoadedKextSummaries->summaries + i;
if (!summary->address) {
continue;
}
if (!summaryIsInBacktrace(summary, addr, cnt)) {
continue;
}
if (!found_kmod) {
(*printf_func)(" Kernel Extensions in backtrace:\n");
found_kmod = true;
}
printSummary(summary, printf_func);
}
finish:
if (lockFlag) {
IOLockUnlock(sKextSummariesLock);
}
return;
}
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;
}
static void findSummaryUUID(
uint32_t tag_ID,
uuid_string_t uuid);
static void findSummaryUUID(
uint32_t tag_ID,
uuid_string_t uuid)
{
u_int i;
uuid[0] = 0x00;
for (i = 0; i < gLoadedKextSummaries->numSummaries; ++i) {
OSKextLoadedKextSummary * summary;
summary = gLoadedKextSummaries->summaries + i;
if (summary->loadTag == tag_ID) {
(void) uuid_unparse(summary->uuid, uuid);
break;
}
}
return;
}
void OSKext::printSummary(
OSKextLoadedKextSummary * summary,
int (* printf_func)(const char *fmt, ...))
{
kmod_reference_t * kmod_ref = NULL;
uuid_string_t uuid;
char version[kOSKextVersionMaxLength];
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",
summary->name, version, uuid,
summary->address, summary->address + summary->size - 1);
for (kmod_ref = (kmod_reference_t *) summary->reference_list;
kmod_ref;
kmod_ref = kmod_ref->next) {
kmod_info_t * rinfo;
if (pmap_find_phys(kernel_pmap, (addr64_t)((uintptr_t)kmod_ref)) == 0) {
(*printf_func)(" kmod dependency scan stopped "
"due to missing dependency page: %p\n", 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);
break;
}
if (!rinfo->address) {
continue; }
findSummaryUUID(rinfo->id, uuid);
(*printf_func)(" dependency: %s(%s)[%s]@%p\n",
rinfo->name, rinfo->version, uuid, rinfo->address);
}
return;
}
static int substitute(
const char * scan_string,
char * string_out,
uint32_t * to_index,
uint32_t * from_index,
const char * substring,
char marker,
char substitution);
static int
substitute(
const char * scan_string,
char * string_out,
uint32_t * to_index,
uint32_t * from_index,
const char * substring,
char marker,
char substitution)
{
uint32_t substring_length = strnlen(substring, KMOD_MAX_NAME - 1);
if (!strncmp(scan_string, substring, substring_length)) {
if (marker) {
string_out[(*to_index)++] = marker;
}
string_out[(*to_index)++] = substitution;
(*from_index) += substring_length;
return 1;
}
return 0;
}
static void compactIdentifier(
const char * identifier,
char * identifier_out,
char ** identifier_out_end);
static void
compactIdentifier(
const char * identifier,
char * identifier_out,
char ** identifier_out_end)
{
uint32_t from_index, to_index;
uint32_t scan_from_index = 0;
uint32_t scan_to_index = 0;
subs_entry_t * subs_entry = NULL;
int did_sub = 0;
from_index = to_index = 0;
identifier_out[0] = '\0';
for (subs_entry = &kext_identifier_prefix_subs[0];
subs_entry->substring && !did_sub;
subs_entry++) {
did_sub = substitute(identifier, identifier_out,
&scan_to_index, &scan_from_index,
subs_entry->substring, '\0', subs_entry->substitute);
}
did_sub = 0;
for (;
scan_from_index < KMOD_MAX_NAME - 1 && identifier[scan_from_index];
) {
const char * scan_string = &identifier[scan_from_index];
did_sub = 0;
if (scan_from_index) {
for (subs_entry = &kext_identifier_substring_subs[0];
subs_entry->substring && !did_sub;
subs_entry++) {
did_sub = substitute(scan_string, identifier_out,
&scan_to_index, &scan_from_index,
subs_entry->substring, '!', subs_entry->substitute);
}
}
if (!did_sub) {
identifier_out[scan_to_index++] = identifier[scan_from_index++];
}
}
identifier_out[scan_to_index] = '\0';
if (identifier_out_end) {
*identifier_out_end = &identifier_out[scan_to_index];
}
return;
}
static int assemble_identifier_and_version(
kmod_info_t * kmod_info,
char * identPlusVers);
static int
assemble_identifier_and_version(
kmod_info_t * kmod_info,
char * identPlusVers)
{
int result = 0;
compactIdentifier(kmod_info->name, identPlusVers, NULL);
result = strnlen(identPlusVers, KMOD_MAX_NAME - 1);
identPlusVers[result++] = '\t'; identPlusVers[result] = '\0'; result = strlcat(identPlusVers, kmod_info->version, KMOD_MAX_NAME);
return result;
}
uint32_t
OSKext::saveLoadedKextPanicListTyped(
const char * prefix,
int invertFlag,
int libsFlag,
char * paniclist,
uint32_t list_size,
uint32_t * list_length_ptr)
{
uint32_t result = 0;
int error = 0;
unsigned int count, i;
count = sLoadedKexts->getCount();
if (!count) {
goto finish;
}
i = count - 1;
do {
OSObject * rawKext = sLoadedKexts->getObject(i);
OSKext * theKext = OSDynamicCast(OSKext, rawKext);
int match;
char identPlusVers[2*KMOD_MAX_NAME];
uint32_t identPlusVersLength;
if (!rawKext) {
printf("OSKext::saveLoadedKextPanicListTyped - "
"NULL kext in loaded kext list; continuing\n");
continue;
}
if (!theKext) {
printf("OSKext::saveLoadedKextPanicListTyped - "
"Kext type cast failed in loaded kext list; continuing\n");
continue;
}
if (theKext->isKernelComponent()) {
continue;
}
kmod_info_t * kmod_info = theKext->kmod_info;
match = !strncmp(kmod_info->name, prefix, strnlen(prefix, KMOD_MAX_NAME));
if ((match && invertFlag) || (!match && !invertFlag)) {
continue;
}
if ((libsFlag == 0 && theKext->getCompatibleVersion() > 1) ||
(libsFlag == 1 && theKext->getCompatibleVersion() < 1)) {
continue;
}
if (!kmod_info ||
!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;
goto finish;
}
identPlusVersLength = assemble_identifier_and_version(kmod_info,
identPlusVers);
if (!identPlusVersLength) {
printf("error saving loaded kext info\n");
goto finish;
}
if (*list_length_ptr + identPlusVersLength + 1 >= list_size) {
goto finish;
}
*list_length_ptr = strlcat(paniclist, identPlusVers, list_size);
*list_length_ptr = strlcat(paniclist, "\n", list_size);
} while (i--);
finish:
if (!error) {
if (*list_length_ptr + 1 <= list_size) {
result = list_size - (*list_length_ptr + 1);
}
}
return result;
}
void
OSKext::saveLoadedKextPanicList(void)
{
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);
if (!newlist) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Couldn't allocate kext panic log buffer.");
goto finish;
}
newlist[0] = '\0';
if (!OSKext::saveLoadedKextPanicListTyped("com.apple.", 1,
-1, newlist, newlist_size, &newlist_length)) {
goto finish;
}
if (!OSKext::saveLoadedKextPanicListTyped("com.apple.", 0,
0, newlist, newlist_size, &newlist_length)) {
goto finish;
}
if (!OSKext::saveLoadedKextPanicListTyped("com.apple.", 0,
1, newlist, newlist_size, &newlist_length)) {
goto finish;
}
if (loaded_kext_paniclist) {
kfree(loaded_kext_paniclist, loaded_kext_paniclist_size);
}
loaded_kext_paniclist = newlist;
loaded_kext_paniclist_size = newlist_size;
loaded_kext_paniclist_length = newlist_length;
finish:
return;
}
void
OSKext::savePanicString(bool isLoading)
{
u_long len;
if (!kmod_info) {
return; }
len = assemble_identifier_and_version(kmod_info,
(isLoading) ? last_loaded_str : last_unloaded_str);
if (!len) {
printf("error saving unloaded kext info\n");
goto finish;
}
if (isLoading) {
last_loaded_strlen = len;
last_loaded_address = (void *)kmod_info->address;
last_loaded_size = kmod_info->size;
clock_get_uptime(&last_loaded_timestamp);
} else {
last_unloaded_strlen = len;
last_unloaded_address = (void *)kmod_info->address;
last_unloaded_size = kmod_info->size;
clock_get_uptime(&last_unloaded_timestamp);
}
finish:
return;
}
void
OSKext::printKextPanicLists(int (*printf_func)(const char *fmt, ...))
{
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_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_address, last_unloaded_size);
}
printf_func("loaded kexts:\n");
if (loaded_kext_paniclist &&
pmap_find_phys(kernel_pmap, (addr64_t) (uintptr_t) loaded_kext_paniclist) &&
loaded_kext_paniclist[0]) {
printf_func("%.*s", loaded_kext_paniclist_length, loaded_kext_paniclist);
} else {
printf_func("(none)\n");
}
return;
}
void
OSKext::updateLoadedKextSummaries(void)
{
kern_return_t result = KERN_FAILURE;
OSKextLoadedKextSummaryHeader *summaryHeader = NULL;
OSKextLoadedKextSummaryHeader *summaryHeaderAlloc = NULL;
OSKext *aKext;
vm_map_offset_t start, end;
size_t summarySize = 0;
size_t size;
u_int count;
u_int numKexts;
u_int i, j;
IOLockLock(sKextSummariesLock);
count = sLoadedKexts->getCount();
for (i = 0, numKexts = 0; i < count; ++i) {
aKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i));
numKexts += (aKext && aKext->isExecutable());
}
if (!numKexts) goto finish;
size = sizeof(*gLoadedKextSummaries);
size += numKexts * sizeof(*gLoadedKextSummaries->summaries);
size = round_page(size);
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 (result != KERN_SUCCESS) goto finish;
summaryHeader = summaryHeaderAlloc;
summarySize = size;
} else {
summaryHeader = sPrevLoadedKextSummaries;
summarySize = sPrevLoadedKextSummariesAllocSize;
start = (vm_map_offset_t) summaryHeader;
end = start + summarySize;
result = vm_map_protect(kernel_map, start, end, VM_PROT_DEFAULT, FALSE);
if (result != KERN_SUCCESS) goto finish;
}
bzero(summaryHeader, summarySize);
summaryHeader->version = kOSKextLoadedKextSummaryVersion;
summaryHeader->entry_size = sizeof(OSKextLoadedKextSummary);
summaryHeader->numSummaries = numKexts;
count = sLoadedKexts->getCount();
for (i = 0, j = 0; i < count; ++i) {
aKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i));
if (!aKext || !aKext->isExecutable()) continue;
aKext->updateLoadedKextSummary(&summaryHeader->summaries[j++]);
}
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;
gLoadedKextSummaries = summaryHeader;
sLoadedKextSummariesAllocSize = summarySize;
summaryHeaderAlloc = NULL;
if (sLoadedKextSummariesUpdated) (*sLoadedKextSummariesUpdated)();
finish:
IOLockUnlock(sKextSummariesLock);
if (summaryHeaderAlloc) {
kmem_free(kernel_map, (vm_offset_t)summaryHeaderAlloc, summarySize);
}
return;
}
void
OSKext::updateLoadedKextSummary(OSKextLoadedKextSummary *summary)
{
OSData *uuid;
strlcpy(summary->name, getIdentifierCString(),
sizeof(summary->name));
uuid = copyUUID();
if (uuid) {
memcpy(summary->uuid, uuid->getBytesNoCopy(), sizeof(summary->uuid));
OSSafeRelease(uuid);
}
summary->address = kmod_info->address;
summary->size = kmod_info->size;
summary->version = getVersion();
summary->loadTag = kmod_info->id;
summary->flags = 0;
summary->reference_list = (uint64_t) kmod_info->reference_list;
return;
}
#if __i386__
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;
IORecursiveLockLock(sKextLock);
k = kmod;
while (k) {
size += sizeof(kmod_info_t);
r = k->reference_list;
while (r) {
size +=sizeof(kmod_reference_t);
r = r->next;
}
k = k->next;
}
if (!size) {
result = KERN_SUCCESS;
goto finish;
}
result = kmem_alloc(kernel_map, &data, size);
if (result != KERN_SUCCESS) {
goto finish;
}
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;
}
kmod_info_scan_ptr++;
k = k->next;
}
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) {
*ref_scan_ptr = *r;
ref_scan_ptr++;
r = r->next;
ref_count++;
}
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;
}
*kmodCount = size;
result = KERN_SUCCESS;
finish:
IORecursiveLockUnlock(sKextLock);
if (result != KERN_SUCCESS && data) {
kmem_free(kernel_map, data, size);
*kmodList = (kmod_info_t *)0;
*kmodCount = 0;
}
return result;
}
#endif
#if PRAGMA_MARK
#pragma mark MAC Framework Support
#endif
#if CONFIG_MACF_KEXT
#ifdef IOC_DEBUG
#define DPRINTF(x) printf x
#else
#define IOC_DEBUG
#define DPRINTF(x)
#endif
static bool
MACFObjectIsPrimitiveType(OSObject * obj)
{
const OSMetaClass * typeID = NULL;
typeID = OSTypeIDInst(obj);
if (typeID == OSTypeID(OSString) || typeID == OSTypeID(OSNumber) ||
typeID == OSTypeID(OSBoolean) || typeID == OSTypeID(OSData)) {
return true;
}
return false;
}
static int
MACFLengthForObject(OSObject * obj)
{
const OSMetaClass * typeID = NULL; 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");
} 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;
}
static void
MACFInitElementFromObject(
struct mac_module_data_element * element,
OSObject * value)
{
const OSMetaClass * typeID = NULL;
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;
}
static struct mac_module_data *
MACFEncodeOSDictionary(OSDictionary * dict)
{
struct mac_module_data * result = NULL; const OSMetaClass * typeID = NULL; OSString * key = NULL; OSCollectionIterator * keyIterator = NULL; struct mac_module_data_element * element = NULL; unsigned int strtabsize = 0;
unsigned int listtabsize = 0;
unsigned int dicttabsize = 0;
unsigned int nkeys = 0;
unsigned int datalen = 0;
char * strtab = NULL; char * listtab = NULL; char * dicttab = NULL; vm_offset_t data_addr = 0;
keyIterator = OSCollectionIterator::withCollection(dict);
if (!keyIterator) {
goto finish;
}
while ( (key = OSDynamicCast(OSString, keyIterator->getNextObject())) ) {
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; OSString * dictkey = NULL; OSCollectionIterator * dictIterator = NULL;
dictObj = OSDynamicCast(OSDictionary, value);
dictIterator = OSCollectionIterator::withCollection(dictObj);
if (!dictIterator) {
goto finish;
}
while ((dictkey = OSDynamicCast(OSString,
dictIterator->getNextObject()))) {
OSObject * dictvalue = NULL;
dictvalue = dictObj->getObject(dictkey);
if (!dictvalue) {
continue;
}
if (MACFObjectIsPrimitiveType(dictvalue)) {
strtabsize += MACFLengthForObject(dictvalue);
} else {
continue;
}
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;
}
}
if (nents == 0) {
continue;
}
listtabsize += sizeof(struct mac_module_data_list) +
(nents - 1) * sizeof(struct mac_module_data_element);
} else {
continue;
}
strtabsize += key->getLength() + 1;
nkeys++;
}
if (nkeys == 0) {
goto finish;
}
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;
}
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())) ) {
OSObject * value = dict->getObject(key);
if (!value) {
continue;
}
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);
typeID = OSTypeIDInst(value);
if (MACFObjectIsPrimitiveType(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; OSDictionary * dictObj = NULL; OSString * dictkey = NULL; OSCollectionIterator * dictIterator = NULL; struct mac_module_data_list * dicthd = NULL; struct mac_module_data_element * dele = NULL;
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;
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;
}
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;
}
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;
}
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;
}
static void *
MACFCopyModuleDataForKext(
OSKext * theKext,
mach_msg_type_number_t * datalen)
{
struct mac_module_data * result = NULL;
OSDictionary * kextModuleData = NULL; 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;
finish:
return (void *)result;
}
#endif