OSReturn OSKextRetainKextWithLoadTag(uint32_t loadTag) { OSReturn result = kOSKextReturnNotFound; OSKext * theKext = NULL; // do not release; as this function is a retain if (loadTag == kOSKextInvalidLoadTag) { result = kOSKextReturnInvalidArgument; goto finish; } theKext = OSKext::lookupKextWithLoadTag(loadTag); if (theKext) { result = kOSReturnSuccess; OSKextLog(theKext, kOSKextLogDebugLevel | kOSKextLogKextBookkeepingFlag, "Kext %s (load tag %d) has been retained.", theKext->getIdentifierCString(), loadTag); /* Call this after so a log message about autounload comes second. */ theKext->setAutounloadEnabled(true); } else { OSKextLog(theKext, kOSKextLogErrorLevel | kOSKextLogKextBookkeepingFlag, "Can't retain kext with load tag %d - no such kext is loaded.", loadTag); } finish: return result; }
OSReturn OSKextReleaseKextWithLoadTag(uint32_t loadTag) { OSReturn result = kOSKextReturnNotFound; OSKext * theKext = NULL; // must release twice! if (loadTag == kOSKextInvalidLoadTag) { result = kOSKextReturnInvalidArgument; goto finish; } theKext = OSKext::lookupKextWithLoadTag(loadTag); if (theKext) { result = kOSReturnSuccess; OSKext::considerUnloads(); // schedule autounload pass theKext->release(); // do the release the caller wants theKext->release(); // now do the release on the lookup OSKextLog(theKext, kOSKextLogDebugLevel | kOSKextLogKextBookkeepingFlag, "Kext %s (load tag %d) has been released.", theKext->getIdentifierCString(), loadTag); } else { OSKextLog(theKext, kOSKextLogErrorLevel | kOSKextLogKextBookkeepingFlag, "Can't release kext with load tag %d - no such kext is loaded.", loadTag); } // xxx - should I check that the refcount of the OSKext is above the lower bound? // xxx - do we want a OSKextGetRetainCountOfKextWithLoadTag()? finish: return result; }
kern_return_t OSKextPingKextd(void) { kern_return_t result = KERN_FAILURE; mach_port_t kextd_port = IPC_PORT_NULL; result = host_get_kextd_port(host_priv_self(), &kextd_port); if (result != KERN_SUCCESS || !IPC_PORT_VALID(kextd_port)) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogIPCFlag, "Can't get kextd port."); goto finish; } result = kextd_ping(kextd_port); if (result != KERN_SUCCESS) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogIPCFlag, "kextd ping failed (0x%x).", (int)result); goto finish; } finish: if (IPC_PORT_VALID(kextd_port)) { ipc_port_release_send(kextd_port); } return result; }
ExitStatus checkKextForProblems( KcgenArgs * toolArgs, OSKextRef theKext, const NXArchInfo * arch) { ExitStatus result = EX_SOFTWARE; char kextPath[PATH_MAX]; if (!CFURLGetFileSystemRepresentation(OSKextGetURL(theKext), /* resolveToBase */ false, (UInt8 *)kextPath, sizeof(kextPath))) { strlcpy(kextPath, "(unknown)", sizeof(kextPath)); } /* Skip kexts we have no interest in for the current arch. */ if (!OSKextSupportsArchitecture(theKext, arch)) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag, "%s doesn't support architecture '%s'; ommiting.", kextPath, arch->name); goto finish; } if (!OSKextIsValid(theKext)) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag | kOSKextLogValidationFlag | kOSKextLogGeneralFlag, "%s is not valid; omitting.", kextPath); if (toolArgs->printTestResults) { OSKextLogDiagnostics(theKext, kOSKextDiagnosticsFlagAll); } goto finish; } if (!OSKextResolveDependencies(theKext)) { OSKextLog(/* kext */ NULL, kOSKextLogWarningLevel | kOSKextLogArchiveFlag | kOSKextLogDependenciesFlag | kOSKextLogGeneralFlag, "%s is missing dependencies (including anyway; " "dependencies may be available from elsewhere)", kextPath); if (toolArgs->printTestResults) { OSKextLogDiagnostics(theKext, kOSKextDiagnosticsFlagAll); } } result = EX_OK; finish: return result; }
/****************************************************************************** * _kextmanager_lock_volume tries to lock volumes for clients (kextutil) *****************************************************************************/ kern_return_t _kextmanager_lock_kextload( mach_port_t server, mach_port_t client, int * lockstatus) { kern_return_t mig_result = KERN_FAILURE; int result; if (!lockstatus) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogIPCFlag, "kextmanager_lock_kextload requires non-NULL lockstatus."); mig_result = KERN_SUCCESS; result = EINVAL; goto finish; } if (gClientUID != 0) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel, "Non-root process doesn't need to lock as it will fail to load."); mig_result = KERN_SUCCESS; result = EPERM; goto finish; } if (_gKextutilLock) { mig_result = KERN_SUCCESS; result = EBUSY; goto finish; } result = ENOMEM; _gKextutilLock = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND, client, DISPATCH_MACH_SEND_DEAD, dispatch_get_main_queue()); if (_gKextutilLock) { dispatch_source_set_event_handler(_gKextutilLock, ^{ OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogIPCFlag, "Client exited without releasing kextutil lock."); removeKextutilLock(); }); dispatch_source_set_cancel_handler(_gKextutilLock, ^{ dispatch_release(_gKextutilLock); mach_port_deallocate(mach_task_self(), client); _gKextutilLock = NULL; });
void KLDBootstrap::readStartupExtensions(void) { kernel_section_t * prelinkInfoSect = NULL; // do not free OSKextLog(/* kext */ NULL, kOSKextLogProgressLevel | kOSKextLogGeneralFlag | kOSKextLogDirectoryScanFlag | kOSKextLogKextBookkeepingFlag, "Reading startup extensions."); /* If the prelink info segment has a nonzero size, we are prelinked * and won't have any individual kexts or mkexts to read. * Otherwise, we need to read kexts or the mkext from what the booter * has handed us. */ prelinkInfoSect = getsectbyname(kPrelinkInfoSegment, kPrelinkInfoSection); if (prelinkInfoSect->size) { readPrelinkedExtensions(prelinkInfoSect); } else { readBooterExtensions(); } loadKernelComponentKexts(); loadKernelExternalComponents(); readBuiltinPersonalities(); OSKext::sendAllKextPersonalitiesToCatalog(); return; }
/********************************************************************* * We used to require that all listed kernel components load, but * nowadays we can get them from userland so we only try to load the * ones we have. If an error occurs later, such is life. * * Note that we look the kexts up first, so we can avoid spurious * (in this context, anyhow) log messages about kexts not being found. * * xxx - do we even need to do this any more? Check if the kernel * xxx - compoonents just load in the regular paths *********************************************************************/ OSReturn KLDBootstrap::loadKernelComponentKexts(void) { OSReturn result = kOSReturnSuccess; // optimistic OSKext * theKext = NULL; // must release const char ** kextIDPtr = NULL; // do not release for (kextIDPtr = &sKernelComponentNames[0]; *kextIDPtr; kextIDPtr++) { OSSafeReleaseNULL(theKext); theKext = OSKext::lookupKextWithIdentifier(*kextIDPtr); if (theKext) { if (kOSReturnSuccess != OSKext::loadKextWithIdentifier( *kextIDPtr, /* allowDefer */ false)) { // xxx - check KextBookkeeping, might be redundant OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogDirectoryScanFlag | kOSKextLogKextBookkeepingFlag, "Failed to initialize kernel component %s.", *kextIDPtr); result = kOSReturnError; } } } OSSafeReleaseNULL(theKext); return result; }
void filterKextID(const void * vValue, void * vContext) { CFStringRef kextID = (CFStringRef)vValue; FilterIDContext * context = (FilterIDContext *)vContext; OSKextRef theKext = OSKextGetKextWithIdentifier(kextID); if (!theKext) { char kextIDCString[KMOD_MAX_NAME]; CFStringGetCString(kextID, kextIDCString, sizeof(kextIDCString), kCFStringEncodingUTF8); if (context->optional) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Can't find kext with optional identifier %s; skipping.", kextIDCString); } else { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Error - can't find kext with identifier %s.", kextIDCString); context->error = TRUE; } goto finish; } if (checkKextForProblems(context->toolArgs, theKext, context->arch)) { if (!context->optional) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Error - a required kext was omitted"); context->error = true; } goto finish; } if (!CFArrayContainsValue(context->kextArray, RANGE_ALL(context->kextArray), theKext)) { CFArrayAppendValue(context->kextArray, theKext); } finish: return; }
ExitStatus setPrelinkedKernelArgs( KcgenArgs * toolArgs, char * filename) { ExitStatus result = EX_USAGE; if (toolArgs->prelinkedKernelPath) { OSKextLog(/* kext */ NULL, kOSKextLogWarningLevel | kOSKextLogGeneralFlag, "Warning - prelinked kernel already specified; using last."); } else { toolArgs->prelinkedKernelPath = malloc(PATH_MAX); if (!toolArgs->prelinkedKernelPath) { OSKextLogMemError(); result = EX_OSERR; goto finish; } } /* If we don't have a filename we construct a default one, automatically * add the system extensions folders, and note that we're using default * info. */ if (!filename) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Error - prelinked kernel filename required"); goto finish; } else { size_t len = strlcpy(toolArgs->prelinkedKernelPath, filename, PATH_MAX); if (len >= PATH_MAX) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Error - prelinked kernel filename length exceeds PATH_MAX"); goto finish; } } result = EX_OK; finish: return result; }
ExitStatus checkArgs(KcgenArgs * toolArgs) { ExitStatus result = EX_USAGE; if (!toolArgs->prelinkedKernelPath) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Error - no work to do; check options and try again."); goto finish; } if (!CFArrayGetCount(toolArgs->argURLs) && !toolArgs->compress && !toolArgs->uncompress) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Error - no kexts or directories specified."); goto finish; } if (!toolArgs->compress && !toolArgs->uncompress) { toolArgs->compress = true; } else if (toolArgs->compress && toolArgs->uncompress) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Both -%s and -%s specified; using -%s.", kOptNameCompressed, kOptNameUncompressed, kOptNameCompressed); toolArgs->compress = true; toolArgs->uncompress = false; } result = EX_OK; finish: if (result == EX_USAGE) { usage(kUsageLevelBrief); } return result; }
OSReturn KLDBootstrap::readMkextExtensions( OSString * deviceTreeName, OSData * booterData) { OSReturn result = kOSReturnError; uint32_t checksum; IORegistryEntry * registryRoot = NULL; // do not release OSData * checksumObj = NULL; // must release OSKextLog(/* kext */ NULL, kOSKextLogStepLevel | kOSKextLogDirectoryScanFlag | kOSKextLogArchiveFlag, "Reading startup mkext archive from device tree entry %s.", deviceTreeName->getCStringNoCopy()); /* If we successfully read the archive, * then save the mkext's checksum in the IORegistry. * assumes we'll only ever have one mkext to boot */ result = OSKext::readMkextArchive(booterData, &checksum); if (result == kOSReturnSuccess) { OSKextLog(/* kext */ NULL, kOSKextLogProgressLevel | kOSKextLogArchiveFlag, "Startup mkext archive has checksum 0x%x.", (int)checksum); registryRoot = IORegistryEntry::getRegistryRoot(); assert(registryRoot); checksumObj = OSData::withBytes((void *)&checksum, sizeof(checksum)); assert(checksumObj); if (checksumObj) { registryRoot->setProperty(kOSStartupMkextCRC, checksumObj); } } return result; }
Boolean getKextAddressAndSize(CFDictionaryRef infoDict, uint64_t *addr, uint64_t *size) { Boolean result = FALSE; CFNumberRef scratchNum = NULL; // do not release scratchNum = CFDictionaryGetValue(infoDict, CFSTR(kPrelinkExecutableSourceKey)); if (!scratchNum) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Cannot find kext load address"); goto finish; } if (!CFNumberGetValue(scratchNum, kCFNumberSInt64Type, addr)) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Cannot convert kext load address"); goto finish; } scratchNum = CFDictionaryGetValue(infoDict, CFSTR(kPrelinkExecutableSizeKey)); if (!scratchNum) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Cannot find kext size\n"); goto finish; } if (!CFNumberGetValue(scratchNum, kCFNumberSInt64Type, size)) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Cannot convert kext size\n"); goto finish; } result = TRUE; finish: return result; }
void OSMetaClass::instanceDestructed() const { if ((1 == OSDecrementAtomic(&instanceCount)) && superClassLink) { superClassLink->instanceDestructed(); } if (((int)instanceCount) < 0) { OSKext * myKext = (OSKext *)reserved; OSKextLog(myKext, kOSMetaClassLogSpec, // xxx - this phrasing is rather cryptic "OSMetaClass: Class %s - bad retain (%d)", getClassName(), instanceCount); } }
/********************************************************************* * The core constructor for a MetaClass (defined with this name always * but within the scope of its represented class). * * MetaClass constructors are invoked in OSRuntimeInitializeCPP(), * in between calls to OSMetaClass::preModLoad(), which sets up for * registration, and OSMetaClass::postModLoad(), which actually * records all the class/kext relationships of the new MetaClasses. *********************************************************************/ OSMetaClass::OSMetaClass( const char * inClassName, const OSMetaClass * inSuperClass, unsigned int inClassSize) { instanceCount = 0; classSize = inClassSize; superClassLink = inSuperClass; /* Hack alert: We are just casting inClassName and storing it in * an OSString * instance variable. This may be because you can't * create C++ objects in static constructors, but I really don't know! */ className = (const OSSymbol *)inClassName; // sStalledClassesLock taken in preModLoad if (!sStalled) { /* There's no way we can look up the kext here, unfortunately. */ OSKextLog(/* kext */ NULL, kOSMetaClassLogSpec, "OSMetaClass: preModLoad() wasn't called for class %s " "(runtime internal error).", inClassName); } else if (!sStalled->result) { // Grow stalled array if neccessary if (sStalled->count >= sStalled->capacity) { OSMetaClass **oldStalled = sStalled->classes; int oldSize = sStalled->capacity * sizeof(OSMetaClass *); int newSize = oldSize + kKModCapacityIncrement * sizeof(OSMetaClass *); sStalled->classes = (OSMetaClass **)kalloc(newSize); if (!sStalled->classes) { sStalled->classes = oldStalled; sStalled->result = kOSMetaClassNoTempData; return; } sStalled->capacity += kKModCapacityIncrement; memmove(sStalled->classes, oldStalled, oldSize); kfree(oldStalled, oldSize); ACCUMSIZE(newSize - oldSize); } sStalled->classes[sStalled->count++] = this; } }
static void OSMetaClassLogErrorForKext( OSReturn error, OSKext * aKext) { const char * message = NULL; switch (error) { case kOSReturnSuccess: return; case kOSMetaClassNoInit: // xxx - never returned; logged at fail site message = "OSMetaClass: preModLoad() wasn't called (runtime internal error)."; break; case kOSMetaClassNoDicts: message = "OSMetaClass: Allocation failure for OSMetaClass internal dictionaries."; break; case kOSMetaClassNoKModSet: message = "OSMetaClass: Allocation failure for internal kext recording set/set missing."; break; case kOSMetaClassNoInsKModSet: message = "OSMetaClass: Failed to record class in kext."; break; case kOSMetaClassDuplicateClass: message = "OSMetaClass: Duplicate class encountered."; break; case kOSMetaClassNoSuper: // xxx - never returned message = "OSMetaClass: Can't associate a class with its superclass."; break; case kOSMetaClassInstNoSuper: // xxx - never returned message = "OSMetaClass: Instance construction error; unknown superclass."; break; case kOSMetaClassNoKext: message = "OSMetaClass: Kext not found for metaclass."; break; case kOSMetaClassInternal: default: message = "OSMetaClass: Runtime internal error."; break; } if (message) { OSKextLog(aKext, kOSMetaClassLogSpec, "%s", message); } return; }
/******************************************************************************* * XXX: I'm really not sure this is completely reliable for getting a relative * XXX: path. *******************************************************************************/ CFStringRef copyKextInfoDictionaryPath( OSKextRef theKext, PathSpec pathSpec) { CFStringRef result = NULL; CFURLRef kextURL = NULL; // do not release CFURLRef kextAbsURL = NULL; // must release CFBundleRef kextBundle = NULL; // must release CFURLRef infoDictURL = NULL; // must release kextURL = OSKextGetURL(theKext); if (!kextURL) { OSKextLog(theKext, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Kext has no URL!"); goto finish; } kextAbsURL = CFURLCopyAbsoluteURL(kextURL); if (!kextAbsURL) { OSKextLogMemError(); goto finish; } kextBundle = CFBundleCreate(kCFAllocatorDefault, kextAbsURL); if (!kextBundle) { OSKextLogMemError(); goto finish; } infoDictURL = _CFBundleCopyInfoPlistURL(kextBundle); if (!infoDictURL) { // not able to determine error here, bundle might have no plist // (well, we should never have gotten here if that were the case) result = CFStringCreateWithCString(kCFAllocatorDefault, "", kCFStringEncodingUTF8); goto finish; } result = copyAdjustedPathForURL(theKext, infoDictURL, pathSpec); finish: SAFE_RELEASE(infoDictURL); SAFE_RELEASE(kextBundle); SAFE_RELEASE(kextAbsURL); return result; }
/******************************************************************************* * XXX: I'm really not sure this is completely reliable for getting a relative * XXX: path. *******************************************************************************/ CFStringRef copyKextExecutablePath( OSKextRef theKext, PathSpec pathSpec) { CFStringRef result = NULL; CFURLRef kextURL = NULL; // do not release CFURLRef kextAbsURL = NULL; // must release CFURLRef executableURL = NULL; // must release kextURL = OSKextGetURL(theKext); if (!kextURL) { OSKextLog(theKext, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Kext has no URL!"); goto finish; } kextAbsURL = CFURLCopyAbsoluteURL(kextURL); if (!kextAbsURL) { OSKextLogMemError(); goto finish; } executableURL = _CFBundleCopyExecutableURLInDirectory(kextAbsURL); if (!executableURL) { // not able to determine error here, bundle might have no executable result = CFStringCreateWithCString(kCFAllocatorDefault, "", kCFStringEncodingUTF8); goto finish; } result = copyAdjustedPathForURL(theKext, executableURL, pathSpec); finish: SAFE_RELEASE(executableURL); SAFE_RELEASE(kextAbsURL); return result; }
/******************************************************************************* * copyAdjustedPathForURL() * * This function takes an URL with a given kext, and adjusts it to be absolute * or relative to the kext's containing repository, properly handling plugin * kexts to include the repository-path of the containing kext as well. *******************************************************************************/ CFStringRef copyAdjustedPathForURL( OSKextRef theKext, CFURLRef urlToAdjust, PathSpec pathSpec) { CFStringRef result = NULL; CFURLRef absURL = NULL; // must release CFStringRef absPath = NULL; // must release CFStringRef kextAbsPath = NULL; // must release CFStringRef kextRelPath = NULL; // must release CFStringRef pathInKext = NULL; // must release CFRange scratchRange; if (pathSpec != kPathsFull && pathSpec != kPathsRelative) { OSKextLog(theKext, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Invalid argument to copyAdjustedPathForURL()."); } absURL = CFURLCopyAbsoluteURL(urlToAdjust); if (!absURL) { OSKextLogMemError(); goto finish; } if (pathSpec == kPathsFull) { result = CFURLCopyFileSystemPath(absURL, kCFURLPOSIXPathStyle); goto finish; } /***** * Okay, we are doing repository-relative paths here. Here's how! * We are strip the matching part of the kext's absolute path * from the URL/path handed in, which gives us the path in the kext. * Then we tack that back onto the kext's repository-relative path. Got it? */ kextAbsPath = copyPathForKext(theKext, kPathsFull); kextRelPath = copyPathForKext(theKext, kPathsRelative); absPath = CFURLCopyFileSystemPath(absURL, kCFURLPOSIXPathStyle); if (!kextAbsPath || !kextRelPath || !absPath) { goto finish; } scratchRange = CFRangeMake(CFStringGetLength(kextAbsPath), CFStringGetLength(absPath) - CFStringGetLength(kextAbsPath)); pathInKext = CFStringCreateWithSubstring(kCFAllocatorDefault, absPath, scratchRange); if (!pathInKext) { OSKextLogMemError(); } result = CFStringCreateWithFormat(kCFAllocatorDefault, /* options */ 0, CFSTR("%@%@"), kextRelPath, pathInKext); finish: SAFE_RELEASE(absURL); SAFE_RELEASE(absPath); SAFE_RELEASE(kextAbsPath); SAFE_RELEASE(kextRelPath); SAFE_RELEASE(pathInKext); return result; }
void KLDBootstrap::readBuiltinPersonalities(void) { OSObject * parsedXML = NULL; // must release OSArray * builtinExtensions = NULL; // do not release OSArray * allPersonalities = NULL; // must release OSString * errorString = NULL; // must release kernel_section_t * infosect = NULL; // do not free OSCollectionIterator * personalitiesIterator = NULL; // must release unsigned int count, i; OSKextLog(/* kext */ NULL, kOSKextLogStepLevel | kOSKextLogLoadFlag, "Reading built-in kernel personalities for I/O Kit drivers."); /* Look in the __BUILTIN __info segment for an array of Info.plist * entries. For each one, extract the personalities dictionary, add * it to our array, then push them all (without matching) to * the IOCatalogue. This can be used to augment the personalities * in gIOKernelConfigTables, especially when linking entire kexts into * the mach_kernel image. */ infosect = getsectbyname("__BUILTIN", "__info"); if (!infosect) { // this isn't fatal goto finish; } parsedXML = OSUnserializeXML((const char *) (uintptr_t)infosect->addr, &errorString); if (parsedXML) { builtinExtensions = OSDynamicCast(OSArray, parsedXML); } if (!builtinExtensions) { const char * errorCString = "(unknown error)"; if (errorString && errorString->getCStringNoCopy()) { errorCString = errorString->getCStringNoCopy(); } else if (parsedXML) { errorCString = "not an array"; } OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogLoadFlag, "Error unserializing built-in personalities: %s.", errorCString); goto finish; } // estimate 3 personalities per Info.plist/kext count = builtinExtensions->getCount(); allPersonalities = OSArray::withCapacity(count * 3); for (i = 0; i < count; i++) { OSDictionary * infoDict = NULL; // do not release OSString * moduleName = NULL; // do not release OSDictionary * personalities; // do not release OSString * personalityName; // do not release OSSafeReleaseNULL(personalitiesIterator); infoDict = OSDynamicCast(OSDictionary, builtinExtensions->getObject(i)); if (!infoDict) { continue; } moduleName = OSDynamicCast(OSString, infoDict->getObject(kCFBundleIdentifierKey)); if (!moduleName) { continue; } OSKextLog(/* kext */ NULL, kOSKextLogStepLevel | kOSKextLogLoadFlag, "Adding personalities for built-in driver %s:", moduleName->getCStringNoCopy()); personalities = OSDynamicCast(OSDictionary, infoDict->getObject("IOKitPersonalities")); if (!personalities) { continue; } personalitiesIterator = OSCollectionIterator::withCollection(personalities); if (!personalitiesIterator) { continue; // xxx - well really, what can we do? should we panic? } while ((personalityName = OSDynamicCast(OSString, personalitiesIterator->getNextObject()))) { OSDictionary * personality = OSDynamicCast(OSDictionary, personalities->getObject(personalityName)); OSKextLog(/* kext */ NULL, kOSKextLogDetailLevel | kOSKextLogLoadFlag, "Adding built-in driver personality %s.", personalityName->getCStringNoCopy()); if (personality && !personality->getObject(kCFBundleIdentifierKey)) { personality->setObject(kCFBundleIdentifierKey, moduleName); } allPersonalities->setObject(personality); } } gIOCatalogue->addDrivers(allPersonalities, false); finish: OSSafeReleaseNULL(parsedXML); OSSafeReleaseNULL(allPersonalities); OSSafeReleaseNULL(errorString); OSSafeReleaseNULL(personalitiesIterator); return; }
void KLDBootstrap::loadKernelExternalComponents(void) { OSDictionary * extensionsDict = NULL; // must release OSCollectionIterator * keyIterator = NULL; // must release OSString * bundleID = NULL; // don't release OSKext * theKext = NULL; // don't release OSBoolean * isKernelExternalComponent = NULL; // don't release OSKextLog(/* kext */ NULL, kOSKextLogStepLevel | kOSKextLogLoadFlag, "Loading Kernel External Components."); extensionsDict = OSKext::copyKexts(); if (!extensionsDict) { return; } keyIterator = OSCollectionIterator::withCollection(extensionsDict); if (!keyIterator) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Failed to allocate iterator for Kernel External Components."); goto finish; } while ((bundleID = OSDynamicCast(OSString, keyIterator->getNextObject()))) { const char * bundle_id = bundleID->getCStringNoCopy(); /* Skip extensions whose bundle IDs don't start with "com.apple.kec.". */ if (!bundle_id || (strncmp(bundle_id, COM_APPLE_KEC, CONST_STRLEN(COM_APPLE_KEC)) != 0)) { continue; } theKext = OSDynamicCast(OSKext, extensionsDict->getObject(bundleID)); if (!theKext) { continue; } isKernelExternalComponent = OSDynamicCast(OSBoolean, theKext->getPropertyForHostArch(kAppleKernelExternalComponentKey)); if (isKernelExternalComponent && isKernelExternalComponent->isTrue()) { OSKextLog(/* kext */ NULL, kOSKextLogStepLevel | kOSKextLogLoadFlag, "Loading kernel external component %s.", bundleID->getCStringNoCopy()); OSKext::loadKextWithIdentifier(bundleID->getCStringNoCopy(), /* allowDefer */ false); } } finish: OSSafeReleaseNULL(keyIterator); OSSafeReleaseNULL(extensionsDict); return; }
void KLDBootstrap::readBooterExtensions(void) { IORegistryEntry * booterMemoryMap = NULL; // must release OSDictionary * propertyDict = NULL; // must release OSCollectionIterator * keyIterator = NULL; // must release OSString * deviceTreeName = NULL; // do not release const _DeviceTreeBuffer * deviceTreeBuffer = NULL; // do not free char * booterDataPtr = NULL; // do not free OSData * booterData = NULL; // must release OSKext * aKext = NULL; // must release OSKextLog(/* kext */ NULL, kOSKextLogProgressLevel | kOSKextLogDirectoryScanFlag | kOSKextLogKextBookkeepingFlag, "Reading startup extensions from booter memory."); booterMemoryMap = IORegistryEntry::fromPath( "/chosen/memory-map", gIODTPlane); if (!booterMemoryMap) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag | kOSKextLogDirectoryScanFlag, "Can't read booter memory map."); goto finish; } propertyDict = booterMemoryMap->dictionaryWithProperties(); if (!propertyDict) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogDirectoryScanFlag, "Can't get property dictionary from memory map."); goto finish; } keyIterator = OSCollectionIterator::withCollection(propertyDict); if (!keyIterator) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Can't allocate iterator for driver images."); goto finish; } /* Create dictionary of excluded kexts */ OSKext::createExcludeListFromBooterData(propertyDict, keyIterator); keyIterator->reset(); while ( ( deviceTreeName = OSDynamicCast(OSString, keyIterator->getNextObject() ))) { const char * devTreeNameCString = deviceTreeName->getCStringNoCopy(); OSData * deviceTreeEntry = OSDynamicCast(OSData, propertyDict->getObject(deviceTreeName)); /* Clear out the booterData from the prior iteration. */ OSSafeReleaseNULL(booterData); /* If there is no entry for the name, we can't do much with it. */ if (!deviceTreeEntry) { continue; } /* Make sure it is a kext */ if (strncmp(devTreeNameCString, BOOTER_KEXT_PREFIX, CONST_STRLEN(BOOTER_KEXT_PREFIX))) { continue; } deviceTreeBuffer = (const _DeviceTreeBuffer *) deviceTreeEntry->getBytesNoCopy(0, sizeof(deviceTreeBuffer)); if (!deviceTreeBuffer) { /* We can't get to the data, so we can't do anything, * not even free it from physical memory (if it's there). */ OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogDirectoryScanFlag, "Device tree entry %s has NULL pointer.", devTreeNameCString); goto finish; // xxx - continue, panic? } booterDataPtr = (char *)ml_static_ptovirt(deviceTreeBuffer->paddr); if (!booterDataPtr) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogDirectoryScanFlag, "Can't get virtual address for device tree entry %s.", devTreeNameCString); goto finish; } /* Wrap the booter data buffer in an OSData and set a dealloc function * so it will take care of the physical memory when freed. Kexts will * retain the booterData for as long as they need it. Remove the entry * from the booter memory map after this is done. */ booterData = OSData::withBytesNoCopy(booterDataPtr, deviceTreeBuffer->length); if (!booterData) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Error - Can't allocate OSData wrapper for device tree entry %s.", devTreeNameCString); goto finish; } booterData->setDeallocFunction(osdata_phys_free); /* Create the kext for the entry, then release it, because the * kext system keeps them around until explicitly removed. * Any creation/registration failures are already logged for us. */ OSKext * newKext = OSKext::withBooterData(deviceTreeName, booterData); OSSafeReleaseNULL(newKext); booterMemoryMap->removeProperty(deviceTreeName); } /* while ( (deviceTreeName = OSDynamicCast(OSString, ...) ) ) */ finish: OSSafeReleaseNULL(booterMemoryMap); OSSafeReleaseNULL(propertyDict); OSSafeReleaseNULL(keyIterator); OSSafeReleaseNULL(booterData); OSSafeReleaseNULL(aKext); return; }
void KLDBootstrap::readPrelinkedExtensions( kernel_section_t * prelinkInfoSect) { OSArray * infoDictArray = NULL; // do not release OSObject * parsedXML = NULL; // must release OSDictionary * prelinkInfoDict = NULL; // do not release OSString * errorString = NULL; // must release OSKext * theKernel = NULL; // must release kernel_segment_command_t * prelinkTextSegment = NULL; // see code kernel_segment_command_t * prelinkInfoSegment = NULL; // see code /* We make some copies of data, but if anything fails we're basically * going to fail the boot, so these won't be cleaned up on error. */ void * prelinkData = NULL; // see code vm_size_t prelinkLength = 0; OSDictionary * infoDict = NULL; // do not release IORegistryEntry * registryRoot = NULL; // do not release OSNumber * prelinkCountObj = NULL; // must release u_int i = 0; #if NO_KEXTD bool ramDiskBoot; bool developerDevice; bool dontLoad; #endif OSData * kaslrOffsets = NULL; unsigned long plk_segSizes[PLK_SEGMENTS]; vm_offset_t plk_segAddrs[PLK_SEGMENTS]; OSKextLog(/* kext */ NULL, kOSKextLogProgressLevel | kOSKextLogDirectoryScanFlag | kOSKextLogArchiveFlag, "Starting from prelinked kernel."); prelinkTextSegment = getsegbyname(kPrelinkTextSegment); if (!prelinkTextSegment) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogDirectoryScanFlag | kOSKextLogArchiveFlag, "Can't find prelinked kexts' text segment."); goto finish; } #if KASLR_KEXT_DEBUG unsigned long scratchSize; vm_offset_t scratchAddr; IOLog("kaslr: prelinked kernel address info: \n"); scratchAddr = (vm_offset_t) getsegdatafromheader(&_mh_execute_header, "__TEXT", &scratchSize); IOLog("kaslr: start 0x%lx end 0x%lx length %lu for __TEXT \n", (unsigned long)scratchAddr, (unsigned long)(scratchAddr + scratchSize), scratchSize); scratchAddr = (vm_offset_t) getsegdatafromheader(&_mh_execute_header, "__DATA", &scratchSize); IOLog("kaslr: start 0x%lx end 0x%lx length %lu for __DATA \n", (unsigned long)scratchAddr, (unsigned long)(scratchAddr + scratchSize), scratchSize); scratchAddr = (vm_offset_t) getsegdatafromheader(&_mh_execute_header, "__LINKEDIT", &scratchSize); IOLog("kaslr: start 0x%lx end 0x%lx length %lu for __LINKEDIT \n", (unsigned long)scratchAddr, (unsigned long)(scratchAddr + scratchSize), scratchSize); scratchAddr = (vm_offset_t) getsegdatafromheader(&_mh_execute_header, "__KLD", &scratchSize); IOLog("kaslr: start 0x%lx end 0x%lx length %lu for __KLD \n", (unsigned long)scratchAddr, (unsigned long)(scratchAddr + scratchSize), scratchSize); scratchAddr = (vm_offset_t) getsegdatafromheader(&_mh_execute_header, "__PRELINK_TEXT", &scratchSize); IOLog("kaslr: start 0x%lx end 0x%lx length %lu for __PRELINK_TEXT \n", (unsigned long)scratchAddr, (unsigned long)(scratchAddr + scratchSize), scratchSize); scratchAddr = (vm_offset_t) getsegdatafromheader(&_mh_execute_header, "__PRELINK_INFO", &scratchSize); IOLog("kaslr: start 0x%lx end 0x%lx length %lu for __PRELINK_INFO \n", (unsigned long)scratchAddr, (unsigned long)(scratchAddr + scratchSize), scratchSize); #endif prelinkData = (void *) prelinkTextSegment->vmaddr; prelinkLength = prelinkTextSegment->vmsize; /* build arrays of plk info for later use */ const char ** segNamePtr; for (segNamePtr = &plk_segNames[0], i = 0; *segNamePtr && i < PLK_SEGMENTS; segNamePtr++, i++) { plk_segSizes[i] = 0; plk_segAddrs[i] = (vm_offset_t)getsegdatafromheader(&_mh_execute_header, *segNamePtr, &plk_segSizes[i]); } /* Unserialize the info dictionary from the prelink info section. */ parsedXML = OSUnserializeXML((const char *)prelinkInfoSect->addr, &errorString); if (parsedXML) { prelinkInfoDict = OSDynamicCast(OSDictionary, parsedXML); } if (!prelinkInfoDict) { const char * errorCString = "(unknown error)"; if (errorString && errorString->getCStringNoCopy()) { errorCString = errorString->getCStringNoCopy(); } else if (parsedXML) { errorCString = "not a dictionary"; } OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag, "Error unserializing prelink plist: %s.", errorCString); goto finish; } #if NO_KEXTD /* Check if we should keep developer kexts around. * TODO: Check DeviceTree instead of a boot-arg <rdar://problem/10604201> */ developerDevice = true; PE_parse_boot_argn("developer", &developerDevice, sizeof(developerDevice)); ramDiskBoot = IORamDiskBSDRoot(); #endif /* NO_KEXTD */ infoDictArray = OSDynamicCast(OSArray, prelinkInfoDict->getObject(kPrelinkInfoDictionaryKey)); if (!infoDictArray) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag, "The prelinked kernel has no kext info dictionaries"); goto finish; } /* kaslrOffsets are available use them to slide local relocations */ kaslrOffsets = OSDynamicCast(OSData, prelinkInfoDict->getObject(kPrelinkLinkKASLROffsetsKey)); /* Create dictionary of excluded kexts */ OSKext::createExcludeListFromPrelinkInfo(infoDictArray); /* Create OSKext objects for each info dictionary. */ for (i = 0; i < infoDictArray->getCount(); ++i) { infoDict = OSDynamicCast(OSDictionary, infoDictArray->getObject(i)); if (!infoDict) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogDirectoryScanFlag | kOSKextLogArchiveFlag, "Can't find info dictionary for prelinked kext #%d.", i); continue; } #if NO_KEXTD dontLoad = false; /* If we're not on a developer device, skip and free developer kexts. */ if (developerDevice == false) { OSBoolean *devOnlyBool = OSDynamicCast(OSBoolean, infoDict->getObject(kOSBundleDeveloperOnlyKey)); if (devOnlyBool == kOSBooleanTrue) { dontLoad = true; } } /* Skip and free kexts that are only needed when booted from a ram disk. */ if (ramDiskBoot == false) { OSBoolean *ramDiskOnlyBool = OSDynamicCast(OSBoolean, infoDict->getObject(kOSBundleRamDiskOnlyKey)); if (ramDiskOnlyBool == kOSBooleanTrue) { dontLoad = true; } } if (dontLoad == true) { OSString *bundleID = OSDynamicCast(OSString, infoDict->getObject(kCFBundleIdentifierKey)); if (bundleID) { OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogGeneralFlag, "Kext %s not loading.", bundleID->getCStringNoCopy()); } OSNumber *addressNum = OSDynamicCast(OSNumber, infoDict->getObject(kPrelinkExecutableLoadKey)); OSNumber *lengthNum = OSDynamicCast(OSNumber, infoDict->getObject(kPrelinkExecutableSizeKey)); if (addressNum && lengthNum) { #error Pick the right way to free prelinked data on this arch } infoDictArray->removeObject(i--); continue; } #endif /* NO_KEXTD */ /* Create the kext for the entry, then release it, because the * kext system keeps them around until explicitly removed. * Any creation/registration failures are already logged for us. */ OSKext * newKext = OSKext::withPrelinkedInfoDict(infoDict, (kaslrOffsets ? TRUE : FALSE)); OSSafeReleaseNULL(newKext); } /* slide kxld relocations */ if (kaslrOffsets && vm_kernel_slide > 0) { int slidKextAddrCount = 0; int badSlideAddr = 0; int badSlideTarget = 0; kaslrPackedOffsets * myOffsets = NULL; myOffsets = (kaslrPackedOffsets *) kaslrOffsets->getBytesNoCopy(); for (uint32_t j = 0; j < myOffsets->count; j++) { uint64_t slideOffset = (uint64_t) myOffsets->offsetsArray[j]; uintptr_t * slideAddr = (uintptr_t *) ((uint64_t)prelinkData + slideOffset); int slideAddrSegIndex = -1; int addrToSlideSegIndex = -1; slideAddrSegIndex = __whereIsAddr( (vm_offset_t)slideAddr, &plk_segSizes[0], &plk_segAddrs[0], PLK_SEGMENTS ); if (slideAddrSegIndex >= 0) { addrToSlideSegIndex = __whereIsAddr( (vm_offset_t)(*slideAddr + vm_kernel_slide), &plk_segSizes[0], &plk_segAddrs[0], PLK_SEGMENTS ); if (addrToSlideSegIndex < 0) { badSlideTarget++; continue; } } else { badSlideAddr++; continue; } slidKextAddrCount++; *(slideAddr) += vm_kernel_slide; } // for ... /* All kexts are now slid, set VM protections for them */ OSKext::setAllVMAttributes(); } /* Store the number of prelinked kexts in the registry so we can tell * when the system has been started from a prelinked kernel. */ registryRoot = IORegistryEntry::getRegistryRoot(); assert(registryRoot); prelinkCountObj = OSNumber::withNumber( (unsigned long long)infoDictArray->getCount(), 8 * sizeof(uint32_t)); assert(prelinkCountObj); if (prelinkCountObj) { registryRoot->setProperty(kOSPrelinkKextCountKey, prelinkCountObj); } OSKextLog(/* kext */ NULL, kOSKextLogProgressLevel | kOSKextLogGeneralFlag | kOSKextLogKextBookkeepingFlag | kOSKextLogDirectoryScanFlag | kOSKextLogArchiveFlag, "%u prelinked kexts", infoDictArray->getCount()); #if CONFIG_KEXT_BASEMENT /* On CONFIG_KEXT_BASEMENT systems, kexts are copied to their own * special VM region during OSKext init time, so we can free the whole * segment now. */ ml_static_mfree((vm_offset_t) prelinkData, prelinkLength); #endif /* __x86_64__ */ /* Free the prelink info segment, we're done with it. */ prelinkInfoSegment = getsegbyname(kPrelinkInfoSegment); if (prelinkInfoSegment) { ml_static_mfree((vm_offset_t)prelinkInfoSegment->vmaddr, (vm_size_t)prelinkInfoSegment->vmsize); } finish: OSSafeReleaseNULL(errorString); OSSafeReleaseNULL(parsedXML); OSSafeReleaseNULL(theKernel); OSSafeReleaseNULL(prelinkCountObj); return; }
ExitStatus printKextInfo(KctoolArgs * toolArgs) { ExitStatus result = EX_SOFTWARE; CFArrayRef kextPlistArray = NULL; CFIndex i, count; if (CFArrayGetTypeID() == CFGetTypeID(toolArgs->kernelcacheInfoPlist)) { kextPlistArray = (CFArrayRef)toolArgs->kernelcacheInfoPlist; } else if (CFDictionaryGetTypeID() == CFGetTypeID(toolArgs->kernelcacheInfoPlist)){ kextPlistArray = (CFArrayRef)CFDictionaryGetValue(toolArgs->kernelcacheInfoPlist, CFSTR("_PrelinkInfoDictionary")); } else { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Unrecognized kernelcache plist data."); goto finish; } count = CFArrayGetCount(kextPlistArray); for (i = 0; i < count; i++) { CFDictionaryRef kextInfoDict = (CFDictionaryRef)CFArrayGetValueAtIndex(kextPlistArray, i); CFStringRef thisKextID = CFDictionaryGetValue(kextInfoDict, kCFBundleIdentifierKey); if (thisKextID && CFEqual(thisKextID, toolArgs->kextID)) { uint64_t kextAddr = 0; uint64_t kextSize = 0; u_long kextOffset = 0; const UInt8 * kextMachO = NULL; // do not free void * section = NULL; // do not free if (!getKextAddressAndSize(kextInfoDict, &kextAddr, &kextSize)) { goto finish; } if (ISMACHO64(MAGIC32(toolArgs->kernelcacheImageBytes))) { section = (void *)macho_get_section_by_name_64( (struct mach_header_64 *)toolArgs->kernelcacheImageBytes, kPrelinkTextSegment, kPrelinkTextSection); } else { section = (void *)macho_get_section_by_name( (struct mach_header *)toolArgs->kernelcacheImageBytes, kPrelinkTextSegment, kPrelinkTextSection); } if (!section) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Cannot find %s,%s in kernelcache.", kPrelinkTextSegment, kPrelinkTextSection); goto finish; } if (ISMACHO64(MAGIC32(toolArgs->kernelcacheImageBytes))) { kextOffset = ((struct section_64 *)section)->offset + (u_long)(kextAddr - ((struct section_64 *)section)->addr); } else { kextOffset = ((struct section *)section)->offset + (u_long)(kextAddr - ((struct section *)section)->addr); } kextMachO = toolArgs->kernelcacheImageBytes + kextOffset; /* Find the requested section's file offset and size */ if (ISMACHO64(MAGIC32(toolArgs->kernelcacheImageBytes))) { section = (void *)macho_get_section_by_name_64( (struct mach_header_64 *)kextMachO, toolArgs->segmentName, toolArgs->sectionName); } else { /* macho_get_section_by_name doesn't work as the kexts don't have a __TEXT segment. * They just have a single segment named "" with all the sections dumped under it. */ section = (void *)getSectionByName( kextMachO, toolArgs->segmentName, toolArgs->sectionName); } if (!section) { OSKextLogCFString(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, CFSTR("Cannot find %s,%s in kext %@\n"), toolArgs->segmentName, toolArgs->sectionName, toolArgs->kextID); goto finish; } if (ISMACHO64(MAGIC32(toolArgs->kernelcacheImageBytes))) { printf("%#llx %#lx %#llx\n", ((struct section_64 *)section)->addr, kextOffset + ((struct section_64 *)section)->offset, ((struct section_64 *)section)->size); } else { printf("%#x %#lx %#x\n", ((struct section *)section)->addr, kextOffset + ((struct section *)section)->offset, ((struct section *)section)->size); } result = EX_OK; break; } } finish: return result; }
/********************************************************************* * IMPORTANT: Once we have done the vm_map_copyout(), we *must* return * KERN_SUCCESS or the kernel map gets messed up (reason as yet * unknown). We use op_result to return the real result of our work. *********************************************************************/ kern_return_t kext_request( host_priv_t hostPriv, /* in only */ uint32_t clientLogSpec, /* in only */ vm_offset_t requestIn, /* in only */ mach_msg_type_number_t requestLengthIn, /* out only */ vm_offset_t * responseOut, /* out only */ mach_msg_type_number_t * responseLengthOut, /* out only */ vm_offset_t * logDataOut, /* out only */ mach_msg_type_number_t * logDataLengthOut, /* out only */ kern_return_t * op_result) { kern_return_t result = KERN_FAILURE; vm_map_address_t map_addr = 0; // do not free/deallocate char * request = NULL; // must vm_deallocate mkext2_header * mkextHeader = NULL; // do not release bool isMkext = false; char * response = NULL; // must kmem_free uint32_t responseLength = 0; char * logData = NULL; // must kmem_free uint32_t logDataLength = 0; /* MIG doesn't pass "out" parameters as empty, so clear them immediately * just in case, or MIG will try to copy out bogus data. */ *op_result = KERN_FAILURE; *responseOut = NULL; *responseLengthOut = 0; *logDataOut = NULL; *logDataLengthOut = 0; /* Check for input. Don't discard what isn't there, though. */ if (!requestLengthIn || !requestIn) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogIPCFlag, "Invalid request from user space (no data)."); *op_result = KERN_INVALID_ARGUMENT; goto finish; } /* Once we have done the vm_map_copyout(), we *must* return KERN_SUCCESS * or the kernel map gets messed up (reason as yet unknown). We will use * op_result to return the real result of our work. */ result = vm_map_copyout(kernel_map, &map_addr, (vm_map_copy_t)requestIn); if (result != KERN_SUCCESS) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogIPCFlag, "vm_map_copyout() failed for request from user space."); vm_map_copy_discard((vm_map_copy_t)requestIn); goto finish; } request = CAST_DOWN(char *, map_addr); /* Check if request is an mkext; this is always a load request * and requires root access. If it isn't an mkext, see if it's * an XML request, and check the request to see if that requires * root access. */ if (requestLengthIn > sizeof(mkext2_header)) { mkextHeader = (mkext2_header *)request; if (MKEXT_GET_MAGIC(mkextHeader) == MKEXT_MAGIC && MKEXT_GET_SIGNATURE(mkextHeader) == MKEXT_SIGN) { isMkext = true; } } if (isMkext) { #ifdef SECURE_KERNEL // xxx - something tells me if we have a secure kernel we don't even // xxx - want to log a message here. :-) *op_result = KERN_NOT_SUPPORTED; goto finish; #else // xxx - can we find out if calling task is kextd? // xxx - can we find the name of the calling task? if (hostPriv == HOST_PRIV_NULL) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag, "Attempt by non-root process to load a kext."); *op_result = kOSKextReturnNotPrivileged; goto finish; } *op_result = OSKext::loadFromMkext((OSKextLogSpec)clientLogSpec, request, requestLengthIn, &logData, &logDataLength); #endif /* defined(SECURE_KERNEL) */ } else { /* If the request isn't an mkext, then is should be XML. Parse it * if possible and hand the request over to OSKext. */ *op_result = OSKext::handleRequest(hostPriv, (OSKextLogSpec)clientLogSpec, request, requestLengthIn, &response, &responseLength, &logData, &logDataLength); } if (response && responseLength > 0) { kern_return_t copyin_result; copyin_result = vm_map_copyin(kernel_map, CAST_USER_ADDR_T(response), responseLength, /* src_destroy */ false, (vm_map_copy_t *)responseOut); if (copyin_result == KERN_SUCCESS) { *responseLengthOut = responseLength; } else { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogIPCFlag, "Failed to copy response to request from user space."); *op_result = copyin_result; // xxx - should we map to our own code? *responseOut = NULL; *responseLengthOut = 0; goto finish; } } if (logData && logDataLength > 0) { kern_return_t copyin_result; copyin_result = vm_map_copyin(kernel_map, CAST_USER_ADDR_T(logData), logDataLength, /* src_destroy */ false, (vm_map_copy_t *)logDataOut); if (copyin_result == KERN_SUCCESS) { *logDataLengthOut = logDataLength; } else { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogIPCFlag, "Failed to copy log data for request from user space."); *op_result = copyin_result; // xxx - should we map to our own code? *logDataOut = NULL; *logDataLengthOut = 0; goto finish; } } finish: if (request) { (void)vm_deallocate(kernel_map, (vm_offset_t)request, requestLengthIn); } if (response) { kmem_free(kernel_map, (vm_offset_t)response, responseLength); } if (logData) { kmem_free(kernel_map, (vm_offset_t)logData, logDataLength); } return result; }
void KLDBootstrap::readPrelinkedExtensions( kernel_section_t * prelinkInfoSect) { OSArray * infoDictArray = NULL; // do not release OSArray * personalitiesArray = NULL; // do not release OSObject * parsedXML = NULL; // must release OSDictionary * prelinkInfoDict = NULL; // do not release OSString * errorString = NULL; // must release OSKext * theKernel = NULL; // must release #if CONFIG_KXLD kernel_section_t * kernelLinkStateSection = NULL; // see code #endif kernel_segment_command_t * prelinkLinkStateSegment = NULL; // see code kernel_segment_command_t * prelinkTextSegment = NULL; // see code kernel_segment_command_t * prelinkInfoSegment = NULL; // see code /* We make some copies of data, but if anything fails we're basically * going to fail the boot, so these won't be cleaned up on error. */ void * prelinkData = NULL; // see code void * prelinkCopy = NULL; // see code vm_size_t prelinkLength = 0; #if !__LP64__ && !defined(__arm__) vm_map_offset_t prelinkDataMapOffset = 0; #endif kern_return_t mem_result = KERN_SUCCESS; OSDictionary * infoDict = NULL; // do not release IORegistryEntry * registryRoot = NULL; // do not release OSNumber * prelinkCountObj = NULL; // must release u_int i = 0; OSKextLog(/* kext */ NULL, kOSKextLogProgressLevel | kOSKextLogDirectoryScanFlag | kOSKextLogArchiveFlag, "Starting from prelinked kernel."); /***** * Wrap the kernel link state in-place in an OSData. * This is unnecessary (and the link state may not be present) if the kernel * does not have kxld support because this information is only used for * runtime linking. */ #if CONFIG_KXLD kernelLinkStateSection = getsectbyname(kPrelinkLinkStateSegment, kPrelinkKernelLinkStateSection); if (!kernelLinkStateSection) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag, "Can't find prelinked kernel link state."); goto finish; } theKernel = OSKext::lookupKextWithIdentifier(kOSKextKernelIdentifier); if (!theKernel) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag, "Can't find kernel kext object in prelinked kernel."); goto finish; } prelinkData = (void *) kernelLinkStateSection->addr; prelinkLength = kernelLinkStateSection->size; mem_result = kmem_alloc_pageable(kernel_map, (vm_offset_t *) &prelinkCopy, prelinkLength); if (mem_result != KERN_SUCCESS) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag | kOSKextLogArchiveFlag, "Can't copy prelinked kernel link state."); goto finish; } memcpy(prelinkCopy, prelinkData, prelinkLength); theKernel->linkState = OSData::withBytesNoCopy(prelinkCopy, prelinkLength); if (!theKernel->linkState) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag | kOSKextLogArchiveFlag, "Can't create prelinked kernel link state wrapper."); goto finish; } theKernel->linkState->setDeallocFunction(osdata_kmem_free); #endif prelinkTextSegment = getsegbyname(kPrelinkTextSegment); if (!prelinkTextSegment) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogDirectoryScanFlag | kOSKextLogArchiveFlag, "Can't find prelinked kexts' text segment."); goto finish; } prelinkData = (void *) prelinkTextSegment->vmaddr; prelinkLength = prelinkTextSegment->vmsize; #if !__LP64__ /* To enable paging and write/execute protections on the kext * executables, we need to copy them out of the booter-created * memory, reallocate that space with VM, then prelinkCopy them back in. * This isn't necessary on LP64 because kexts have their own VM * region on that architecture model. */ mem_result = kmem_alloc(kernel_map, (vm_offset_t *)&prelinkCopy, prelinkLength); if (mem_result != KERN_SUCCESS) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag | kOSKextLogArchiveFlag, "Can't copy prelinked kexts' text for VM reassign."); goto finish; } /* Copy it out. */ memcpy(prelinkCopy, prelinkData, prelinkLength); /* Dump the booter memory. */ ml_static_mfree((vm_offset_t)prelinkData, prelinkLength); /* Set up the VM region. */ prelinkDataMapOffset = (vm_map_offset_t)(uintptr_t)prelinkData; mem_result = vm_map_enter_mem_object( kernel_map, &prelinkDataMapOffset, prelinkLength, /* mask */ 0, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, (ipc_port_t)NULL, (vm_object_offset_t) 0, /* copy */ FALSE, /* cur_protection */ VM_PROT_ALL, /* max_protection */ VM_PROT_ALL, /* inheritance */ VM_INHERIT_DEFAULT); if ((mem_result != KERN_SUCCESS) || (prelinkTextSegment->vmaddr != prelinkDataMapOffset)) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag | kOSKextLogArchiveFlag, "Can't create kexts' text VM entry at 0x%llx, length 0x%x (error 0x%x).", (unsigned long long) prelinkDataMapOffset, prelinkLength, mem_result); goto finish; } prelinkData = (void *)(uintptr_t)prelinkDataMapOffset; /* And copy it back. */ memcpy(prelinkData, prelinkCopy, prelinkLength); kmem_free(kernel_map, (vm_offset_t)prelinkCopy, prelinkLength); #endif /* !__LP64__ */ /* Unserialize the info dictionary from the prelink info section. */ parsedXML = OSUnserializeXML((const char *)prelinkInfoSect->addr, &errorString); if (parsedXML) { prelinkInfoDict = OSDynamicCast(OSDictionary, parsedXML); } if (!prelinkInfoDict) { const char * errorCString = "(unknown error)"; if (errorString && errorString->getCStringNoCopy()) { errorCString = errorString->getCStringNoCopy(); } else if (parsedXML) { errorCString = "not a dictionary"; } OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag, "Error unserializing prelink plist: %s.", errorCString); goto finish; } infoDictArray = OSDynamicCast(OSArray, prelinkInfoDict->getObject(kPrelinkInfoDictionaryKey)); if (!infoDictArray) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag, "The prelinked kernel has no kext info dictionaries"); goto finish; } /* Create OSKext objects for each info dictionary. */ for (i = 0; i < infoDictArray->getCount(); ++i) { infoDict = OSDynamicCast(OSDictionary, infoDictArray->getObject(i)); if (!infoDict) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogDirectoryScanFlag | kOSKextLogArchiveFlag, "Can't find info dictionary for prelinked kext #%d.", i); continue; } /* Create the kext for the entry, then release it, because the * kext system keeps them around until explicitly removed. * Any creation/registration failures are already logged for us. */ OSKext * newKext = OSKext::withPrelinkedInfoDict(infoDict); OSSafeReleaseNULL(newKext); } /* Get all of the personalities for kexts that were not prelinked and * add them to the catalogue. */ personalitiesArray = OSDynamicCast(OSArray, prelinkInfoDict->getObject(kPrelinkPersonalitiesKey)); if (!personalitiesArray) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag, "The prelinked kernel has no personalities array"); goto finish; } if (personalitiesArray->getCount()) { OSKext::setPrelinkedPersonalities(personalitiesArray); } /* Store the number of prelinked kexts in the registry so we can tell * when the system has been started from a prelinked kernel. */ registryRoot = IORegistryEntry::getRegistryRoot(); assert(registryRoot); prelinkCountObj = OSNumber::withNumber( (unsigned long long)infoDictArray->getCount(), 8 * sizeof(uint32_t)); assert(prelinkCountObj); if (prelinkCountObj) { registryRoot->setProperty(kOSPrelinkKextCountKey, prelinkCountObj); } OSSafeReleaseNULL(prelinkCountObj); prelinkCountObj = OSNumber::withNumber( (unsigned long long)personalitiesArray->getCount(), 8 * sizeof(uint32_t)); assert(prelinkCountObj); if (prelinkCountObj) { registryRoot->setProperty(kOSPrelinkPersonalityCountKey, prelinkCountObj); } OSKextLog(/* kext */ NULL, kOSKextLogProgressLevel | kOSKextLogGeneralFlag | kOSKextLogKextBookkeepingFlag | kOSKextLogDirectoryScanFlag | kOSKextLogArchiveFlag, "%u prelinked kexts, and %u additional personalities.", infoDictArray->getCount(), personalitiesArray->getCount()); #if __LP64__ /* On LP64 systems, kexts are copied to their own special VM region * during OSKext init time, so we can free the whole segment now. */ ml_static_mfree((vm_offset_t) prelinkData, prelinkLength); #endif /* __LP64__ */ /* Free the link state segment, kexts have copied out what they need. */ prelinkLinkStateSegment = getsegbyname(kPrelinkLinkStateSegment); if (prelinkLinkStateSegment) { ml_static_mfree((vm_offset_t)prelinkLinkStateSegment->vmaddr, (vm_size_t)prelinkLinkStateSegment->vmsize); } /* Free the prelink info segment, we're done with it. */ prelinkInfoSegment = getsegbyname(kPrelinkInfoSegment); if (prelinkInfoSegment) { ml_static_mfree((vm_offset_t)prelinkInfoSegment->vmaddr, (vm_size_t)prelinkInfoSegment->vmsize); } finish: OSSafeRelease(errorString); OSSafeRelease(parsedXML); OSSafeRelease(theKernel); OSSafeRelease(prelinkCountObj); return; }
/******************************************************************************* * Fork a process after a specified delay, and either wait on it to exit or * leave it to run in the background. * * Returns -2 on spawn() failure, -1 on other failure, and depending on wait: * wait: true - exit status of forked program * wait: false - pid of background process *******************************************************************************/ int fork_program(const char * argv0, char * const argv[], Boolean wait) { int result; int spawn_result; pid_t child_pid; int child_status; int normal_iopolicy = getiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS); char ** environ = *(_NSGetEnviron()); if (!wait) { setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE); } spawn_result = posix_spawn(&child_pid, argv0, /* file_actions */ NULL, /* spawnattrs */ NULL, argv, environ); // If we couldn't spawn the process, return -2 with errno for detail if (spawn_result != 0) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel, "posix_spawn failed for %s.", argv0); errno = spawn_result; result = -2; goto finish; } OSKextLog(/* kext */ NULL, kOSKextLogDetailLevel, "started child process %s[%d] (%ssynchronous).", argv0, child_pid, wait ? "" : "a"); if (wait) { OSKextLogSpec logSpec = kOSKextLogDetailLevel; if (waitpid(child_pid, &child_status, 0) == -1) { result = -1; goto finish; } if (WIFEXITED(child_status)) { result = WEXITSTATUS(child_status); if (result) { logSpec = kOSKextLogErrorLevel; } OSKextLog(/* kext */ NULL, logSpec, "Child process %s[%d] exited with status %d.", argv0, child_pid, result); } else if (WIFSIGNALED(child_status)) { result = WTERMSIG(child_status); logSpec = kOSKextLogErrorLevel; OSKextLog(/* kext */ NULL, logSpec, "Child process %s[%d] exited due to signal %d.", argv0, child_pid, result); } else { // shouldn't be any other types of exit result = -1; } } else { result = child_pid; } finish: setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, normal_iopolicy); return result; }
OSReturn OSMetaClass::postModLoad(void * loadHandle) { OSReturn result = kOSReturnSuccess; OSSymbol * myKextName = 0; // must release OSKext * myKext = 0; // must release if (!sStalled || loadHandle != sStalled) { result = kOSMetaClassInternal; goto finish; } if (sStalled->result) { result = sStalled->result; } else switch (sBootstrapState) { case kNoDictionaries: sBootstrapState = kMakingDictionaries; // No break; fall through case kMakingDictionaries: sAllClassesDict = OSDictionary::withCapacity(kClassCapacityIncrement); if (!sAllClassesDict) { result = kOSMetaClassNoDicts; break; } // No break; fall through case kCompletedBootstrap: { unsigned int i; myKextName = const_cast<OSSymbol *>(OSSymbol::withCStringNoCopy( sStalled->kextIdentifier)); if (!sStalled->count) { break; // Nothing to do so just get out } myKext = OSKext::lookupKextWithIdentifier(myKextName); if (!myKext) { result = kOSMetaClassNoKext; /* Log this error here so we can include the kext name. */ OSKextLog(/* kext */ NULL, kOSMetaClassLogSpec, "OSMetaClass: Can't record classes for kext %s - kext not found.", sStalled->kextIdentifier); break; } /* First pass checking classes aren't already loaded. If any already * exist, we don't register any, and so we don't technically have * to do any C++ teardown. * * Hack alert: me->className has been a C string until now. * We only release the OSSymbol if we store the kext. */ IOLockLock(sAllClassesLock); for (i = 0; i < sStalled->count; i++) { OSMetaClass * me = sStalled->classes[i]; OSMetaClass * orig = OSDynamicCast(OSMetaClass, sAllClassesDict->getObject((const char *)me->className)); if (orig) { /* Log this error here so we can include the class name. * xxx - we should look up the other kext that defines the class */ OSKextLog(myKext, kOSMetaClassLogSpec, "OSMetaClass: Kext %s class %s is a duplicate;" "kext %s already has a class by that name.", sStalled->kextIdentifier, (const char *)me->className, ((OSKext *)orig->reserved)->getIdentifierCString()); result = kOSMetaClassDuplicateClass; break; } } IOLockUnlock(sAllClassesLock); /* Bail if we didn't go through the entire list of new classes * (if we hit a duplicate). */ if (i != sStalled->count) { break; } // Second pass symbolling strings and inserting classes in dictionary IOLockLock(sAllClassesLock); for (i = 0; i < sStalled->count; i++) { OSMetaClass * me = sStalled->classes[i]; /* Hack alert: me->className has been a C string until now. * We only release the OSSymbol in ~OSMetaClass() * if we set the reference to the kext. */ me->className = OSSymbol::withCStringNoCopy((const char *)me->className); // xxx - I suppose if these fail we're going to panic soon.... sAllClassesDict->setObject(me->className, me); /* Do not retain the kext object here. */ me->reserved = (ExpansionData *)myKext; if (myKext) { result = myKext->addClass(me, sStalled->count); if (result != kOSReturnSuccess) { /* OSKext::addClass() logs with kOSMetaClassNoInsKModSet. */ break; } } } IOLockUnlock(sAllClassesLock); sBootstrapState = kCompletedBootstrap; break; } default: result = kOSMetaClassInternal; break; } finish: /* Don't call logError() for success or the conditions logged above * or by called function. */ if (result != kOSReturnSuccess && result != kOSMetaClassNoInsKModSet && result != kOSMetaClassDuplicateClass && result != kOSMetaClassNoKext) { OSMetaClassLogErrorForKext(result, myKext); } OSSafeRelease(myKextName); OSSafeRelease(myKext); if (sStalled) { ACCUMSIZE(-(sStalled->capacity * sizeof(OSMetaClass *) + sizeof(*sStalled))); kfree(sStalled->classes, sStalled->capacity * sizeof(OSMetaClass *)); kfree(sStalled, sizeof(*sStalled)); sStalled = 0; } IOLockUnlock(sStalledClassesLock); return result; }
ExitStatus readArgs( int * argc, char * const ** argv, KctoolArgs * toolArgs) { ExitStatus result = EX_USAGE; ExitStatus scratchResult = EX_USAGE; int optchar = 0; int longindex = -1; bzero(toolArgs, sizeof(*toolArgs)); /***** * Process command line arguments. */ while ((optchar = getopt_long_only(*argc, *argv, kOptChars, sOptInfo, &longindex)) != -1) { switch (optchar) { case kOptArch: toolArgs->archInfo = NXGetArchInfoFromName(optarg); if (!toolArgs->archInfo) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Unknown architecture %s.", optarg); goto finish; } break; case kOptHelp: usage(kUsageLevelFull); result = kKctoolExitHelp; goto finish; default: OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "unrecognized option %s", (*argv)[optind-1]); goto finish; break; } /* Reset longindex, because getopt_long_only() is stupid and doesn't. */ longindex = -1; } /* Update the argc & argv seen by main() so that boot<>root calls * handle remaining args. */ *argc -= optind; *argv += optind; if (*argc != 4) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "incorrect number of arguments"); goto finish; } /***** * Record remaining args from the command line. */ toolArgs->kernelcachePath = (*argv)[0]; toolArgs->kextID = CFStringCreateWithCString(kCFAllocatorDefault, (*argv)[1], kCFStringEncodingUTF8); if (!toolArgs->kextID) { OSKextLogMemError(); result = EX_OSERR; goto finish; } toolArgs->segmentName = (*argv)[2]; toolArgs->sectionName = (*argv)[3]; result = EX_OK; finish: if (result == EX_USAGE) { usage(kUsageLevelBrief); } return result; }
CFDataRef createMkext1ForArch(const NXArchInfo * arch, CFArrayRef archiveKexts, boolean_t compress) { CFMutableDataRef result = NULL; CFMutableDictionaryRef kextsByIdentifier = NULL; Mkext1Context context; mkext1_header * mkextHeader = NULL; // do not free const uint8_t * adler_point = 0; CFIndex count, i; result = CFDataCreateMutable(kCFAllocatorDefault, /* capaacity */ 0); if (!result || !createCFMutableDictionary(&kextsByIdentifier)) { OSKextLogMemError(); goto finish; } /* mkext1 can only contain 1 kext for a given bundle identifier, so we * have to pick out the most recent versions. */ count = CFArrayGetCount(archiveKexts); for (i = 0; i < count; i++) { OSKextRef theKext = (OSKextRef)CFArrayGetValueAtIndex(archiveKexts, i); CFStringRef bundleIdentifier = OSKextGetIdentifier(theKext); OSKextRef savedKext = (OSKextRef)CFDictionaryGetValue(kextsByIdentifier, bundleIdentifier); OSKextVersion thisVersion, savedVersion; if (!OSKextSupportsArchitecture(theKext, arch)) { continue; } if (!savedKext) { CFDictionarySetValue(kextsByIdentifier, bundleIdentifier, theKext); continue; } thisVersion = OSKextGetVersion(theKext); savedVersion = OSKextGetVersion(savedKext); if (thisVersion > savedVersion) { CFDictionarySetValue(kextsByIdentifier, bundleIdentifier, theKext); } } /* Add room for the mkext header and kext descriptors. */ CFDataSetLength(result, sizeof(mkext1_header) + CFDictionaryGetCount(kextsByIdentifier) * sizeof(mkext_kext)); context.mkext = result; context.kextIndex = 0; context.compressOffset = (uint32_t)CFDataGetLength(result); context.arch = arch; context.fatal = false; context.compress = compress; CFDictionaryApplyFunction(kextsByIdentifier, addToMkext1, &context); if (context.fatal) { SAFE_RELEASE_NULL(result); goto finish; } mkextHeader = (mkext1_header *)CFDataGetBytePtr(result); mkextHeader->magic = OSSwapHostToBigInt32(MKEXT_MAGIC); mkextHeader->signature = OSSwapHostToBigInt32(MKEXT_SIGN); mkextHeader->version = OSSwapHostToBigInt32(0x01008000); // 'vers' 1.0.0 mkextHeader->numkexts = OSSwapHostToBigInt32((__uint32_t)CFDictionaryGetCount(kextsByIdentifier)); mkextHeader->cputype = OSSwapHostToBigInt32(arch->cputype); mkextHeader->cpusubtype = OSSwapHostToBigInt32(arch->cpusubtype); mkextHeader->length = OSSwapHostToBigInt32((__uint32_t)CFDataGetLength(result)); adler_point = (UInt8 *)&mkextHeader->version; mkextHeader->adler32 = OSSwapHostToBigInt32(local_adler32( (UInt8 *)&mkextHeader->version, (int)(CFDataGetLength(result) - (adler_point - (uint8_t *)mkextHeader)))); OSKextLog(/* kext */ NULL, kOSKextLogProgressLevel | kOSKextLogArchiveFlag, "Created mkext for %s containing %lu kexts.", arch->name, CFDictionaryGetCount(kextsByIdentifier)); finish: SAFE_RELEASE(kextsByIdentifier); return result; }
int main(int argc, char * const argv[]) { ExitStatus result = EX_SOFTWARE; KctoolArgs toolArgs; int kernelcache_fd = -1; // must close() void * fat_header = NULL; // must unmapFatHeaderPage() struct fat_arch * fat_arch = NULL; CFDataRef rawKernelcache = NULL; // must release CFDataRef kernelcacheImage = NULL; // must release void * prelinkInfoSect = NULL; const char * prelinkInfoBytes = NULL; CFPropertyListRef prelinkInfoPlist = NULL; // must release bzero(&toolArgs, sizeof(toolArgs)); /***** * Find out what the program was invoked as. */ progname = rindex(argv[0], '/'); if (progname) { progname++; // go past the '/' } else { progname = (char *)argv[0]; } /* Set the OSKext log callback right away. */ OSKextSetLogOutputFunction(&tool_log); /***** * Process args & check for permission to load. */ result = readArgs(&argc, &argv, &toolArgs); if (result != EX_OK) { if (result == kKctoolExitHelp) { result = EX_OK; } goto finish; } kernelcache_fd = open(toolArgs.kernelcachePath, O_RDONLY); if (kernelcache_fd == -1) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Can't open %s: %s.", toolArgs.kernelcachePath, strerror(errno)); result = EX_OSERR; goto finish; } fat_header = mapAndSwapFatHeaderPage(kernelcache_fd); if (!fat_header) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Can't map %s: %s.", toolArgs.kernelcachePath, strerror(errno)); result = EX_OSERR; goto finish; } fat_arch = getFirstFatArch(fat_header); if (fat_arch && !toolArgs.archInfo) { toolArgs.archInfo = NXGetArchInfoFromCpuType(fat_arch->cputype, fat_arch->cpusubtype); } rawKernelcache = readMachOSliceForArch(toolArgs.kernelcachePath, toolArgs.archInfo, /* checkArch */ FALSE); if (!rawKernelcache) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Can't read arch %s from %s.", toolArgs.archInfo->name, toolArgs.kernelcachePath); goto finish; } if (MAGIC32(CFDataGetBytePtr(rawKernelcache)) == OSSwapHostToBigInt32('comp')) { kernelcacheImage = uncompressPrelinkedSlice(rawKernelcache); if (!kernelcacheImage) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Can't uncompress kernelcache slice."); goto finish; } } else { kernelcacheImage = CFRetain(rawKernelcache); } toolArgs.kernelcacheImageBytes = CFDataGetBytePtr(kernelcacheImage); if (ISMACHO64(MAGIC32(toolArgs.kernelcacheImageBytes))) { prelinkInfoSect = (void *)macho_get_section_by_name_64( (struct mach_header_64 *)toolArgs.kernelcacheImageBytes, "__PRELINK_INFO", "__info"); } else { prelinkInfoSect = (void *)macho_get_section_by_name( (struct mach_header *)toolArgs.kernelcacheImageBytes, "__PRELINK_INFO", "__info"); } if (!prelinkInfoSect) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Can't find prelink info section."); goto finish; } if (ISMACHO64(MAGIC32(toolArgs.kernelcacheImageBytes))) { prelinkInfoBytes = ((char *)toolArgs.kernelcacheImageBytes) + ((struct section_64 *)prelinkInfoSect)->offset; } else { prelinkInfoBytes = ((char *)toolArgs.kernelcacheImageBytes) + ((struct section *)prelinkInfoSect)->offset; } toolArgs.kernelcacheInfoPlist = (CFPropertyListRef)IOCFUnserialize(prelinkInfoBytes, kCFAllocatorDefault, /* options */ 0, /* errorString */ NULL); if (!toolArgs.kernelcacheInfoPlist) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Can't unserialize prelink info."); goto finish; } result = printKextInfo(&toolArgs); if (result != EX_OK) { goto finish; } result = EX_OK; finish: SAFE_RELEASE(toolArgs.kernelcacheInfoPlist); SAFE_RELEASE(kernelcacheImage); SAFE_RELEASE(rawKernelcache); if (fat_header) { unmapFatHeaderPage(fat_header); } if (kernelcache_fd != -1) { close(kernelcache_fd); } return result; }