#include "kextutil_main.h"
#include "kext_tools_util.h"
#include "security.h"
#include "bootcaches.h"
#include <libc.h>
#include <sysexits.h>
#include <IOKit/kext/OSKext.h>
#include <IOKit/kext/OSKextPrivate.h>
#include <IOKit/kext/KextManager.h>
#include <IOKit/kext/KextManagerPriv.h>
#include <IOKit/kext/kextmanager_types.h>
#include <servers/bootstrap.h> // bootstrap mach ports
#include <bootfiles.h>
#pragma mark Constants
#define BAD_ADDRESS_SPEC "Address format is <bundle-id@address>, " \
"with nonzero hexadecimal address."
#pragma mark Global/Static Variables
const char * progname = "(unknown)";
#define LOCK_MAXTRIES 90
#define LOCK_DELAY 1
static mach_port_t sKextdPort = MACH_PORT_NULL;
static mach_port_t sLockPort = MACH_PORT_NULL; static int sLockStatus = 0;
static bool sLockTaken = false;
#pragma mark Main Routine
ExitStatus
main(int argc, char * const * argv)
{
ExitStatus result = EX_SOFTWARE;
ExitStatus processResult = EX_SOFTWARE;
Boolean fatal = false;
CFArrayRef allKexts = NULL; CFArrayRef kextsToProcess = NULL; KextutilArgs toolArgs;
progname = rindex(argv[0], '/');
if (progname) {
progname++; } else {
progname = (char *)argv[0];
}
OSKextSetLogOutputFunction(&tool_log);
OSKextSetLogFilter(kOSKextLogWarningLevel | kOSKextLogVerboseFlagsMask,
true);
result = readArgs(argc, argv, &toolArgs);
if (result != EX_OK) {
if (result == kKextutilExitHelp) {
result = EX_OK;
}
goto finish;
}
result = checkArgs(&toolArgs);
if (result != EX_OK) {
goto finish;
}
result = EX_OK;
OSKextSetRecordsDiagnostics(kOSKextDiagnosticsFlagAll);
allKexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault, toolArgs.scanURLs);
if (!allKexts || !CFArrayGetCount(allKexts)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"No kernel extensions found.");
result = EX_SOFTWARE;
goto finish;
}
if (result != EX_OK) {
goto finish;
}
result = createKextsToProcess(&toolArgs, &kextsToProcess, &fatal);
if (fatal) {
goto finish;
}
if (!serializeLoad(&toolArgs, toolArgs.doLoad)) {
result = EX_OSERR;
goto finish;
}
processResult = processKexts(kextsToProcess, &toolArgs);
if (result == EX_OK) {
result = processResult;
}
finish:
if (sLockTaken) {
kextmanager_unlock_kextload(sKextdPort, sLockPort);
}
if (sKextdPort != MACH_PORT_NULL) {
mach_port_deallocate(mach_task_self(), sKextdPort);
}
if (sLockPort != MACH_PORT_NULL) {
mach_port_mod_refs(mach_task_self(), sLockPort, MACH_PORT_RIGHT_RECEIVE, -1);
}
exit(result);
SAFE_RELEASE(allKexts);
SAFE_RELEASE(toolArgs.loadAddresses);
SAFE_RELEASE(toolArgs.kextIDs);
SAFE_RELEASE(toolArgs.personalityNames);
SAFE_RELEASE(toolArgs.dependencyURLs);
SAFE_RELEASE(toolArgs.repositoryURLs);
SAFE_RELEASE(toolArgs.kextURLs);
SAFE_RELEASE(toolArgs.scanURLs);
SAFE_RELEASE(toolArgs.kernelURL);
SAFE_RELEASE(toolArgs.kernelFile);
SAFE_RELEASE(toolArgs.symbolDirURL);
return result;
}
#pragma mark Major Subroutines
ExitStatus
readArgs(
int argc,
char * const * argv,
KextutilArgs * toolArgs)
{
ExitStatus result = EX_USAGE;
ExitStatus scratchResult = EX_USAGE;
int optchar;
int longindex;
CFStringRef scratchString = NULL; CFNumberRef scratchNumber = NULL; CFNumberRef existingAddress = NULL; CFURLRef scratchURL = NULL; uint32_t i;
bzero(toolArgs, sizeof(*toolArgs));
toolArgs->useRepositoryCaches = true;
toolArgs->useSystemExtensions = true;
toolArgs->overwriteSymbols = true;
toolArgs->interactiveLevel = kOSKextExcludeNone;
toolArgs->doLoad = true;
toolArgs->doStartMatching = true;
if (!createCFMutableDictionary(&toolArgs->loadAddresses) ||
!createCFMutableArray(&toolArgs->kextIDs, &kCFTypeArrayCallBacks) ||
!createCFMutableArray(&toolArgs->personalityNames, &kCFTypeArrayCallBacks) ||
!createCFMutableArray(&toolArgs->dependencyURLs, &kCFTypeArrayCallBacks) ||
!createCFMutableArray(&toolArgs->repositoryURLs, &kCFTypeArrayCallBacks) ||
!createCFMutableArray(&toolArgs->kextURLs, &kCFTypeArrayCallBacks) ||
!createCFMutableArray(&toolArgs->scanURLs, &kCFTypeArrayCallBacks)) {
result = EX_OSERR;
OSKextLogMemError();
exit(result);
}
while ((optchar = getopt_long_only(argc, (char * const *)argv,
kOptChars, sOptInfo, &longindex)) != -1) {
char * address_string = NULL; uint64_t address;
SAFE_RELEASE_NULL(scratchString);
SAFE_RELEASE_NULL(scratchNumber);
SAFE_RELEASE_NULL(scratchURL);
switch (optchar) {
case kOptHelp:
usage(kUsageLevelFull);
result = kKextutilExitHelp;
goto finish;
break;
case kOptBundleIdentifier:
scratchString = CFStringCreateWithCString(kCFAllocatorDefault,
optarg, kCFStringEncodingUTF8);
if (!scratchString) {
OSKextLogMemError();
result = EX_OSERR;
goto finish;
}
CFArrayAppendValue(toolArgs->kextIDs, scratchString);
break;
case kOptPersonality:
scratchString = CFStringCreateWithCString(kCFAllocatorDefault,
optarg, kCFStringEncodingUTF8);
if (!scratchString) {
OSKextLogMemError();
result = EX_OSERR;
goto finish;
}
CFArrayAppendValue(toolArgs->personalityNames, scratchString);
break;
case kOptKernel:
if (toolArgs->kernelURL) {
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
"Warning: multiple use of -%s (-%c); using last.",
kOptNameKernel, kOptKernel);
SAFE_RELEASE_NULL(toolArgs->kernelURL);
}
scratchURL = CFURLCreateFromFileSystemRepresentation(
kCFAllocatorDefault,
(const UInt8 *)optarg, strlen(optarg), true);
if (!scratchURL) {
OSKextLogStringError( NULL);
result = EX_OSERR;
goto finish;
}
toolArgs->kernelURL = CFRetain(scratchURL);
break;
case kOptDependency:
scratchURL = CFURLCreateFromFileSystemRepresentation(
kCFAllocatorDefault,
(const UInt8 *)optarg, strlen(optarg), true);
if (!scratchURL) {
OSKextLogStringError( NULL);
result = EX_OSERR;
goto finish;
}
CFArrayAppendValue(toolArgs->dependencyURLs, scratchURL);
break;
case kOptRepository:
scratchResult = checkPath(optarg, NULL,
TRUE, FALSE);
if (scratchResult != EX_OK) {
result = scratchResult;
goto finish;
}
scratchURL = CFURLCreateFromFileSystemRepresentation(
kCFAllocatorDefault,
(const UInt8 *)optarg, strlen(optarg), true);
if (!scratchURL) {
OSKextLogStringError( NULL);
result = EX_OSERR;
goto finish;
}
CFArrayAppendValue(toolArgs->repositoryURLs, scratchURL);
break;
case kOptNoCaches:
toolArgs->useRepositoryCaches = false;
break;
case kOptNoLoadedCheck:
toolArgs->checkLoadedForDependencies = false;
break;
case kOptNoSystemExtensions:
toolArgs->useSystemExtensions = false;
break;
case kOptInteractive:
if (toolArgs->interactiveLevel) {
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
"Warning: multiple use of -%s (-%c) or -%s (-%c); using last.",
kOptNameInteractive, kOptInteractive,
kOptNameInteractiveAll, kOptInteractiveAll);
}
toolArgs->overwriteSymbols = false;
toolArgs->interactiveLevel = kOSKextExcludeKext;
break;
case kOptInteractiveAll:
if (toolArgs->interactiveLevel) {
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
"Warning: multiple use of -%s (-%c) or -%s (-%c); using last.",
kOptNameInteractive, kOptInteractive,
kOptNameInteractiveAll, kOptInteractiveAll);
}
toolArgs->overwriteSymbols = false;
toolArgs->interactiveLevel = kOSKextExcludeAll;
break;
case kOptLoadOnly:
toolArgs->flag_l = 1;
break;
case kOptMatchOnly:
toolArgs->flag_m = 1;
break;
case kOptNoLoad:
toolArgs->flag_n = 1;
break;
case kOptSymbolsDirectory:
if (toolArgs->symbolDirURL) {
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
"Warning: multiple use of -%s (-%c); using last",
kOptNameSymbolsDirectory, kOptSymbolsDirectory);
SAFE_RELEASE_NULL(toolArgs->symbolDirURL);
}
scratchResult = checkPath(optarg, NULL,
TRUE, TRUE);
if (scratchResult != EX_OK) {
result = scratchResult;
goto finish;
}
scratchURL = CFURLCreateFromFileSystemRepresentation(
kCFAllocatorDefault,
(const UInt8 *)optarg, strlen(optarg), true);
if (!scratchURL) {
OSKextLogStringError( NULL);
result = EX_OSERR;
goto finish;
}
toolArgs->symbolDirURL = CFRetain(scratchURL);
break;
case kOptAddress:
toolArgs->flag_n = 1;
address_string = index(optarg, '@');
if (!address_string) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
BAD_ADDRESS_SPEC);
goto finish;
}
address_string[0] = '\0';
address_string++;
address = strtoull(address_string, NULL, 16);
if (!address) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
BAD_ADDRESS_SPEC);
goto finish;
}
scratchNumber = CFNumberCreate(kCFAllocatorDefault,
kCFNumberSInt64Type, &address);
if (!scratchNumber) {
OSKextLogMemError();
result = EX_OSERR;
goto finish;
}
scratchString = CFStringCreateWithCString(kCFAllocatorDefault,
optarg, kCFStringEncodingUTF8);
if (!scratchString) {
OSKextLogMemError();
result = EX_OSERR;
goto finish;
}
existingAddress = CFDictionaryGetValue(toolArgs->loadAddresses,
scratchString);
if (existingAddress && !CFEqual(scratchNumber, existingAddress)) {
OSKextLog( NULL, kOSKextLogWarningLevel,
"Warning: multiple addresses specified for %s; using last.",
optarg);
}
CFDictionarySetValue(toolArgs->loadAddresses, scratchString,
scratchNumber);
break;
case kOptUseKernelAddresses:
toolArgs->flag_n = 1; toolArgs->getAddressesFromKernel = true;
break;
case kOptQuiet:
beQuiet();
toolArgs->logFilterChanged = true;
break;
case kOptVerbose:
scratchResult = setLogFilterForOpt(argc, argv, 0);
if (scratchResult != EX_OK) {
result = scratchResult;
goto finish;
}
toolArgs->logFilterChanged = true;
break;
case kOptTests:
toolArgs->printDiagnostics = true;
break;
case kOptSafeBoot:
toolArgs->safeBootMode = true;
toolArgs->useRepositoryCaches = false; break;
case kOptNoAuthentication:
toolArgs->skipAuthentication = true;
break;
case kOptNoResolveDependencies:
toolArgs->skipDependencies = true;
break;
case 0:
switch (longopt) {
case kLongOptArch:
if (toolArgs->archInfo) {
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
"Warning: multiple use of -%s; using last.",
kOptNameArch);
}
toolArgs->archInfo = NXGetArchInfoFromName(optarg);
if (!toolArgs->archInfo) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Unknown architecture %s.", optarg);
goto finish;
}
break;
default:
goto finish;
break;
}
break;
default:
goto finish;
break;
}
}
for (i = optind; i < argc; i++) {
SAFE_RELEASE_NULL(scratchURL);
scratchResult = checkPath(argv[i], kOSKextBundleExtension,
TRUE, FALSE);
if (scratchResult != EX_OK) {
result = scratchResult;
goto finish;
}
scratchURL = CFURLCreateFromFileSystemRepresentation(
kCFAllocatorDefault,
(const UInt8 *)argv[i], strlen(argv[i]), true);
if (!scratchURL) {
result = EX_OSERR;
OSKextLogMemError();
goto finish;
}
CFArrayAppendValue(toolArgs->kextURLs, scratchURL);
}
result = EX_OK;
finish:
SAFE_RELEASE(scratchString);
SAFE_RELEASE(scratchNumber);
SAFE_RELEASE(scratchURL);
if (result == EX_USAGE) {
usage(kUsageLevelBrief);
}
return result;
}
ExitStatus
checkArgs(KextutilArgs * toolArgs)
{
ExitStatus result = EX_USAGE;
char kernelPathCString[PATH_MAX];
const NXArchInfo * kernelArchInfo = OSKextGetRunningKernelArchitecture();
if (!kernelArchInfo) {
result = EX_OSERR;
goto finish;
}
if (toolArgs->flag_l + toolArgs->flag_m + toolArgs->flag_n > 1) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Only one of -%s (-%c), -%s (-%c), or -%s (-%c) is allowed;\n"
"-%s (-%c) and -%s (-%c) imply -%s (-%c).",
kOptNameLoadOnly, kOptLoadOnly,
kOptNameMatchOnly, kOptMatchOnly,
kOptNameNoLoad, kOptNoLoad,
kOptNameAddress, kOptAddress,
kOptNameUseKernelAddresses, kOptUseKernelAddresses,
kOptNameNoLoad, kOptNoLoad);
goto finish;
} else if (toolArgs->flag_l) {
toolArgs->doLoad = true;
toolArgs->doStartMatching = false;
} else if (toolArgs->flag_m) {
toolArgs->doLoad = false;
toolArgs->doStartMatching = true;
} else if (toolArgs->flag_n) {
toolArgs->doLoad = false;
toolArgs->doStartMatching = false;
}
if ((toolArgs->interactiveLevel != kOSKextExcludeNone) &&
!toolArgs->doLoad && !toolArgs->doStartMatching) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Use interactive modes only when loading or matching.");
goto finish;
}
if (OSKextGetLogFilter( false) == kOSKextLogSilentFilter) {
Boolean interactive = (toolArgs->interactiveLevel != kOSKextExcludeNone);
Boolean needAddresses = (toolArgs->symbolDirURL &&
!toolArgs->doLoad &&
CFDictionaryGetCount(toolArgs->loadAddresses) == 0 &&
!toolArgs->getAddressesFromKernel);
if (interactive || needAddresses) {
result = kKextutilExitLoadFailed;
goto finish;
}
}
if (CFDictionaryGetCount(toolArgs->loadAddresses) > 0 &&
(toolArgs->doLoad || toolArgs->getAddressesFromKernel)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Don't use -%s (-%c) when loading, or with -%s (-%c).",
kOptNameAddress, kOptAddress,
kOptNameUseKernelAddresses, kOptUseKernelAddresses);
goto finish;
}
if (toolArgs->doLoad || toolArgs->doStartMatching) {
if (toolArgs->kernelURL) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"-%s (-%c) is allowed only when not loading "
"or sending personalities.",
kOptNameKernel, kOptKernel);
goto finish;
}
if (toolArgs->skipAuthentication) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"-%s (-%c) is allowed only when not loading "
"or sending personalities.",
kOptNameNoAuthentication, kOptNoAuthentication);
goto finish;
}
}
if (!toolArgs->doLoad &&
!toolArgs->doStartMatching &&
!toolArgs->printDiagnostics) {
toolArgs-> skipAuthentication = true;
}
if (toolArgs->getAddressesFromKernel) {
toolArgs->checkLoadedForDependencies = true;
}
if (toolArgs->skipDependencies) {
if (toolArgs->doLoad ||
toolArgs->symbolDirURL) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Use -%s (-%c) only with -%s (-%c) or %s (-%c), "
"and not with -%s (-%c).",
kOptNameNoResolveDependencies, kOptNoResolveDependencies,
kOptNameNoLoad, kOptNoLoad,
kOptNameMatchOnly, kOptMatchOnly,
kOptNameSymbolsDirectory, kOptSymbolsDirectory);
goto finish;
}
}
if (!CFArrayGetCount(toolArgs->kextURLs) &&
!CFArrayGetCount(toolArgs->kextIDs) &&
!CFDictionaryGetCount(toolArgs->loadAddresses)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"No kernel extensions specified; name kernel extension bundles\n"
" following options, or use -%s (-%c) and -%s (-%c).",
kOptNameBundleIdentifier, kOptBundleIdentifier,
kOptNameAddress, kOptAddress);
goto finish;
}
if ((toolArgs->doLoad || toolArgs->doStartMatching) && geteuid() != 0) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"You must be running as root to load kexts "
"or send personalities into the kernel.");
result = EX_NOPERM;
goto finish;
}
if (toolArgs->archInfo) {
if (toolArgs->doLoad || toolArgs->getAddressesFromKernel) {
if (kernelArchInfo != OSKextGetArchitecture()) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Specified architecture %s does not match "
"running kernel architecture %s.",
toolArgs->archInfo->name, kernelArchInfo->name);
goto finish;
}
}
OSKextSetArchitecture(toolArgs->archInfo);
OSKextSetSimulatedSafeBoot(toolArgs->safeBootMode);
}
if (toolArgs->symbolDirURL && !toolArgs->archInfo &&
!toolArgs->getAddressesFromKernel) {
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogLinkFlag,
"Notice: Using running kernel architecture %s to generate symbols.",
kernelArchInfo->name);
}
if (OSKextGetActualSafeBoot() && !toolArgs->safeBootMode) {
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogLoadFlag,
"Notice: system is in safe boot mode; kernel may refuse loads.");
}
if (toolArgs->useSystemExtensions) {
CFArrayRef sysExtFolders = OSKextGetSystemExtensionsFolderURLs();
CFArrayAppendArray(toolArgs->scanURLs,
sysExtFolders, RANGE_ALL(sysExtFolders));
}
CFArrayAppendArray(toolArgs->scanURLs, toolArgs->kextURLs,
RANGE_ALL(toolArgs->kextURLs));
CFArrayAppendArray(toolArgs->scanURLs, toolArgs->repositoryURLs,
RANGE_ALL(toolArgs->repositoryURLs));
CFArrayAppendArray(toolArgs->scanURLs, toolArgs->dependencyURLs,
RANGE_ALL(toolArgs->dependencyURLs));
if ( (CFArrayGetCount(toolArgs->kextIDs) ||
CFDictionaryGetCount(toolArgs->loadAddresses)) &&
!CFArrayGetCount(toolArgs->scanURLs)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"No kexts to search for bundle IDs; "
"-%s (-%c) requires kexts or repository directories "
"be named by path.",
kOptNameBundleIdentifier, kOptBundleIdentifier);
goto finish;
}
if (!toolArgs->kernelURL) {
CFURLRef scratchURL = NULL;
if (getKernelPathForURL(NULL,
kernelPathCString,
sizeof(kernelPathCString)) == FALSE) {
strlcpy(kernelPathCString, "/System/Library/Kernels/kernel",
sizeof(kernelPathCString));
}
if (useDevelopmentKernel(kernelPathCString)) {
if (strlen(kernelPathCString) + strlen(kDefaultKernelSuffix) + 1 < sizeof(kernelPathCString)) {
strlcat(kernelPathCString,
kDefaultKernelSuffix,
sizeof(kernelPathCString));
}
}
scratchURL = CFURLCreateFromFileSystemRepresentation(
kCFAllocatorDefault,
(const UInt8 *)kernelPathCString,
strlen(kernelPathCString),
false );
if (!scratchURL) {
OSKextLogStringError( NULL);
result = EX_OSERR;
goto finish;
}
toolArgs->kernelURL = scratchURL;
OSKextLog( NULL,
kOSKextLogBasicLevel | kOSKextLogLoadFlag,
"Defaulting to kernel file '%s'",
kernelPathCString);
}
if (toolArgs->kernelURL) {
if (!CFURLGetFileSystemRepresentation(toolArgs->kernelURL,
true,
(uint8_t *)kernelPathCString,
sizeof(kernelPathCString))) {
OSKextLogStringError( NULL);
result = EX_OSFILE;
goto finish;
}
if (!createCFDataFromFile(&toolArgs->kernelFile,
kernelPathCString)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Can't read kernel file '%s'",
kernelPathCString);
result = EX_OSFILE;
goto finish;
}
}
if (!toolArgs->doLoad || (toolArgs->interactiveLevel != kOSKextExcludeNone)) {
adjustLogFilterForInteractive(toolArgs);
}
result = EX_OK;
finish:
if (result == EX_USAGE) {
usage(kUsageLevelBrief);
}
return result;
}
void adjustLogFilterForInteractive(KextutilArgs * toolArgs)
{
if (!toolArgs->logFilterChanged) {
OSKextSetLogFilter(kDefaultServiceLogFilter, false);
OSKextSetLogFilter(kDefaultServiceLogFilter, true);
}
}
ExitStatus
createKextsToProcess(
KextutilArgs * toolArgs,
CFArrayRef * outArray,
Boolean * fatal)
{
ExitStatus result = EX_OK;
CFMutableArrayRef kextsToProcess = NULL;
CFURLRef kextURL = NULL; char kextPathCString[PATH_MAX];
CFStringRef * addressKeys = NULL; char * kextIDCString = NULL; OSKextRef theKext = NULL; CFIndex kextCount, idCount, kextIndex, idIndex;
if (!createCFMutableArray(&kextsToProcess, &kCFTypeArrayCallBacks)) {
result = EX_OSERR;
goto finish;
}
kextCount = CFArrayGetCount(toolArgs->kextURLs);
for (kextIndex = 0; kextIndex < kextCount; kextIndex++) {
kextURL = (CFURLRef)CFArrayGetValueAtIndex(
toolArgs->kextURLs, kextIndex);
if (!CFURLGetFileSystemRepresentation(kextURL,
false,
(u_char *)kextPathCString, sizeof(kextPathCString))) {
OSKextLogStringError( NULL);
result = EX_OSERR;
*fatal = true;
goto finish;
}
OSKextLog( NULL,
kOSKextLogStepLevel | kOSKextLogKextBookkeepingFlag,
"Looking up extension with URL %s.",
kextPathCString);
theKext = OSKextGetKextWithURL(kextURL);
if (!theKext) {
result = kKextutilExitNotFound;
continue;
}
addToArrayIfAbsent(kextsToProcess, theKext);
OSKextSetLoggingEnabled(theKext, true);
}
idCount = CFDictionaryGetCount(toolArgs->loadAddresses);
if (idCount) {
addressKeys = (CFStringRef *)malloc(idCount * sizeof(CFStringRef));
if (!addressKeys) {
OSKextLogMemError();
result = EX_OSERR;
*fatal = true;
goto finish;
}
CFDictionaryGetKeysAndValues(toolArgs->loadAddresses,
(void *)addressKeys, NULL);
for (idIndex = 0; idIndex < idCount; idIndex++) {
if (kCFNotFound == CFArrayGetFirstIndexOfValue(toolArgs->kextIDs,
RANGE_ALL(toolArgs->kextIDs), addressKeys[idIndex])) {
CFArrayAppendValue(toolArgs->kextIDs, addressKeys[idIndex]);
}
}
}
idCount = CFArrayGetCount(toolArgs->kextIDs);
for (idIndex = 0; idIndex < idCount; idIndex++) {
CFStringRef thisKextID = (CFStringRef)
CFArrayGetValueAtIndex(toolArgs->kextIDs, idIndex);
SAFE_FREE_NULL(kextIDCString);
kextIDCString = createUTF8CStringForCFString(thisKextID);
if (!kextIDCString) {
OSKextLogMemError();
result = EX_OSERR;
goto finish;
}
kextCount = CFArrayGetCount(kextsToProcess);
for (kextIndex = 0; kextIndex < kextCount; kextIndex++) {
OSKextRef scanKext = (OSKextRef)CFArrayGetValueAtIndex(
kextsToProcess, kextIndex);
CFStringRef scanKextID = OSKextGetIdentifier(scanKext);
if (CFEqual(thisKextID, scanKextID)) {
theKext = scanKext;
break;
}
}
if (!theKext) {
OSKextLog( NULL,
kOSKextLogStepLevel | kOSKextLogKextBookkeepingFlag,
"Looking up extension with identifier %s.",
kextIDCString);
theKext = OSKextGetKextWithIdentifier(thisKextID);
}
if (!theKext) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Can't find extension with identifier %s.",
kextIDCString);
result = kKextutilExitNotFound;
continue; }
kextURL = OSKextGetURL(theKext);
if (!CFURLGetFileSystemRepresentation(kextURL,
false,
(u_char *)kextPathCString, sizeof(kextPathCString))) {
OSKextLogStringError(theKext);
result = EX_OSERR;
*fatal = true;
goto finish;
}
OSKextLog( NULL,
kOSKextLogStepLevel | kOSKextLogKextBookkeepingFlag,
"Found %s for identifier %s.", kextPathCString, kextIDCString);
addToArrayIfAbsent(kextsToProcess, theKext);
OSKextSetLoggingEnabled(theKext, true);
}
finish:
SAFE_FREE(addressKeys);
SAFE_FREE(kextIDCString);
SAFE_RELEASE_NULL(toolArgs->kextURLs);
SAFE_RELEASE_NULL(toolArgs->kextIDs);
if (*fatal) {
SAFE_RELEASE(kextsToProcess);
*outArray = NULL;
} else {
*outArray = kextsToProcess;
}
return result;
}
typedef struct {
Boolean fatal;
} SetAddressContext;
void
setKextLoadAddress(
const void * vKey,
const void * vValue,
void * vContext)
{
CFStringRef bundleID = (CFStringRef)vKey;
CFNumberRef loadAddressNumber = (CFNumberRef)vValue;
SetAddressContext * context = (SetAddressContext *)vContext;
CFArrayRef kexts = NULL; OSKextRef theKext = NULL; uint64_t loadAddress = 0;
CFIndex count, i;
if (context->fatal) {
goto finish;
}
if (!CFNumberGetValue(loadAddressNumber, kCFNumberSInt64Type,
&loadAddress)) {
context->fatal = true;
goto finish;
}
kexts = OSKextCopyKextsWithIdentifier(bundleID);
if (!kexts) {
goto finish;
}
count = CFArrayGetCount(kexts);
for (i = 0; i < count; i++) {
theKext = (OSKextRef)CFArrayGetValueAtIndex(kexts, i);
if (!OSKextSetLoadAddress(theKext, loadAddress)) {
context->fatal = true;
goto finish;
}
}
finish:
SAFE_RELEASE(kexts);
return;
}
ExitStatus
processKexts(
CFArrayRef kextsToProcess,
KextutilArgs * toolArgs)
{
ExitStatus result = EX_OK;
OSReturn readLoadInfoResult = kOSReturnError;
Boolean fatal = false;
SetAddressContext setLoadAddressContext;
CFIndex count, i;
if (toolArgs->getAddressesFromKernel) {
readLoadInfoResult = OSKextReadLoadedKextInfo(
NULL,
true);
if (readLoadInfoResult != kOSReturnSuccess) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"Failed to read load info for kexts - %s.",
safe_mach_error_string(readLoadInfoResult));
result = EX_OSERR;
goto finish;
}
} else if (toolArgs->loadAddresses) {
setLoadAddressContext.fatal = false;
CFDictionaryApplyFunction(toolArgs->loadAddresses, setKextLoadAddress,
&setLoadAddressContext);
if (setLoadAddressContext.fatal) {
result = EX_OSERR; goto finish;
}
}
count = CFArrayGetCount(kextsToProcess);
for (i = 0; i < count; i++) {
OSKextRef theKext = (OSKextRef)CFArrayGetValueAtIndex(kextsToProcess, i);
int loadResult = processKext(theKext, toolArgs, &fatal);
if (result == EX_OK && loadResult != EX_OK) {
result = loadResult;
}
if (fatal) {
goto finish;
}
}
finish:
return result;
}
ExitStatus
processKext(
OSKextRef aKext,
KextutilArgs * toolArgs,
Boolean * fatal)
{
ExitStatus result = EX_OK;
char kextPathCString[PATH_MAX];
if (!CFURLGetFileSystemRepresentation(OSKextGetURL(aKext),
false,
(u_char *)kextPathCString, sizeof(kextPathCString))) {
OSKextLogMemError();
result = EX_OSERR;
*fatal = true;
goto finish;
}
result = runTestsOnKext(aKext, kextPathCString, toolArgs, fatal);
if (result != EX_OK) {
goto finish;
}
if (!toolArgs->doLoad &&
!toolArgs->doStartMatching &&
!toolArgs->symbolDirURL) {
goto finish;
}
if (toolArgs->doLoad) {
OSStatus sigResult = checkKextSignature(aKext, true, false);
if ( sigResult != 0 ) {
if ( isInvalidSignatureAllowed() ) {
OSKextLogCFString(NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag,
CFSTR("kext-dev-mode allowing invalid signature %ld 0x%02lX for kext \"%s\""),
(long)sigResult, (long)sigResult, kextPathCString);
}
else {
CFStringRef myBundleID;
myBundleID = OSKextGetIdentifier(aKext);
result = kOSKextReturnNotLoadable; OSKextLogCFString(NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag,
CFSTR("ERROR: invalid signature for %@, will not load"),
myBundleID ? myBundleID : CFSTR("Unknown"));
goto finish;
}
}
result = loadKext(aKext, kextPathCString, toolArgs, fatal);
}
if (result != EX_OK) {
goto finish;
}
if (toolArgs->doLoad) {
recordKextLoadForMT(aKext);
}
if (toolArgs->doLoad &&
(toolArgs->symbolDirURL ||
toolArgs->interactiveLevel != kOSKextExcludeNone)) {
OSReturn readLoadedResult = OSKextReadLoadedKextInfo(
NULL,
true);
if (kOSReturnSuccess != readLoadedResult) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"Failed to reread load info after loading %s - %s.",
kextPathCString,
safe_mach_error_string(readLoadedResult));
result = EX_OSERR;
goto finish;
}
}
if (toolArgs->symbolDirURL) {
result = generateKextSymbols(aKext, kextPathCString, toolArgs,
TRUE, fatal);
if (result != EX_OK) {
goto finish;
}
}
if (toolArgs->doLoad ||
toolArgs->doStartMatching) {
result = startKextsAndSendPersonalities(aKext, toolArgs,
fatal);
if (result != EX_OK) {
goto finish;
}
}
finish:
return result;
}
ExitStatus
runTestsOnKext(
OSKextRef aKext,
char * kextPathCString,
KextutilArgs * toolArgs,
Boolean * fatal)
{
ExitStatus result = EX_OK;
ExitStatus tempResult = EX_OK;
Boolean kextLooksGood = true;
Boolean tryLink = false;
OSKextLogSpec logFilter = OSKextGetLogFilter( false);
OSStatus sigResult = 0;
if (toolArgs->safeBootMode &&
!OSKextIsLoadableInSafeBoot(aKext)) {
Boolean mustQualify = (toolArgs->doLoad || toolArgs->doStartMatching);
OSKextLogSpec msgLogLevel = kOSKextLogErrorLevel;
if (mustQualify) {
msgLogLevel = kOSKextLogWarningLevel;
}
OSKextLog( NULL,
msgLogLevel | kOSKextLogLoadFlag,
"%s%s is not eligible for loading during safe boot.",
mustQualify ? "" : "Notice: ",
kextPathCString);
if (mustQualify && result == EX_OK) {
result = kKextutilExitSafeBoot;
}
}
if (OSKextHasLogOrDebugFlags(aKext)) {
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogLoadFlag,
"Notice: %s has debug properties set.", kextPathCString);
}
kextLooksGood = OSKextValidate(aKext) && kextLooksGood;
if (!toolArgs->skipAuthentication) {
kextLooksGood = OSKextAuthenticate(aKext) && kextLooksGood;
}
if (!toolArgs->skipDependencies) {
kextLooksGood = OSKextResolveDependencies(aKext) && kextLooksGood;
kextLooksGood = OSKextValidateDependencies(aKext) && kextLooksGood;
if (!toolArgs->skipAuthentication) {
kextLooksGood = OSKextAuthenticateDependencies(aKext) &&
kextLooksGood;
}
}
if (toolArgs->safeBootMode) {
kextLooksGood = OSKextIsLoadableInSafeBoot(aKext) && kextLooksGood;
kextLooksGood = OSKextDependenciesAreLoadableInSafeBoot(aKext) && kextLooksGood;
}
sigResult = checkKextSignature(aKext, false, false);
if (!kextLooksGood || sigResult != 0) {
if ((logFilter & kOSKextLogLevelMask) >= kOSKextLogErrorLevel) {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Diagnostics for %s:",
kextPathCString);
OSKextLogDiagnostics(aKext, kOSKextDiagnosticsFlagAll);
if (sigResult != 0) {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Code Signing Failure: %s",
(sigResult == errSecCSUnsigned
? "not code signed"
: "code signature is invalid") );
}
}
if (!kextLooksGood) {
result = kKextutilExitKextBad;
goto finish;
}
}
if (result == EX_OK) {
if ((logFilter & kOSKextLogLevelMask) >= kOSKextLogWarningLevel) {
OSKextLogDiagnostics(aKext, kOSKextDiagnosticsFlagWarnings);
}
}
tryLink = !toolArgs->doLoad &&
!toolArgs->symbolDirURL &&
kextLooksGood;
tryLink = tryLink &&
((OSKextGetArchitecture() == OSKextGetRunningKernelArchitecture()) ||
toolArgs->kernelURL);
if (tryLink) {
tempResult = generateKextSymbols(aKext,
kextPathCString, toolArgs, false, fatal);
tryLink = true;
if (result == EX_OK) {
result = tempResult;
}
}
if (result == EX_OK) {
OSKextLog( NULL,
kOSKextLogBasicLevel | kOSKextLogLoadFlag,
"%s appears to be loadable (%sincluding linkage for on-disk libraries).",
kextPathCString, tryLink ? "" : "not ");
}
#if 0
OSKextLogDependencyGraph(aKext, true,
true);
#endif
finish:
return result;
}
ExitStatus
loadKext(
OSKextRef aKext,
char * kextPathCString,
KextutilArgs * toolArgs,
Boolean * fatal)
{
ExitStatus result = EX_OK;
OSKextExcludeLevel startExclude = toolArgs->interactiveLevel;
OSKextExcludeLevel matchExclude = toolArgs->interactiveLevel;
CFArrayRef personalityNames = toolArgs->personalityNames;
OSReturn loadResult = kOSReturnError;
if (OSKextIsInExcludeList(aKext, false)) {
#if 1 // <rdar://problem/12811081>
CFMutableDictionaryRef myAlertInfoDict = NULL;
addKextToAlertDict(&myAlertInfoDict, aKext);
if (myAlertInfoDict) {
postNoteAboutKexts(CFSTR("Excluded Kext Notification"),
myAlertInfoDict );
SAFE_RELEASE(myAlertInfoDict);
}
#endif
messageTraceExcludedKext(aKext);
OSKextLog(NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag |
kOSKextLogValidationFlag | kOSKextLogGeneralFlag,
"%s is in exclude list; omitting.", kextPathCString);
result = kOSKextReturnNotLoadable;
*fatal = true;
goto finish;
}
if (toolArgs->interactiveLevel != kOSKextExcludeNone) {
switch (user_approve( FALSE, REPLY_YES,
"Load %s and its dependencies into the kernel",
kextPathCString)) {
case REPLY_NO:
fprintf(stderr, "Not loading %s.", kextPathCString);
goto finish; break;
case REPLY_YES:
break;
default:
fprintf(stderr, "Failed to read response.");
result = EX_SOFTWARE;
*fatal = true;
goto finish;
break;
}
}
OSKextLog( NULL, kOSKextLogBasicLevel | kOSKextLogLoadFlag,
"Loading %s.", kextPathCString);
if (toolArgs->interactiveLevel != kOSKextExcludeNone) {
personalityNames = NULL;
}
if (!toolArgs->doStartMatching) {
matchExclude = kOSKextExcludeAll;
}
loadResult = OSKextLoadWithOptions(aKext,
startExclude, matchExclude, personalityNames,
(startExclude != kOSKextExcludeNone));
if (loadResult == kOSReturnSuccess) {
OSKextLog( NULL, kOSKextLogBasicLevel | kOSKextLogLoadFlag,
"%s successfully loaded (or already loaded).",
kextPathCString);
} else {
OSKextLog( NULL, kOSKextLogBasicLevel | kOSKextLogLoadFlag,
"Failed to load %s - %s.",
kextPathCString, safe_mach_error_string(loadResult));
}
if (loadResult == kOSKextReturnLinkError) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Check library declarations for your kext with kextlibs(8).");
}
if (loadResult != kOSReturnSuccess) {
result = kKextutilExitLoadFailed;
}
finish:
return result;
}
ExitStatus generateKextSymbols(
OSKextRef aKext,
char * kextPathCString,
KextutilArgs * toolArgs,
Boolean saveFlag,
Boolean * fatal)
{
ExitStatus result = EX_OK;
CFArrayRef loadList = NULL; CFDictionaryRef kextSymbols = NULL; const NXArchInfo * archInfo = NULL; CFIndex count, i;
if (saveFlag && !toolArgs->symbolDirURL) {
result = EX_USAGE;
*fatal = TRUE;
goto finish;
}
if (OSKextIsKernelComponent(aKext)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"%s is a kernel component; no symbols to generate.",
kextPathCString);
result = EX_DATAERR;
goto finish;
}
if (OSKextIsInterface(aKext)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"%s is a an interface kext; no symbols to generate.",
kextPathCString);
result = EX_DATAERR;
goto finish;
}
archInfo = OSKextGetArchitecture();
if (!OSKextSupportsArchitecture(aKext, archInfo)) {
int native = (archInfo == NXGetLocalArchInfo());
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s does not contain code for %sarchitecture%s%s.",
kextPathCString,
native ? "this computer's " : "",
native ? "" : " ",
native ? "" : archInfo->name);
result = kKextutilExitArchNotFound;
goto finish;
}
if (saveFlag &&
OSKextNeedsLoadAddressForDebugSymbols(aKext)) {
loadList = OSKextCopyLoadList(aKext, true);
if (!loadList) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"Can't resolve dependencies for %s.",
kextPathCString);
result = EX_SOFTWARE;
*fatal = true;
goto finish;
}
fprintf(stderr, "\nEnter the hexadecimal load addresses for these extensions\n"
"(press Return to skip symbol generation for an extension):\n\n");
count = CFArrayGetCount(loadList);
for (i = 0; i < count; i++) {
OSKextRef thisKext = (OSKextRef)CFArrayGetValueAtIndex(loadList, i);
Boolean mainNeedsAddress = ((i + 1) == count);
do {
switch (requestLoadAddress(thisKext)) {
case -1: result = EX_SOFTWARE;
goto finish;
break;
case 0: fprintf(stderr, "\nuser canceled address input; exiting\n");
result = kKextutilExitUserAbort;
goto finish;
break;
case 1: break;
}
if (mainNeedsAddress && OSKextNeedsLoadAddressForDebugSymbols(aKext)) {
switch (user_approve( FALSE, REPLY_NO,
"\n%s is the main extension; really skip", kextPathCString)) {
case REPLY_NO:
break;
case REPLY_YES:
result = EX_OK;
goto finish; default:
result = EX_SOFTWARE;
goto finish;
}
}
} while (mainNeedsAddress && OSKextNeedsLoadAddressForDebugSymbols(aKext));
}
}
kextSymbols = OSKextGenerateDebugSymbols(aKext, toolArgs->kernelFile);
if (!kextSymbols) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Check library declarations for your kext with kextlibs(8).");
result = kKextutilExitKextBad; *fatal = true;
goto finish;
}
if (saveFlag) {
SaveFileContext saveFileContext;
saveFileContext.saveDirURL = toolArgs->symbolDirURL;
saveFileContext.overwrite = toolArgs->overwriteSymbols;
saveFileContext.fatal = false;
CFDictionaryApplyFunction(kextSymbols, &saveFile, &saveFileContext);
if (saveFileContext.fatal) {
*fatal = true;
goto finish;
}
}
finish:
SAFE_RELEASE(loadList);
SAFE_RELEASE(kextSymbols);
return result;
}
int
requestLoadAddress(
OSKextRef aKext)
{
int result = -1;
char * bundleIDCString = NULL; char * user_response = NULL; char * scan_pointer = NULL; uint64_t address = 0;
Boolean eof = false;
bundleIDCString = createUTF8CStringForCFString(
OSKextGetIdentifier(aKext));
if (!bundleIDCString) {
goto finish;
}
if (OSKextNeedsLoadAddressForDebugSymbols(aKext)) {
while (1) {
SAFE_FREE(user_response);
user_response = (char *)user_input(&eof, "%s:", bundleIDCString);
if (eof) {
result = 0;
goto finish;
}
if (!user_response) {
goto finish;
}
if (user_response[0] == '\0') {
result = 1;
goto finish;
break;
}
errno = 0;
address = strtoull(user_response, &scan_pointer, 16);
if (address == ULONG_LONG_MAX && errno == ERANGE) {
fprintf(stderr, "input address %s is too large; try again\n",
user_response);
continue;
} else if (address == 0 && errno == EINVAL) {
fprintf(stderr, "no address found in input '%s'; try again\n",
user_response);
continue;
} else if (address == 0) {
fprintf(stderr, "invalid address %s\n",
user_response);
continue;
} else if (*scan_pointer != '\0') {
fprintf(stderr,
"input '%s' not a plain hexadecimal address; try again\n",
user_response);
continue;
} else {
break;
}
}
OSKextSetLoadAddress(aKext, address);
}
result = 1;
finish:
SAFE_FREE(bundleIDCString);
SAFE_FREE(user_response);
return result;
}
ExitStatus startKextsAndSendPersonalities(
OSKextRef aKext,
KextutilArgs * toolArgs,
Boolean * fatal)
{
ExitStatus result = EX_OK;
CFArrayRef loadList = NULL; CFMutableArrayRef kextIdentifiers = NULL; char * kextIDCString = NULL; char * thisKextIDCString = NULL; Boolean startedAndPersonalitiesSent = TRUE; Boolean yesToAllKexts = FALSE;
Boolean yesToAllKextPersonalities = FALSE;
char kextPath[PATH_MAX];
CFIndex count, i;
if (!CFURLGetFileSystemRepresentation(OSKextGetURL(aKext),
false, (UInt8*)kextPath, sizeof(kextPath))) {
strlcpy(kextPath, "(unknown)", sizeof(kextPath));
}
kextIDCString = createUTF8CStringForCFString(OSKextGetIdentifier(aKext));
if (!kextIDCString) {
OSKextLogMemError();
result = EX_OSERR;
}
loadList = OSKextCopyLoadList(aKext, true);
if (!loadList) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Can't get dependency list for %s.",
kextIDCString);
result = EX_OSERR;
goto finish;
}
count = CFArrayGetCount(loadList);
if (!count) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Internal error - empty load list.");
result = EX_SOFTWARE;
*fatal = true;
goto finish;
}
kextIdentifiers = CFArrayCreateMutable(CFGetAllocator(aKext),
count, &kCFTypeArrayCallBacks);
if (!kextIdentifiers) {
OSKextLogMemError();
result = EX_OSERR;
goto finish;
}
if (toolArgs->interactiveLevel != kOSKextExcludeNone && toolArgs->doLoad) {
fprintf(stderr, "\n"
"%s and its dependencies are now loaded, and started as listed below. "
"You can now return to the debugger to set breakpoints before starting "
"any kexts that need to be started.\n\n",
kextPath);
if (toolArgs->interactiveLevel == kOSKextExcludeKext) {
i = count - 1;
} else {
i = 0;
}
for ( ; i < count; i++) {
OSKextRef thisKext = (OSKextRef)CFArrayGetValueAtIndex(
loadList, i);
const char * status = NULL;
SAFE_FREE_NULL(thisKextIDCString);
if (!CFURLGetFileSystemRepresentation(OSKextGetURL(thisKext),
false, (UInt8*)kextPath, sizeof(kextPath))) {
strlcpy(kextPath, "(unknown)", sizeof(kextPath));
}
thisKextIDCString = createUTF8CStringForCFString(OSKextGetIdentifier(thisKext));
if (!thisKextIDCString) {
OSKextLogMemError();
result = EX_OSERR;
goto finish;
}
if (OSKextIsInterface(thisKext)) {
status = "interface, not startable";
} else if (!OSKextDeclaresExecutable(thisKext)) {
status = "no executable, not startable";
} else if (OSKextIsStarted(thisKext)) {
status = "already started";
} else {
status = "not started";
}
fprintf(stderr, " %s - %s\n", thisKextIDCString, status);
}
fprintf(stderr, "\n");
}
if (toolArgs->interactiveLevel == kOSKextExcludeKext) {
i = count - 1;
} else {
i = 0;
}
for ( ; i < count; i++) {
OSKextRef thisKext = (OSKextRef)CFArrayGetValueAtIndex(
loadList, i);
SAFE_FREE_NULL(thisKextIDCString);
if (!CFURLGetFileSystemRepresentation(OSKextGetURL(thisKext),
false, (UInt8*)kextPath, sizeof(kextPath))) {
strlcpy(kextPath, "(unknown)", sizeof(kextPath));
}
CFArrayAppendValue(kextIdentifiers, OSKextGetIdentifier(thisKext));
thisKextIDCString = createUTF8CStringForCFString(OSKextGetIdentifier(thisKext));
if (!thisKextIDCString) {
OSKextLogMemError();
result = EX_OSERR;
goto finish;
}
if (toolArgs->interactiveLevel != kOSKextExcludeNone && toolArgs->doLoad) {
if (!OSKextIsStarted(thisKext)) {
result = startKext(thisKext, kextPath, toolArgs,
&startedAndPersonalitiesSent, &yesToAllKexts, fatal);
if (result != EX_OK || !startedAndPersonalitiesSent) {
break;
}
}
}
if (toolArgs->interactiveLevel != kOSKextExcludeNone ||
(toolArgs->doStartMatching && !toolArgs->doLoad)) {
result = sendPersonalities(thisKext, kextPath, toolArgs,
(i + 1 == count), &yesToAllKextPersonalities, fatal);
if (result != EX_OK) {
startedAndPersonalitiesSent = false;
break;
}
}
}
if (!startedAndPersonalitiesSent) {
OSReturn readLoadedResult = OSKextReadLoadedKextInfo(
kextIdentifiers, false);
if (kOSReturnSuccess != readLoadedResult) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Failed to read load info after starting - %s.",
safe_mach_error_string(readLoadedResult));
result = EX_OSERR;
*fatal = true;
}
for (i = count - 1; i >= 0; i--) {
OSKextRef thisKext = (OSKextRef)CFArrayGetValueAtIndex(
loadList, i);
if (!CFURLGetFileSystemRepresentation(OSKextGetURL(thisKext),
false, (UInt8*)kextPath, sizeof(kextPath))) {
strlcpy(kextPath, "(unknown)", sizeof(kextPath));
}
if (!OSKextIsStarted(thisKext)) {
OSReturn unloadResult = OSKextUnload(thisKext,
true);
OSKextLog( NULL,
kOSKextLogStepLevel | kOSKextLogLoadFlag,
"Unloading kext %s after failing to start/send personalities.", kextPath);
if (kOSReturnSuccess != unloadResult) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"Failed to unload kext %s after failing to start/send personalities - %s.",
kextPath,
safe_mach_error_string(unloadResult));
result = EX_OSERR;
} else {
OSKextLog( NULL,
kOSKextLogStepLevel | kOSKextLogLoadFlag,
"%s unloaded.", kextPath);
}
}
}
}
finish:
SAFE_RELEASE(loadList);
SAFE_RELEASE(kextIdentifiers);
SAFE_FREE(kextIDCString);
SAFE_FREE(thisKextIDCString);
return result;
}
ExitStatus startKext(
OSKextRef aKext,
char * kextPathCString,
KextutilArgs * toolArgs,
Boolean * started,
Boolean * yesToAll,
Boolean * fatal)
{
ExitStatus result = EX_OK;
OSReturn startResult = kOSReturnError;
if (!OSKextIsLoaded(aKext)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"%s is unexpectedly not loaded after loading!",
kextPathCString);
*started = false;
result = EX_OSERR;
goto finish;
}
if (FALSE == *yesToAll) {
switch (user_approve( TRUE,
REPLY_YES,
"Start %s",
kextPathCString)) {
case REPLY_NO:
OSKextLog( NULL,
kOSKextLogBasicLevel | kOSKextLogLoadFlag,
"Not starting %s.",
kextPathCString);
*started = false;
goto finish; break;
case REPLY_YES:
break;
case REPLY_ALL:
fprintf(stderr, "Starting all kexts just loaded.\n");
*yesToAll = TRUE;
break;
default:
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Error: couldn't read response.");
result = EX_SOFTWARE;
*started = false;
*fatal = true;
goto finish;
break;
}
}
startResult = OSKextStart(aKext);
if (kOSReturnSuccess != startResult) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"%s failed to start - %s.",
kextPathCString,
safe_mach_error_string(startResult));
*started = false;
result = EX_OSERR;
goto finish;
} else {
OSKextLog( NULL,
kOSKextLogBasicLevel | kOSKextLogLoadFlag,
"%s started.", kextPathCString);
*started = true;
}
finish:
return result;
}
ExitStatus sendPersonalities(
OSKextRef aKext,
char * kextPathCString,
KextutilArgs * toolArgs,
Boolean isMainFlag,
Boolean * yesToAllKextPersonalities,
Boolean * fatal)
{
ExitStatus result = EX_OK;
CFDictionaryRef kextPersonalities = NULL; CFMutableArrayRef namesToSend = NULL; CFStringRef * names = NULL; char * nameCString = NULL; OSReturn sendPersonalitiesResult = kOSReturnError;
Boolean yesToAllPersonalities = FALSE;
CFIndex count, i;
kextPersonalities = OSKextGetValueForInfoDictionaryKey(aKext,
CFSTR(kIOKitPersonalitiesKey));
if (!kextPersonalities || !CFDictionaryGetCount(kextPersonalities)) {
OSKextLog( NULL,
kOSKextLogStepLevel | kOSKextLogLoadFlag,
"%s has no personalities to send.",
kextPathCString);
goto finish;
}
if (toolArgs->interactiveLevel != kOSKextExcludeNone) {
if (FALSE == *yesToAllKextPersonalities) {
switch (user_approve( TRUE, REPLY_YES,
"Send personalities for %s",
kextPathCString)) {
case REPLY_NO:
fprintf(stderr, "Not sending personalities for %s.", kextPathCString);
goto finish; break;
case REPLY_YES:
break;
case REPLY_ALL:
fprintf(stderr, "Sending personalities for all kexts just loaded.\n");
*yesToAllKextPersonalities = TRUE;
break;
default:
fprintf(stderr, "Error: couldn't read response.");
result = EX_SOFTWARE;
*fatal = true;
goto finish;
break;
}
}
}
namesToSend = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks);
if (!namesToSend) {
OSKextLogMemError();
result = EX_OSERR;
*fatal = true;
goto finish;
}
count = CFDictionaryGetCount(kextPersonalities);
names = (CFStringRef *)malloc(count * sizeof(CFStringRef));
if (!names) {
OSKextLogMemError();
result = EX_OSERR;
*fatal = true;
goto finish;
}
CFDictionaryGetKeysAndValues(kextPersonalities,
(const void **)names, NULL);
for (i = 0; i < count; i++) {
Boolean includeIt = TRUE;
SAFE_FREE_NULL(nameCString);
nameCString = createUTF8CStringForCFString(names[i]);
if (!nameCString) {
OSKextLogMemError();
result = EX_OSERR;
*fatal = true;
goto finish;
}
if (isMainFlag && CFArrayGetCount(toolArgs->personalityNames) > 0) {
if (kCFNotFound == CFArrayGetFirstIndexOfValue(
toolArgs->personalityNames,
RANGE_ALL(toolArgs->personalityNames), names[i])) {
continue;
}
}
if (toolArgs->interactiveLevel != kOSKextExcludeNone &&
FALSE == *yesToAllKextPersonalities) {
if (FALSE == yesToAllPersonalities) {
switch (user_approve( TRUE, REPLY_YES,
"Send personality %s", nameCString)) {
case REPLY_NO:
includeIt = FALSE;
break;
case REPLY_YES:
includeIt = TRUE;
break;
case REPLY_ALL:
fprintf(stderr, "Sending all personalities for %s.\n",
kextPathCString);
includeIt = TRUE;
yesToAllPersonalities = TRUE;
break;
default:
fprintf(stderr, "Error: couldn't read response.");
result = EX_SOFTWARE;
*fatal = true;
goto finish;
break;
}
}
}
if (includeIt) {
CFArrayAppendValue(namesToSend, names[i]);
}
}
OSKextLog( NULL,
kOSKextLogStepLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Sending personalities of %s to the IOCatalogue.",
kextPathCString);
sendPersonalitiesResult = OSKextSendKextPersonalitiesToKernel(aKext, namesToSend);
if (kOSReturnSuccess != sendPersonalitiesResult) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Failed to send personalities for %s - %s.",
kextPathCString,
safe_mach_error_string(sendPersonalitiesResult));
result = EX_OSERR;
goto finish;
} else {
OSKextLog( NULL,
kOSKextLogStepLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Personalities sent for %s.", kextPathCString);
}
finish:
SAFE_RELEASE(namesToSend);
SAFE_FREE(names);
SAFE_FREE(nameCString);
return result;
}
Boolean serializeLoad(KextutilArgs * toolArgs, Boolean loadFlag)
{
Boolean result = false;
kern_return_t kern_result;
int lock_retries = LOCK_MAXTRIES;
if (!loadFlag) {
result = true;
goto finish;
}
kern_result = bootstrap_look_up(bootstrap_port,
KEXTD_SERVER_NAME, &sKextdPort);
if (kern_result != KERN_SUCCESS) {
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogIPCFlag,
"Can't contact kextd (continuing anyway) - %s.",
bootstrap_strerror(kern_result));
}
if (sKextdPort != MACH_PORT_NULL) {
kern_result = mach_port_allocate(mach_task_self(),
MACH_PORT_RIGHT_RECEIVE, &sLockPort);
if (kern_result != KERN_SUCCESS) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Can't allocate kext loading serialization mach port.");
goto finish;
}
do {
kern_result = kextmanager_lock_kextload(sKextdPort, sLockPort,
&sLockStatus);
if (kern_result != KERN_SUCCESS) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Can't acquire kextload serialization lock; aborting.");
goto finish;
}
if (sLockStatus == EBUSY) {
--lock_retries;
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogIPCFlag,
"Kext loading serialization lock busy; "
"sleeping (%d retries left).",
lock_retries);
sleep(LOCK_DELAY);
}
} while (sLockStatus == EBUSY && lock_retries > 0);
if (sLockStatus != 0) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Can't acquire kextload serialization lock; aborting.");
goto finish;
} else {
sLockTaken = true;
}
}
result = true;
finish:
return result;
}
void usage(UsageLevel usageLevel)
{
fprintf(stderr, "usage: %s [options] [--] [kext] ...\n"
"\n", progname);
if (usageLevel == kUsageLevelBrief) {
fprintf(stderr, "use %s -%s for an explanation of each option\n",
progname, kOptNameHelp);
return;
}
fprintf(stderr, "kext: a kext bundle to load or examine\n");
fprintf(stderr, "\n");
fprintf(stderr, "-%s <bundle_id> (-%c):\n"
" load/use the kext whose CFBundleIdentifier is <bundle_id>\n",
kOptNameBundleIdentifier, kOptBundleIdentifier);
fprintf(stderr, "-%s <personality> (-%c):\n"
" send the named personality to the catalog\n",
kOptNamePersonality, kOptPersonality);
fprintf(stderr, "-%s <kext> (-%c):\n"
" consider <kext> as a candidate dependency\n",
kOptNameDependency, kOptDependency);
fprintf(stderr, "-%s <directory> (-%c):\n"
" look in <directory> for kexts\n",
kOptNameRepository, kOptRepository);
fprintf(stderr, "\n");
fprintf(stderr, "-%s (-%c):\n"
" don't use repository caches; scan repository folders\n",
kOptNameNoCaches, kOptNoCaches);
fprintf(stderr, "-%s (-%c):\n"
" don't check for loaded kexts when resolving dependencies "
"(deprecated)\n",
kOptNameNoLoadedCheck, kOptNoLoadedCheck);
fprintf(stderr, "-%s (-%c):\n"
" don't use system extension folders\n",
kOptNameNoSystemExtensions, kOptNoSystemExtensions);
fprintf(stderr, "\n");
fprintf(stderr, "-%s (-%c):\n"
" interactive mode\n",
kOptNameInteractive, kOptInteractive);
fprintf(stderr, "-%s (-%c):\n"
" interactive mode for extension and all its dependencies\n",
kOptNameInteractiveAll, kOptInteractiveAll);
fprintf(stderr, "\n");
fprintf(stderr, "-%s (-%c):\n"
" load & start only; don't start matching\n",
kOptNameLoadOnly, kOptLoadOnly);
fprintf(stderr, "-%s (-%c):\n"
" start matching only, by sending personalities; "
"don't load executable\n",
kOptNameMatchOnly, kOptMatchOnly);
fprintf(stderr, "-%s (-%c):\n"
" neither load nor start matching\n",
kOptNameNoLoad, kOptNoLoad);
fprintf(stderr, "-%s <directory> (-%c):\n"
" write symbol files into <directory>\n",
kOptNameSymbolsDirectory, kOptSymbolsDirectory);
fprintf(stderr, "-%s <archname>:\n"
" use architecture <archnaem>\n",
kOptNameArch);
fprintf(stderr, "-%s <kext_id@address> (-%c):\n"
" <kext_id> is loaded at address (for symbol generation)\n",
kOptNameAddress, kOptAddress);
fprintf(stderr, "-%s (-%c):\n"
" get load addresses for kexts from what's loaded "
"(for symbol generation)\n",
kOptNameUseKernelAddresses, kOptUseKernelAddresses);
fprintf(stderr, "-%s <kernelFile> (-%c):\n"
" link against <kernelFile> (default is /System/Library/Kernels/kernel)\n",
kOptNameKernel, kOptKernel);
fprintf(stderr, "\n");
fprintf(stderr, "-%s (-%c):\n"
" quiet mode: print no informational or error messages\n",
kOptNameQuiet, kOptQuiet);
fprintf(stderr, "-%s [ 0-6 | 0x<flags> ] (-%c):\n"
" verbose mode; print info about analysis & loading\n",
kOptNameVerbose, kOptVerbose);
fprintf(stderr, "\n");
fprintf(stderr, "-%s (-%c):\n"
" perform all diagnostic tests and print a report on each kext\n",
kOptNameTests, kOptTests);
fprintf(stderr, "-%s (-%c):\n"
" simulate safe boot mode for diagnostic tests\n",
kOptNameSafeBoot, kOptSafeBoot);
fprintf(stderr, "-%s (-%c):\n"
" don't authenticate kexts (for use during development)\n",
kOptNameNoAuthentication, kOptNoAuthentication);
fprintf(stderr, "-%s (-%c):\n"
" don't check dependencies when diagnosing with\n"
" -%s & -%s (-%c%c)\n",
kOptNameNoResolveDependencies, kOptNoResolveDependencies,
kOptNameNoLoad, kOptNameTests,
kOptNoLoad, kOptTests);
fprintf(stderr, "\n");
fprintf(stderr, "-%s (-%c): print this message and exit\n",
kOptNameHelp, kOptHelp);
fprintf(stderr, "\n");
fprintf(stderr, "--: end of options\n");
return;
}