#include <CommonCrypto/CommonDigest.h>
#include <CoreFoundation/CFRuntime.h>
#include <CoreFoundation/CFBundlePriv.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOCFSerialize.h>
#include <IOKit/IOCFUnserialize.h>
#include <IOKit/IOKitServer.h>
#ifndef IOKIT_EMBEDDED
#include <kxld.h>
#include <kxld_types.h>
#endif
#ifdef SPLIT_KEXTS_DEBUG
#undef SPLIT_KEXTS_DEBUG
#endif
#define SPLIT_KEXTS_DEBUG 0
#include <sys/cdefs.h>
#include <System/libkern/mkext.h>
#include <System/libkern/kext_request_keys.h>
#include <System/libkern/OSKextLibPrivate.h>
#include <Kernel/mach/vm_param.h>
#if __has_include(<prelink.h>)
#include <prelink.h>
#else
#include <System/libkern/prelink.h>
#endif
#include <fcntl.h>
#include <libc.h>
#include <pthread.h>
#include <mach/host_priv.h>
#include <mach/machine.h>
#include <zlib.h>
#include <sys/file.h>
#include <sys/mman.h>
#include <sys/sysctl.h>
#include <uuid/uuid.h>
#include <libgen.h>
#include <sandbox/rootless.h>
#include <sys/csr.h>
#include "OSKext.h"
#include "OSKextPrivate.h"
#include "printPList_new.h"
#include "fat_util.h"
#include "macho_util.h"
#include "misc_util.h"
#include "cross_link.h"
#include "kextsplit.h"
#define SHARED_EXECUTABLE 1
#define KEXT_MIN_ALIGN 6
#ifndef SEG_TEXT_EXEC
#define SEG_TEXT_EXEC "__TEXT_EXEC"
#endif
#ifndef SEG_DATA_CONST
#define SEG_DATA_CONST "__DATA_CONST"
#endif
#ifndef SEG_LLVM_COV
#define SEG_LLVM_COV "__LLVM_COV"
#endif
#ifndef kPrelinkTextSegment
#define kPrelinkTextSegment "__PRELINK_TEXT"
#endif
#ifndef kPrelinkTextExecSegment
#define kPrelinkTextExecSegment "__PLK_TEXT_EXEC"
#endif
#ifndef kPrelinkTextSection
#define kPrelinkTextSection "__text"
#endif
#ifndef kPrelinkDataSegment
#define kPrelinkDataSegment "__PRELINK_DATA"
#endif
#ifndef kPrelinkDataConstSegment
#define kPrelinkDataConstSegment "__PLK_DATA_CONST"
#endif
#ifndef kPrelinkDataSection
#define kPrelinkDataSection "__data"
#endif
#ifndef kPrelinkLinkeditSegment
#define kPrelinkLinkeditSegment "__PLK_LINKEDIT"
#endif
#ifndef kPrelinkLLVMCovSegment
#define kPrelinkLLVMCovSegment "__PLK_LLVM_COV"
#endif
#ifndef kPrelinkLLVMCovSection
#define kPrelinkLLVMCovSection "__llvm_covmap"
#endif
#ifndef kPrelinkLinkeditSection
#define kPrelinkLinkeditSection "__data"
#endif
#define MAX_SEGNAME_LEN 16
#pragma mark Notes
#pragma mark OSKext Data Structures
#ifdef IOKIT_EMBEDDED
typedef struct splitKextLinkInfo {
u_char * kextExecutable; size_t kextSize; u_char * linkedKext; size_t linkedKextSize; uint64_t vmaddr_TEXT; uint64_t vmaddr_TEXT_EXEC; uint64_t vmaddr_DATA; uint64_t vmaddr_DATA_CONST; uint64_t vmaddr_LINKEDIT; uint64_t vmaddr_LLVM_COV; uint32_t kaslr_offsets_count; uint32_t * kaslr_offsets; } splitKextLinkInfo;
#endif
typedef struct __OSKextLoadInfo {
CFMutableArrayRef dependencies;
CFDictionaryRef kernelLoadInfo; uint32_t loadTag;
splitKextLinkInfo linkInfo; uint64_t sourceAddress;
CFDataRef executable;
CFDataRef linkedExecutable;
CFDataRef prelinkedExecutable;
kmod_info_t * kmod_info;
uint64_t kmodInfoAddress;
struct {
unsigned int hasRawKernelDependency:1;
unsigned int hasKernelDependency:1;
unsigned int hasKPIDependency:1;
unsigned int hasPrivateKPIDependency:1;
unsigned int hasAllDependencies:1;
unsigned int dependenciesValid:1;
unsigned int dependenciesAuthentic:1;
unsigned int isLoaded:1;
unsigned int isStarted:1;
unsigned int otherCFBundleVersionIsLoaded:1;
unsigned int otherUUIDIsLoaded:1; } flags;
} __OSKextLoadInfo;
typedef struct __OSKextMkextInfo {
CFURLRef mkextURL;
CFDataRef mkextData; CFDataRef executable;
CFMutableDictionaryRef resources;
} __OSKextMkextInfo;
typedef struct __OSKextDiagnostics {
CFMutableDictionaryRef validationFailures;
CFMutableDictionaryRef authenticationFailures;
CFMutableDictionaryRef dependencyFailures; CFMutableDictionaryRef warnings;
CFMutableDictionaryRef bootLevel;
} __OSKextDiagnostics;
typedef struct __OSKext {
CFRuntimeBase cfBase;
CFURLRef bundleURL;
CFStringRef bundleID;
CFURLRef executableURL;
OSKextVersion version;
OSKextVersion compatibleVersion;
CFMutableDictionaryRef infoDictionary;
__OSKextDiagnostics * diagnostics;
__OSKextLoadInfo * loadInfo;
__OSKextMkextInfo * mkextInfo;
struct {
unsigned int isPluginChecked:1;
unsigned int isPlugin:1;
unsigned int isFromIdentifierCache:1; unsigned int isFromMkext:1; } staticFlags;
struct {
unsigned int isKernelComponent:1;
unsigned int isInterface:1;
unsigned int declaresExecutable:1;
unsigned int loggingEnabled:1;
unsigned int plistHasEnableLoggingSet:1;
unsigned int plistHasIOKitDebugFlags:1;
unsigned int isLoadableInSafeBoot:1;
unsigned int sip_protected:1;
unsigned int validated:1; unsigned int invalid:1; unsigned int valid:1;
unsigned int authenticated:1; unsigned int inauthentic:1; unsigned int authentic:1;
unsigned int hasIOKitDebugProperty:1;
unsigned int warnForMismatchedKmodInfo:1;
unsigned int isSigned:1;
} flags;
} __OSKext, * __OSKextRef;
#pragma mark Internal Constants and Enums
#define __sOSKextFullBundleExtension ".kext/"
#define __kDSStoreFilename CFSTR(".DS_Store")
#define __kOSKextKernelIdentifier CFSTR("__kernel__")
#define __kOSKextUnknownIdentifier "__unknown__"
#define __kOSKextApplePrefix CFSTR("com.apple.")
#define __kOSKextKernelLibBundleID CFSTR("com.apple.kernel")
#define __kOSKextKernelLibPrefix CFSTR("com.apple.kernel.")
#define __kOSKextKPIPrefix CFSTR("com.apple.kpi.")
#define __kOSKextCompatibilityBundleID "com.apple.kernel.6.0"
#define __kOSKextPrivateKPI CFSTR("com.apple.kpi.private")
#define __kOSKextKasanKPI CFSTR("com.apple.kpi.kasan")
#define __kOSKextSymbolFileSuffix "sym"
#define __kOSKextKmodInfoSymbol "_kmod_info"
#define __kStringUnknown "(unknown)"
#define __kOSKextMaxKextDisplacement_x86_64 (2 * 1024 * 1024 * 1024ULL)
#define __kOSKextExcludeListKey CFSTR("OSKextExcludeList")
#define __kOSKextExcludeListVersionKey CFSTR("Versions")
#define __kOSKextExcludeListAnyMatchKey CFSTR("Any_Match")
#define __kOSKextExcludeListAllMatchKey CFSTR("All_Match")
#define __kOSKextIdentifierCacheBasePathKey "OSKextIdentifierCacheBasePath"
#define __kOSKextIdentifierCacheKextInfoKey "OSKextIdentifierCacheKextInfo"
#define __kOSKextIdentifierCacheVersionKey "OSKextIdentifierCacheVersion"
#define __kOSKextIdentifierCacheCurrentVersion (1)
#ifndef kKextRequestPredicateGetLoadedByUUID
#define kKextRequestPredicateGetLoadedByUUID "Get Loaded Kext Info By UUID"
#endif
#pragma mark Module Internal Variables
const char * OSKEXT_BUILD_DATE = "OSKEXT_BUILD_DATE " __TIME__ " " __DATE__;
static pthread_once_t __sOSKextInitialized = PTHREAD_ONCE_INIT;
static Boolean __sOSKextInitializing = false;
__unused static const int g_max_align_to_4k = 0;
static CFMutableArrayRef __sOSAllKexts = NULL;
static CFMutableDictionaryRef __sOSKextsByURL = NULL;
static CFMutableDictionaryRef __sOSKextsByIdentifier = NULL;
static OSKextLogSpec __sUserLogFilter = kOSKextLogWarningLevel |
kOSKextLogVerboseFlagsMask;
static OSKextLogSpec __sKernelLogFilter = kOSKextLogWarningLevel |
kOSKextLogVerboseFlagsMask;
static const NXArchInfo __sOSKextUnknownArchInfo = {
.name = "unknown",
.cputype = CPU_TYPE_ANY,
.cpusubtype = CPU_SUBTYPE_MULTIPLE,
.byteorder = NX_UnknownByteOrder,
.description = "unknown CPU architecture",
};
static const NXArchInfo * __sOSKextArchInfo = &__sOSKextUnknownArchInfo;
static Boolean __sOSKextSimulatedSafeBoot = FALSE;
static Boolean __sOSKextUsesCaches = TRUE;
static Boolean __sOSKextStrictRecordingByLastOpened = FALSE;
static Boolean __sOSKextStrictAuthentication = FALSE;
static Boolean __sOSKextIsSIPDisabled = FALSE;
static OSKextAuthFnPtr __sOSKextAuthenticationFunction = _OSKextBasicFilesystemAuthentication;
static void * __sOSKextAuthenticationContext = NULL;
static CFArrayRef __sOSKextPackageTypeValues = NULL;
static CFArrayRef __sOSKextOSBundleRequiredValues = NULL;
static OSKextDiagnosticsFlags __sOSKextRecordsDiagnositcs = kOSKextDiagnosticsFlagNone;
static OSKextVersion __sOSNewKmodInfoKernelVersion = -1;
static char sOSKextExecutableSuffix[128];
__unused static uint64_t nSplitKexts = 0, nNonSplitKexts = 0;
enum enumSegIdx {
SEG_IDX_TEXT,
SEG_IDX_TEXT_EXEC,
SEG_IDX_DATA,
SEG_IDX_DATA_CONST,
SEG_IDX_LLVM_COV,
SEG_IDX_LINKEDIT,
SEG_IDX_COUNT,
};
void __sOSKextDefaultLogFunction(
OSKextRef aKext,
OSKextLogSpec msgLogSpec,
const char * format, ...);
void __OSKextLogKernelMessages(
OSKextRef aKext,
CFTypeRef kernelMessages);
void (*__sOSKextLogOutputFunction)(
OSKextRef aKext,
OSKextLogSpec msgLogSpec,
const char * format, ...) =
&__sOSKextDefaultLogFunction;
static const char * safe_mach_error_string(mach_error_t error_code);
#pragma mark External Variables and Constants
static CFArrayRef __sOSKextSystemExtensionsFolderURLs = NULL;
static CFArrayRef __sOSKextInfoEssentialKeys = NULL;
const char * kOSKextLoadNotification = "com.apple.kext.load";
const char * kOSKextUnloadNotification = "com.apple.kext.unload";
#pragma mark -
#pragma mark Diagnostic Keys and Values
const CFStringRef kOSKextDiagnosticsValidationKey =
CFSTR("Validation Failures");
const CFStringRef kOSKextDiagnosticsAuthenticationKey =
CFSTR("Authentication Failures");
const CFStringRef kOSKextDiagnosticsDependenciesKey =
CFSTR("Dependency Resolution Failures");
const CFStringRef kOSKextDiagnosticsWarningsKey =
CFSTR("Warnings");
const CFStringRef kOSKextDiagnosticsBootLevelKey =
CFSTR("Boot Level Restrictions");
#pragma mark Combo validation/authentication diagnostic strings
const CFStringRef kOSKextDiagnosticURLConversionKey =
CFSTR("Internal error converting URL");
const CFStringRef kOSKextDiagnosticFileNotFoundKey =
CFSTR("File not found");
const CFStringRef kOSKextDiagnosticStatFailureKey =
CFSTR("Failed to get file info (stat failed)");
const CFStringRef kOSKextDiagnosticFileAccessKey =
CFSTR("File access failure; can't open, or I/O error");
const CFStringRef kOSKextDiagnosticNotABundleKey =
CFSTR("Failed to open CFBundle (unknown error).");
const CFStringRef kOSKextDiagnosticBadPropertyListXMLKey =
CFSTR("Can't parse info dictionary XML");
const CFStringRef kOSKextDiagnosticMissingPropertyKey =
CFSTR("Info dictionary missing required property/value");
const CFStringRef kOSKextDiagnosticBadSystemPropertyKey =
CFSTR("A system kext has a property set that it shouldn't");
const CFStringRef kOSKextDiagnosticPropertyIsIllegalTypeKey =
CFSTR("Info dictionary property value is of illegal type");
const CFStringRef kOSKextDiagnosticPropertyIsIllegalValueKey =
CFSTR("Info dictionary property value is illegal");
const CFStringRef kOSKextDiagnosticIdentifierOrVersionTooLongKey =
CFSTR("CFBundleIdentifier and CFBundleVersion must be < 64 characters.");
const CFStringRef kOSKextDiagnosticExecutableMissingKey =
CFSTR("Kext has a CFBundleExecutable property but the executable can't be found");
#if SHARED_EXECUTABLE
const CFStringRef kOSKextDiagnosticSharedExecutableKextMissingKey =
CFSTR("Kext claims a shared executable with named kext, "
"but that kext can't be found");
const CFStringRef kOSKextDiagnosticSharedExecutableAndExecutableKey =
CFSTR("Kext declares both CFBundleExecutable and "
"CFBundleSharedExecutableIdentifier; use only one.");
#endif
const CFStringRef kOSKextDiagnosticCompatibleVersionLaterThanVersionKey =
CFSTR("Compatible version must be lower than current version.");
const CFStringRef kOSKextDiagnosticExecutableBadKey =
CFSTR("Executable file doesn't contain kernel extension code "
"(no kmod_info symbol or bad Mach-O layout).");
const CFStringRef kOSKextDiagnosticNoFileKey =
CFSTR("Kext was not created from an URL and can't be authenticated");
const CFStringRef kOSKextDiagnosticOwnerPermissionKey =
CFSTR("File owner/permissions are incorrect "
"(must be root:wheel, nonwritable by group/other)");
const CFStringRef kOSKextDiagnosticTypeWarningKey =
CFSTR("Info dictionary property value is of incorrect type");
const CFStringRef kOSKextDiagnosticKernelComponentNotInterfaceKey =
CFSTR("Kext is a kernel component but OSBundleIsInterface "
"is set to false; overriding");
const CFStringRef kOSKextDiagnosticExecutableArchNotFoundKey =
CFSTR("Executable does not contain code for architecture");
const CFStringRef kOSKextDiagnosticSymlinkKey =
CFSTR("The booter does not recognize symbolic links; "
"confirm these files/directories aren't needed for startup");
const CFStringRef kOSKextDiagnosticDeprecatedPropertyKey =
CFSTR("Deprecated property (ignored)");
const CFStringRef kOSKextDiagnosticPersonalityHasNoBundleIdentifierKey =
CFSTR("Personality has no CFBundleIdentifier; "
"the kext's identifier will be inserted when sending to the IOCatalogue");
const CFStringRef kOSKextDiagnosticPersonalityNamesUnknownKextKey =
CFSTR("Personality CFBundleIdentifier names a kext that "
"can't be found");
const CFStringRef kOSKextDiagnosticPersonalityNamesNonloadableKextKey =
CFSTR("Personality CFBundleIdentifier names a kext that "
"is not loadable (run kextutil(8) on it with -nt for more information)");
const CFStringRef kOSKextDiagnosticPersonalityNamesKextWithNoExecutableKey =
CFSTR("Personality CFBundleIdentifier names a kext that "
"doesn't declare an executable");
const CFStringRef kOSKextDiagnosticPersonalityHasDifferentBundleIdentifierKey =
CFSTR("Personality CFBundleIdentifier differs from "
"containing kext's (not necessarily a mistake, but rarely done)");
const CFStringRef kOSKextDiagnosticNonuniqueIOResourcesMatchKey =
CFSTR("Personality matches on IOResources "
"but IOMatchCategory is missing or not equal to its IOClass; "
"driver may be blocked from matching or may block others");
const CFStringRef kOSKextDiagnosticCodelessWithLibrariesKey =
CFSTR("Kext has no executable or compatible version, "
"so it should not declare any OSBundleLibraries.");
const CFStringRef kOSKextDiagnosticNoExplicitKernelDependencyKey =
CFSTR("Kext declares no kernel/KPI libraries; "
"if it references any kernel symbols, it may fail to link.");
const CFStringRef kOSKextDiagnosticDeclaresNoKPIsWarningKey =
CFSTR("Kext declares no com.apple.kpi.* libraries; "
"if it references any kernel symbols, it may fail to link.");
const CFStringRef kOSKextDiagnosticDeclaresBothKernelAndKPIDependenciesKey =
CFSTR("Kexts should declare dependencies on either "
"com.apple.kernel* or com.apple.kpi.* libraries, not both.");
const CFStringRef kOSKextDiagnosticBundleIdentifierMismatchKey =
CFSTR("Kexts with a kernel library < v6.0 must set MODULE_NAME "
"the same as CFBundleIdentifier to load on kernel < v6.0.");
const CFStringRef kOSKextDiagnosticBundleVersionMismatchKey =
CFSTR("Kexts with a kernel library < v6.0 must set MODULE_VERSION "
"the same as CFBundleVersion to load on kernel < v6.0.");
const CFStringRef kOSKextDiagnosticsDependencyNotOSBundleRequired =
CFSTR("Dependency lacks appropriate value for OSBundleRequired "
"and may not be available during early boot");
const CFStringRef kOSKextDiagnosticNotSignedKey =
CFSTR("Kext is not signed");
const CFStringRef kOSKextDependencyUnavailable =
CFSTR("No kexts found for these libraries");
const CFStringRef kOSKextDependencyNoCompatibleVersion =
CFSTR("Only incompatible kexts found for these libraries");
const CFStringRef kOSKextDependencyCompatibleVersionUndeclared =
CFSTR("Kexts found for these libraries lack valid "
"OSBundleCompatibleVersion");
const CFStringRef kOSKextDependencyLoadedIsIncompatible =
CFSTR("Kexts already loaded for these libraries "
"are not compatible with the requested version");
const CFStringRef kOSKextDependencyLoadedCompatibleVersionUndeclared =
CFSTR("Kexts already loaded for these libraries "
"have no OSBundleCompatibleVersion");
const CFStringRef kOSKextDependencyIndirectDependencyUnresolvable =
CFSTR("Indirect dependencies can't be resolved");
const CFStringRef kOSKextDependencyMultipleVersionsDetected =
CFSTR("Multiple kexts for these libraries "
"occur in the dependency graph");
const CFStringRef kOSKextDependencyCircularReference =
CFSTR("Some dependencies are causing circular references");
const CFStringRef kOSKextDependencyRawAndComponentKernel =
CFSTR("Libraries declared for both "
"com.apple.kernel and a com.apple.kernel.* component.");
const CFStringRef kOSKextDependencyInvalid =
CFSTR("Dependencies have validation problems");
const CFStringRef kOSKextDependencyInauthentic =
CFSTR("Dependencies have incorrect owner/permissions");
const CFStringRef kOSKextDiagnosticDeclaresNonKPIDependenciesKey =
CFSTR("64-bit kexts must use com.apple.kpi.* libraries, "
"not com.apple.kernel* libraries.");
const CFStringRef kOSKextDiagnosticNonAppleKextDeclaresPrivateKPIDependencyKey =
CFSTR("Only Apple kexts may link against com.apple.kpi.private.");
const CFStringRef kOSKextDiagnosticRawKernelDependency =
CFSTR("Kexts may not link against com.apple.kernel; "
"use either com.apple.kpi.* libraries (recommended), "
"or com.apple.kernel.* (for compatiblity with older releases).");
const CFStringRef kOSKextDiagnosticsInterfaceDependencyCount =
CFSTR("Interface kext must have exactly one dependency.");
const CFStringRef kOSKextDiagnosticIneligibleInSafeBoot =
CFSTR("Kext isn't loadable during safe boot.");
const CFStringRef kOSKextDependencyIneligibleInSafeBoot =
CFSTR("Dependencies aren't loadable during safe boot");
#pragma mark General Private Function Declarations
typedef struct SegInfo {
uint64_t vmaddr;
uint64_t vmsize;
uint64_t fileoff;
} SegInfo;
typedef struct plkSegInfo {
SegInfo plkSegInfo;
const char *plk_seg_name;
uint64_t plk_next_kext_vmaddr;
} plkSegInfo;
typedef struct plkInfo {
CFDataRef kernelImage;
CFMutableDataRef kernelCacheImage;
CFMutableSetRef kaslrOffsets;
SegInfo kernel_TEXT;
plkSegInfo plk_TEXT;
plkSegInfo plk_TEXT_EXEC;
plkSegInfo plk_DATA;
plkSegInfo plk_DATA_CONST;
plkSegInfo plk_LINKEDIT;
plkSegInfo plk_LLVM_COV;
SegInfo plk_INFO;
} plkInfo;
__unused static void __OSKextPackKASLROffsets(
CFNumberRef value,
void *context);
__unused static uint32_t kextcacheFileOffsetToPLKTEXTOffset(
uint64_t kcOffset,
plkInfo * plkInfo);
static void __OSKextInitialize(void);
static OSKextRef __OSKextAlloc(
CFAllocatorRef allocator,
CFAllocatorContext * context);
static void __OSKextReleaseContents(CFTypeRef cf);
static CFStringRef __OSKextCopyDebugDescription(CFTypeRef cf);
Boolean __OSKextReadRegistryNumberProperty(
io_registry_entry_t ioObject,
CFStringRef key,
CFNumberType numberType,
void * valuePtr);
static Boolean __OSKextIsArchitectureLP64(void);
static Boolean __OSKextInitWithURL(
OSKextRef aKext,
CFURLRef anURL);
static Boolean __OSKextInitFromMkext(
OSKextRef aKext,
CFDictionaryRef infoDict,
CFURLRef mkextURL,
CFDataRef mkextData);
static void __OSKextReinit(OSKextRef aKext);
static Boolean __OSKextRecordKext(OSKextRef aKext);
static void __OSKextRemoveKext(OSKextRef aKext);
static Boolean __OSKextRecordKextInIdentifierDict(
OSKextRef aKext,
CFMutableDictionaryRef identifierDict);
static void __OSKextRemoveKextFromIdentifierDict(
OSKextRef aKext,
CFMutableDictionaryRef identifierDict);
static CFMutableArrayRef __OSKextCreateKextsFromURL(
CFAllocatorRef allocator,
CFURLRef anURL,
OSKextRef aKext, Boolean createPluginsFlag);
static CFMutableArrayRef __OSKextCreateKextsFromURLs(
CFAllocatorRef allocator,
CFArrayRef arrayOfURLs,
Boolean createPluginsFlag);
OSKextRef __OSKextCreateFromIdentifierCacheDict(
CFAllocatorRef allocator,
CFDictionaryRef cacheDict,
CFStringRef basePath,
CFIndex entryIndex);
void __OSKextRealize(const void * vKext, void * context __unused);
void __OSKextRealizeKextsWithIdentifier(CFStringRef kextIdentifier);
CFURLRef __OSKextCreateCacheFileURL(
CFTypeRef folderURLsOrURL,
CFStringRef cacheName,
const NXArchInfo * arch,
_OSKextCacheFormat format);
Boolean __OSKextCacheNeedsUpdate(
CFURLRef cacheURL,
CFTypeRef folderURLsOrURL);
Boolean _OSKextCreateFolderForCacheURL(CFURLRef cacheURL);
Boolean __OSKextURLIsSystemFolder(CFURLRef absURL);
Boolean __OSKextStatURL(
CFURLRef anURL,
Boolean * missingOut,
struct stat * statOut);
Boolean __OSKextStatURLsOrURL(
CFTypeRef folderURLsOrURL,
Boolean * missingOut,
struct stat * latestStatOut);
void __OSKextRemoveIdentifierCacheForKext(OSKextRef aKext);
CFDictionaryRef __OSKextCreateIdentifierCacheDict(
OSKextRef aKext,
CFStringRef basePath);
static Boolean __OSKextGetFileSystemPath(
OSKextRef aKext,
CFURLRef anURL,
Boolean resolveToBase,
char * pathBuffer);
CFStringRef __OSKextCreateCompositeKey(
CFStringRef baseKey,
const char * auxKey);
static CFTypeRef __CFDictionaryGetValueForCompositeKey(
CFDictionaryRef aDict,
CFStringRef baseKey,
const char * auxKey);
static Boolean __OSKextReadExecutable(OSKextRef aKext);
static Boolean __OSKextHasAllDependencies(OSKextRef aKext);
static Boolean __OSKextResolveDependencies(
OSKextRef aKext,
OSKextRef rootKext,
CFMutableSetRef resolvedSet,
CFMutableArrayRef loopStack);
static Boolean __OSKextAddLinkDependencies(
OSKextRef aKext,
CFMutableArrayRef linkDependencies,
Boolean needAllFlag,
Boolean bleedthroughFlag);
static Boolean __OSKextReadSymbolReferences(
OSKextRef aKext,
CFMutableDictionaryRef symbols);
static Boolean __OSKextIsSearchableForSymbols(
OSKextRef aKext,
Boolean nonKPIFlag,
Boolean allowUnsupportedFlag);
static Boolean __OSKextFindSymbols(
OSKextRef aKext,
CFMutableDictionaryRef undefSymbols,
CFMutableDictionaryRef onedefSymbols,
CFMutableDictionaryRef multdefSymbols,
CFMutableArrayRef multdefLibs);
static CFMutableArrayRef __OSKextCopyDependenciesList(
OSKextRef aKext,
Boolean needAllFlag,
uint32_t minDepth);
CFMutableDictionaryRef __OSKextCreateKextRequest(
CFStringRef predicateIn,
CFTypeRef bundleIdentifierIn,
CFMutableDictionaryRef * argumentsOut);
OSReturn __OSKextSendKextRequest(
OSKextRef aKext,
CFDictionaryRef kextRequest,
CFTypeRef * cfResponseOut,
char ** rawResponseOut,
uint32_t * rawResponseLengthOut);
static OSReturn __OSKextSimpleKextRequest(
OSKextRef aKext,
CFStringRef predicate,
CFTypeRef * cfResponseOut);
OSReturn __OSKextProcessKextRequestResults(
OSKextRef aKext,
kern_return_t mig_result,
kern_return_t op_result,
char * logInfoBuffer,
uint32_t logInfoLength);
static OSReturn __OSKextLoadWithArgsDict(
OSKextRef aKext,
CFDictionaryRef loadArgsDict);
#ifndef IOKIT_EMBEDDED
typedef struct __OSKextKXLDCallbackContext {
OSKextRef kext;
uint64_t kernelLoadAddress;
} __OSKextKXLDCallbackContext;
static Boolean __OSKextGenerateDebugSymbols(
OSKextRef aKext,
CFDataRef kernelImage,
uint64_t kernelLoadAddress,
KXLDContext * kxldContext,
CFMutableDictionaryRef symbols);
static kxld_addr_t __OSKextLinkAddressCallback(
u_long size,
KXLDAllocateFlags * flags,
void * user_data);
static void __OSKextLoggingCallback(
KXLDLogSubsystem subsystem,
KXLDLogLevel level,
const char * format,
va_list argList,
void * user_data);
#endif
OSReturn __OSKextRemovePersonalities(
OSKextRef aKext,
CFStringRef aBundleID);
static void __OSKextProcessLoadInfo(
const void * vKey __unused,
const void * vValue,
void * vContext __unused);
static void __OSKextCheckLoaded(OSKextRef aKext);
Boolean __OSKextSetLoadAddress(OSKextRef aKext, uint64_t address);
static Boolean __OSKextCreateLoadInfo(OSKextRef aKext);
static Boolean __OSKextCreateMkextInfo(OSKextRef aKext);
static Boolean __OSKextIsValid(OSKextRef aKext);
static Boolean __OSKextValidate(OSKextRef aKext, CFMutableArrayRef propPath);
static Boolean __OSKextValidateExecutable(OSKextRef aKext);
static Boolean __OSKextBasicFilesystemAuthenticationRecursive(
OSKextRef aKext,
CFURLRef anURL,
CFURLRef pluginsURL);
static CFDictionaryRef __OSKextCopyDiagnosticsDict(
OSKextRef aKext,
OSKextDiagnosticsFlags type);
static CFMutableDictionaryRef __OSKextGetDiagnostics(OSKextRef aKext,
OSKextDiagnosticsFlags type);
static void __OSKextSetDiagnostic(OSKextRef aKext,
OSKextDiagnosticsFlags type, CFStringRef key);
static void __OSKextAddDiagnostic(OSKextRef aKext,
OSKextDiagnosticsFlags type,
CFStringRef key,
CFTypeRef value,
CFTypeRef note);
static Boolean __OSKextCheckProperty(
OSKextRef aKext,
CFDictionaryRef aDict,
CFTypeRef propKey,
CFTypeRef diagnosticValue,
CFTypeID expectedType,
CFArrayRef legalValues,
Boolean required,
Boolean typeRequired,
Boolean nonnilRequired,
CFTypeRef * valueOut,
Boolean * valueIsNonnil);
static Boolean __OSKextReadInfoDictionary(
OSKextRef aKext, CFBundleRef kextBundle);
static Boolean __OSKextProcessInfoDictionary(
OSKextRef aKext, CFBundleRef kextBundle);
static Boolean __OSKextAddCompressedFileToMkext(
OSKextRef aKext,
CFMutableDataRef mkextData,
CFDataRef fileData,
Boolean plistFlag,
Boolean * compressed);
static Boolean __OSKextAddToMkext(
OSKextRef aKext,
CFMutableDataRef mkextData,
CFMutableArrayRef mkextInfoDictArray,
char * volumePath,
Boolean compressFlag);
static CFDataRef __OSKextCreateMkext(
CFAllocatorRef allocator,
CFArrayRef kextArray,
CFURLRef volumeRootURL,
OSKextRequiredFlags requiredFlags,
Boolean compressFlag,
Boolean skipLoadedFlag,
CFDictionaryRef loadArgsDict);
static CFDataRef __OSKextUncompressMkext2FileData(
CFAllocatorRef allocator,
const UInt8 * buffer,
uint32_t compressedSize,
uint32_t fullSize);
static CFArrayRef __OSKextCreateKextsFromMkext(
CFAllocatorRef allocator,
CFDataRef mkextData,
CFURLRef mkextURL);
static CFDataRef __OSKextExtractMkext2FileEntry(
OSKextRef aKext,
CFDataRef mkextData,
CFNumberRef offsetNum,
CFStringRef filename);
#ifndef IOKIT_EMBEDDED
static boolean_t __OSKextSwapHeaders(
CFDataRef kernelImage);
static boolean_t __OSKextUnswapHeaders(
CFDataRef kernelImage);
static boolean_t __OSKextGetLastKernelLoadAddr(
CFDataRef kernelImage,
uint64_t *lastLoadAddrOut);
#if SPLIT_KEXTS_DEBUG
static void __OSKextShowMachoHeader(const UInt8 * imagePtr, CFIndex imageSize);
#endif
static const char * getSegmentCommandName(uint32_t theSegCommand);
static void __OSKextShowPLKInfo(plkInfo *info);
static Boolean __OSKextIsSplitKext(OSKextRef aKext);
static Boolean __OSKextIsSplitKextMacho64(struct mach_header_64 *kextHeader);
static Boolean __OSKextConvertExeOfstsToLinkedOfsts(OSKextRef aKext);
static boolean_t __OSKextHandlePointersForASLR(OSKextRef aKext,
plkInfo *plkInfo,
u_char *prelinkData,
uint64_t segKCOffsets[SEG_IDX_COUNT],
uintptr_t *pointersForASLR,
size_t numPointers);
static boolean_t __OSKextCompactKext(OSKextRef aKext);
static boolean_t __OSKextCopyToPLK(
OSKextRef aKext,
plkInfo *plkInfo,
u_char *prelinkData,
uint64_t prelinkDataSize,
uint64_t *in_orig_vmaddrs,
uint64_t *in_new_vmaddrs,
uint64_t *in_kc_offs,
uint64_t *out_sizes,
Boolean stripSymbolsFlag);
static boolean_t __OSKextInit_plkInfo(
CFDataRef kernelImage,
CFArrayRef kextArray,
plkInfo *plkInfo);
static boolean_t __OSKextGetPLKSegSizes(
CFArrayRef kextArray,
plkInfo * segInfo);
static boolean_t __OSKextSetPLKSegInfo(
plkInfo *plkInfo);
static uint32_t __OSKextGetSegMaxAlignment(
struct segment_command_64 * seg_cmd);
static uint64_t __OSKextAlignAddress(
uint64_t address,
uint32_t align);
static boolean_t __OSKextSetLinkInfo(
OSKextRef aKext,
plkInfo * plkInfo,
CFDataRef kextExecutable);
static boolean_t __OSKextValidatePLKInfo(
OSKextRef aKext,
struct mach_header_64 *kextHeader,
plkInfo * plkInfo);
static boolean_t __OSKextGetSegmentInfoForOffset(
const UInt8 *imagePtr,
uint64_t fileOffset,
char segnameOut[MAX_SEGNAME_LEN],
uint64_t *segOffsetOut,
uint64_t *vmaddrOut,
uint64_t *vmsizeOut,
uint64_t *fileoffOut,
uint64_t *filesizeOut);
static boolean_t __OSKextGetSegmentInfo(
const UInt8 * imagePtr,
const char * segname,
uint64_t * vmaddrOut,
uint64_t * vmsizeOut,
uint64_t * fileoffOut,
uint64_t * filesizeOut,
uint64_t * maxAlign,
boolean_t truncateSegs);
static boolean_t __OSKextGetSegmentAddressAndOffsetDataRef(
CFDataRef imageRef,
const char * segname,
uint32_t * fileOffsetOut,
uint64_t * loadAddrOut);
static boolean_t __OSKextGetSegmentAddressAndOffset(
const UInt8 * imagePtr,
const char * segname,
uint32_t * fileOffsetOut,
uint64_t * loadAddrOut);
static boolean_t __OSKextGetSegmentFileAndVMSizeDataRef(
CFDataRef imageRef,
const char * segname,
uint64_t * fileSizeOut,
uint64_t * VMSizeOut);
static boolean_t __OSKextGetSegmentFileAndVMSize(
const UInt8 * imagePtr,
const char * segname,
uint64_t * fileSizeOut,
uint64_t * VMSizeOut);
static boolean_t __OSKextSetSegmentAddress(
CFDataRef kernelImage,
const char *segname,
uint64_t loadAddr);
static boolean_t __OSKextSetSegmentVMSize(
CFDataRef kernelImage,
const char *segname,
uint64_t vmsize);
static boolean_t __OSKextSetSegmentOffset(
CFDataRef kernelImage,
const char *segname,
uint64_t fileOffset);
static boolean_t __OSKextSetSegmentFilesize(
CFDataRef kernelImage,
const char *segname,
uint64_t filesize);
static boolean_t __OSKextSetSegmentProtection(
CFDataRef kernelImage,
const char *segname,
vm_prot_t initprot,
vm_prot_t maxprot);
static boolean_t __OSKextMachOSetSegmentProtection(
void *header,
const char *segname,
vm_prot_t initprot,
vm_prot_t maxprot);
static boolean_t __OSKextSetSectionAddress(
CFDataRef kernelImage,
const char *segname,
const char *sectname,
uint64_t loadAddr);
static boolean_t __OSKextSetSectionSize(
CFDataRef kernelImage,
const char *segname,
const char *sectname,
uint64_t size);
static boolean_t __OSKextSetSectionOffset(
CFDataRef kernelImage,
const char *segname,
const char *sectname,
uint32_t fileOffset);
static uint64_t __OSKextGetFakeLoadAddress(CFDataRef kernelImage);
static Boolean __OSKextCheckForPrelinkedKernel(
OSKextRef aKext,
Boolean needAllFlag,
Boolean skipAuthenticationFlag,
Boolean printDiagnosticsFlag);
static void __OSKextRemovePLKLinkedit(plkInfo *plkInfo);
static CFArrayRef __OSKextPrelinkSplitKexts(
CFArrayRef kextArray,
plkInfo * plkInfo,
KXLDContext * kxldContext,
Boolean needAllFlag,
Boolean skipAuthenticationFlag,
Boolean printDiagnosticsFlag,
Boolean stripSymbolsFlag);
static CFArrayRef __OSKextPrelinkKexts(
CFArrayRef kextArray,
CFDataRef kernelImage,
uint64_t loadAddrBase,
uint64_t sourceAddrBase,
KXLDContext * kxldContext,
u_long * loadSizeOut,
Boolean needAllFlag,
Boolean skipAuthenticationFlag,
Boolean printDiagnosticsFlag,
Boolean stripSymbolsFlag);
static CFDataRef __OSKextCreatePrelinkInfoDictionary(
plkInfo *plkInfo,
CFArrayRef loadList,
CFURLRef volumeRootURL,
Boolean includeAllPersonalities,
Boolean isSplitKexts,
CFDataRef kernelUUID);
static Boolean __OSKextRequiredAtEarlyBoot(
OSKextRef theKext);
static Boolean __OSKextCopySplitPrelinkInfoDictionary(
plkInfo * plkInfo,
CFDataRef prelinkInfoData);
static u_long __OSKextCopyPrelinkInfoDictionary(
CFMutableDataRef prelinkImage,
CFDataRef prelinkInfoData,
u_long fileOffset,
uint64_t sourceAddr);
static u_long __OSKextCopyPrelinkedKexts(
CFMutableDataRef prelinkImage,
CFArrayRef loadList,
u_long fileOffsetBase,
uint64_t sourceAddrBase);
static Boolean _excludeThisVersion(OSKextVersion theKextVers,
CFStringRef theExcludedKextVers);
#endif
CFComparisonResult __OSKextCompareIdentifiers(
const void * val1,
const void * val2,
void * context);
char * __absPathOnVolume(
const char * path,
const char * volumePath);
CFStringRef __OSKextCopyExecutableRelativePath(OSKextRef aKext);
static bool __OSKextShouldLog(
OSKextRef aKext,
OSKextLogSpec msgLogSpec);
Boolean _isArray(CFTypeRef);
Boolean _isDictionary(CFTypeRef);
Boolean _isString(CFTypeRef);
__unused static boolean_t __OSKextWantKextsFirst(CFDataRef kernelImage);
#pragma mark Function-Like Macros
#pragma mark Core Foundation Class Functions
static CFTypeID __kOSKextTypeID = _kCFRuntimeNotATypeID;
CFTypeID OSKextGetTypeID(void) {
return __kOSKextTypeID;
}
static const CFRuntimeClass __OSKextClass = {
0, "OSKext", NULL, NULL, __OSKextReleaseContents, NULL, NULL, NULL, __OSKextCopyDebugDescription, #if CF_RECLAIM_AVAILABLE
NULL, #endif
#if CF_REFCOUNT_AVAILABLE
NULL
#endif
};
struct max_data {
int n;
struct {
char name[MAX_SEGNAME_LEN];
uint64_t vmaddr;
uint64_t vmsz;
uint64_t max_off;
} seg_data[SEG_IDX_COUNT];
};
static uint64_t getKCPlkSegVMSize(plkInfo *plkInfo, enum enumSegIdx idx) {
assert(plkInfo);
switch (idx) {
case SEG_IDX_TEXT:
return plkInfo->plk_TEXT.plkSegInfo.vmsize;
case SEG_IDX_TEXT_EXEC:
return plkInfo->plk_TEXT_EXEC.plkSegInfo.vmsize;
case SEG_IDX_DATA:
return plkInfo->plk_DATA.plkSegInfo.vmsize;
case SEG_IDX_DATA_CONST:
return plkInfo->plk_DATA_CONST.plkSegInfo.vmsize;
case SEG_IDX_LINKEDIT:
return plkInfo->plk_LINKEDIT.plkSegInfo.vmsize;
case SEG_IDX_LLVM_COV:
return plkInfo->plk_LLVM_COV.plkSegInfo.vmsize;
default:
assert(false);
return 0;
}
}
__unused static boolean_t setKCPlkSegVMSize(plkInfo *plkInfo, enum enumSegIdx idx, uint64_t x) {
assert(plkInfo);
switch (idx) {
case SEG_IDX_TEXT:
plkInfo->plk_TEXT.plkSegInfo.vmsize = x;
break;
case SEG_IDX_TEXT_EXEC:
plkInfo->plk_TEXT_EXEC.plkSegInfo.vmsize = x;
break;
case SEG_IDX_DATA:
plkInfo->plk_DATA.plkSegInfo.vmsize = x;
break;
case SEG_IDX_DATA_CONST:
plkInfo->plk_DATA_CONST.plkSegInfo.vmsize = x;
break;
case SEG_IDX_LINKEDIT:
plkInfo->plk_LINKEDIT.plkSegInfo.vmsize = x;
break;
case SEG_IDX_LLVM_COV:
plkInfo->plk_LLVM_COV.plkSegInfo.vmsize = x;
break;
default:
assert(false);
return false;
}
return true;
}
static uint64_t getKCPlkSegVMAddr(plkInfo *plkInfo, enum enumSegIdx idx) {
assert(plkInfo);
switch (idx) {
case SEG_IDX_TEXT:
return plkInfo->plk_TEXT.plkSegInfo.vmaddr;
case SEG_IDX_TEXT_EXEC:
return plkInfo->plk_TEXT_EXEC.plkSegInfo.vmaddr;
case SEG_IDX_DATA:
return plkInfo->plk_DATA.plkSegInfo.vmaddr;
case SEG_IDX_DATA_CONST:
return plkInfo->plk_DATA_CONST.plkSegInfo.vmaddr;
case SEG_IDX_LINKEDIT:
return plkInfo->plk_LINKEDIT.plkSegInfo.vmaddr;
case SEG_IDX_LLVM_COV:
return plkInfo->plk_LLVM_COV.plkSegInfo.vmaddr;
default:
assert(false);
return false;
}
}
static uint64_t getKCPlkSegFileOff(plkInfo *plkInfo, enum enumSegIdx idx) {
assert(plkInfo);
switch (idx) {
case SEG_IDX_TEXT:
return plkInfo->plk_TEXT.plkSegInfo.fileoff;
case SEG_IDX_TEXT_EXEC:
return plkInfo->plk_TEXT_EXEC.plkSegInfo.fileoff;
case SEG_IDX_DATA:
return plkInfo->plk_DATA.plkSegInfo.fileoff;
case SEG_IDX_DATA_CONST:
return plkInfo->plk_DATA_CONST.plkSegInfo.fileoff;
case SEG_IDX_LINKEDIT:
return plkInfo->plk_LINKEDIT.plkSegInfo.fileoff;
case SEG_IDX_LLVM_COV:
return plkInfo->plk_LLVM_COV.plkSegInfo.fileoff;
default:
assert(false);
return false;
}
}
static uint64_t getKCPlkSegNextVMAddr(plkInfo *plkInfo, enum enumSegIdx idx) {
assert(plkInfo);
switch (idx) {
case SEG_IDX_TEXT:
return plkInfo->plk_TEXT.plk_next_kext_vmaddr;
case SEG_IDX_TEXT_EXEC:
return plkInfo->plk_TEXT_EXEC.plk_next_kext_vmaddr;
case SEG_IDX_DATA:
return plkInfo->plk_DATA.plk_next_kext_vmaddr;
case SEG_IDX_DATA_CONST:
return plkInfo->plk_DATA_CONST.plk_next_kext_vmaddr;
case SEG_IDX_LINKEDIT:
return plkInfo->plk_LINKEDIT.plk_next_kext_vmaddr;
case SEG_IDX_LLVM_COV:
return plkInfo->plk_LLVM_COV.plk_next_kext_vmaddr;
default:
assert(false);
return 0;
}
}
__unused static boolean_t setKCPlkSegNextVMAddr(plkInfo *plkInfo, enum enumSegIdx idx, uint64_t x) {
if (!plkInfo)
return false;
switch (idx) {
case SEG_IDX_TEXT:
plkInfo->plk_TEXT.plk_next_kext_vmaddr = x;
return true;
case SEG_IDX_TEXT_EXEC:
plkInfo->plk_TEXT_EXEC.plk_next_kext_vmaddr = x;
return true;
case SEG_IDX_DATA:
plkInfo->plk_DATA.plk_next_kext_vmaddr = x;
return true;
case SEG_IDX_DATA_CONST:
plkInfo->plk_DATA_CONST.plk_next_kext_vmaddr = x;
return true;
case SEG_IDX_LINKEDIT:
plkInfo->plk_LINKEDIT.plk_next_kext_vmaddr = x;
return true;
case SEG_IDX_LLVM_COV:
plkInfo->plk_LLVM_COV.plk_next_kext_vmaddr = x;
return true;
default:
assert(false);
return false;
}
}
__unused static boolean_t setKextVMAddr(OSKextRef aKext, enum enumSegIdx idx, uint64_t vmaddr) {
if (!aKext)
return false;
switch (idx) {
case SEG_IDX_TEXT:
aKext->loadInfo->linkInfo.vmaddr_TEXT = vmaddr;
return true;
case SEG_IDX_TEXT_EXEC:
aKext->loadInfo->linkInfo.vmaddr_TEXT_EXEC = vmaddr;
return true;
case SEG_IDX_DATA:
aKext->loadInfo->linkInfo.vmaddr_DATA = vmaddr;
return true;
case SEG_IDX_DATA_CONST:
aKext->loadInfo->linkInfo.vmaddr_DATA_CONST = vmaddr;
return true;
case SEG_IDX_LINKEDIT:
aKext->loadInfo->linkInfo.vmaddr_LINKEDIT = vmaddr;
return true;
case SEG_IDX_LLVM_COV:
aKext->loadInfo->linkInfo.vmaddr_LLVM_COV = vmaddr;
return true;
default:
assert(false);
return false;
}
}
__unused static uint64_t getKextVMAddr(OSKextRef aKext, enum enumSegIdx idx) {
assert(aKext);
switch (idx) {
case SEG_IDX_TEXT:
return aKext->loadInfo->linkInfo.vmaddr_TEXT;
case SEG_IDX_TEXT_EXEC:
return aKext->loadInfo->linkInfo.vmaddr_TEXT_EXEC;
case SEG_IDX_DATA:
return aKext->loadInfo->linkInfo.vmaddr_DATA;
case SEG_IDX_DATA_CONST:
return aKext->loadInfo->linkInfo.vmaddr_DATA_CONST;
case SEG_IDX_LINKEDIT:
return aKext->loadInfo->linkInfo.vmaddr_LINKEDIT;
case SEG_IDX_LLVM_COV:
return aKext->loadInfo->linkInfo.vmaddr_LLVM_COV;
default:
assert(false);
return 0;
}
}
__unused static boolean_t getSegIndex(const char *name, enum enumSegIdx *idx) {
if (!strcmp(name, "__TEXT")) {
if (idx)
*idx = SEG_IDX_TEXT;
return true;
} else if (!strcmp(name, "__TEXT_EXEC")) {
if (idx)
*idx = SEG_IDX_TEXT_EXEC;
return true;
} else if (!strcmp(name, "__DATA")) {
if (idx)
*idx = SEG_IDX_DATA;
return true;
} else if (!strcmp(name, "__DATA_CONST")) {
if (idx)
*idx = SEG_IDX_DATA_CONST;
return true;
} else if (!strcmp(name, "__LINKEDIT")) {
if (idx)
*idx = SEG_IDX_LINKEDIT;
return true;
} else if (!strcmp(name, "__LLVM_COV")) {
if (idx)
*idx = SEG_IDX_LLVM_COV;
return true;
} else {
return false;
}
}
static char * segIdxToName(enum enumSegIdx idx) {
switch (idx) {
case SEG_IDX_TEXT:
return "__TEXT";
case SEG_IDX_TEXT_EXEC:
return "__TEXT_EXEC";
case SEG_IDX_DATA:
return "__DATA";
case SEG_IDX_DATA_CONST:
return "__DATA_CONST";
case SEG_IDX_LINKEDIT:
return "__LINKEDIT";
case SEG_IDX_LLVM_COV:
return "__LLVM_COV";
default:
return NULL;
}
}
static void __OSKextInitialize(void)
{
CFTypeRef packageTypeValues[] = { CFSTR("KEXT") };
CFTypeRef bundleRequiredValues[] = {
CFSTR(kOSBundleRequiredRoot),
CFSTR(kOSBundleRequiredLocalRoot),
CFSTR(kOSBundleRequiredNetworkRoot),
CFSTR(kOSBundleRequiredSafeBoot),
CFSTR(kOSBundleRequiredConsole),
};
CFTypeRef essentialInfoKeys[] = {
kCFBundleIdentifierKey,
kCFBundleVersionKey,
CFSTR(kOSBundleCompatibleVersionKey),
CFSTR(kOSBundleIsInterfaceKey),
CFSTR(kOSKernelResourceKey),
CFSTR(kOSBundleCPUTypeKey),
CFSTR(kOSBundleCPUSubtypeKey),
CFSTR(kOSBundlePathKey),
CFSTR(kOSBundleUUIDKey),
CFSTR(kOSBundleStartedKey),
CFSTR(kOSBundleLoadTagKey),
CFSTR(kOSBundleLoadAddressKey),
CFSTR(kOSBundleLoadSizeKey),
CFSTR(kOSBundleWiredSizeKey),
CFSTR(kOSBundlePrelinkedKey),
CFSTR(kOSBundleDependenciesKey),
CFSTR(kOSBundleRetainCountKey)
};
CFAllocatorContext nonrefcountAllocatorContext;
CFAllocatorRef nonrefcountAllocator = NULL;
CFURLRef extensionsDirs[_kOSKextNumSystemExtensionsFolders] = { 0, 0 };
int numValues;
__sOSKextInitializing = true;
if (csr_check(CSR_ALLOW_UNTRUSTED_KEXTS) == 0) {
__sOSKextIsSIPDisabled = TRUE;
}
__kOSKextTypeID = _CFRuntimeRegisterClass(&__OSKextClass);
CFAllocatorGetContext(kCFAllocatorDefault, &nonrefcountAllocatorContext);
nonrefcountAllocatorContext.retain = NULL;
nonrefcountAllocatorContext.release = NULL;
nonrefcountAllocator = CFAllocatorCreate(kCFAllocatorDefault,
&nonrefcountAllocatorContext);
if (!nonrefcountAllocator) {
OSKextLogMemError();
goto finish;
}
extensionsDirs[0] = CFURLCreateFromFileSystemRepresentation(
nonrefcountAllocator,
(const UInt8 *)_kOSKextSystemLibraryExtensionsFolder,
strlen(_kOSKextSystemLibraryExtensionsFolder),
true);
extensionsDirs[1] = CFURLCreateFromFileSystemRepresentation(
nonrefcountAllocator,
(const UInt8 *)_kOSKextLibraryExtensionsFolder,
strlen(_kOSKextLibraryExtensionsFolder),
true);
__sOSKextSystemExtensionsFolderURLs = CFArrayCreate(
nonrefcountAllocator,
(const void **)extensionsDirs,
_kOSKextNumSystemExtensionsFolders,
&kCFTypeArrayCallBacks);
if (!extensionsDirs[0] ||
!extensionsDirs[1] ||
!__sOSKextSystemExtensionsFolderURLs) {
OSKextLogMemError();
goto finish;
}
numValues = sizeof(packageTypeValues) / sizeof(void *);
__sOSKextPackageTypeValues = CFArrayCreate(kCFAllocatorDefault,
packageTypeValues, numValues, &kCFTypeArrayCallBacks);
numValues = sizeof(bundleRequiredValues) / sizeof(void *);
__sOSKextOSBundleRequiredValues = CFArrayCreate(kCFAllocatorDefault,
bundleRequiredValues, numValues, &kCFTypeArrayCallBacks);
numValues = sizeof(essentialInfoKeys) / sizeof(void *);
__sOSKextInfoEssentialKeys = CFArrayCreate(kCFAllocatorDefault,
essentialInfoKeys, numValues, &kCFTypeArrayCallBacks);
if (!__sOSAllKexts) {
CFArrayCallBacks nonrefcountValueCallBacks =
kCFTypeArrayCallBacks;
nonrefcountValueCallBacks.retain = NULL;
nonrefcountValueCallBacks.release = NULL;
__sOSAllKexts = CFArrayCreateMutable(kCFAllocatorDefault,
0, &nonrefcountValueCallBacks);
if (!__sOSAllKexts) {
OSKextLogMemError();
goto finish;
}
}
if (!__sOSKextsByURL) {
CFDictionaryValueCallBacks nonrefcountValueCallBacks =
kCFTypeDictionaryValueCallBacks;
nonrefcountValueCallBacks.retain = NULL;
nonrefcountValueCallBacks.release = NULL;
__sOSKextsByURL = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks, &nonrefcountValueCallBacks);
__sOSKextsByIdentifier = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks, &nonrefcountValueCallBacks);
if (!__sOSKextsByURL || !__sOSKextsByIdentifier) {
OSKextLogMemError();
goto finish;
}
}
__sOSNewKmodInfoKernelVersion = OSKextParseVersionString("6.0");
OSKextSetArchitecture(NULL);
finish:
SAFE_RELEASE(nonrefcountAllocator);
__sOSKextInitializing = false;
return;
}
static OSKextRef __OSKextAlloc(
CFAllocatorRef allocator,
CFAllocatorContext * context __unused)
{
OSKextRef result = NULL;
char * offset = NULL;
UInt32 size;
size = sizeof(__OSKext) - sizeof(CFRuntimeBase);
result = (OSKextRef)_CFRuntimeCreateInstance(allocator,
__kOSKextTypeID, size, NULL);
if (!result) {
OSKextLogMemError();
goto finish;
}
offset = (char *)result;
bzero(offset + sizeof(CFRuntimeBase), size);
finish:
return result;
}
static void __OSKextReleaseContents(CFTypeRef cfObject)
{
OSKextRef aKext = (OSKextRef)cfObject;
__OSKextRemoveKext(aKext);
OSKextFlushDiagnostics(aKext, kOSKextDiagnosticsFlagAll);
OSKextFlushLoadInfo(aKext, true);
if (aKext->mkextInfo) {
SAFE_RELEASE_NULL(aKext->mkextInfo->mkextURL);
SAFE_RELEASE_NULL(aKext->mkextInfo->mkextData);
SAFE_RELEASE_NULL(aKext->mkextInfo->executable);
SAFE_RELEASE_NULL(aKext->mkextInfo->resources);
SAFE_FREE_NULL(aKext->mkextInfo);
}
SAFE_RELEASE_NULL(aKext->bundleURL);
SAFE_RELEASE_NULL(aKext->bundleID);
SAFE_RELEASE_NULL(aKext->executableURL);
SAFE_RELEASE_NULL(aKext->infoDictionary);
return;
}
static CFStringRef __OSKextCopyDebugDescription(CFTypeRef cfObject)
{
CFMutableStringRef result;
OSKextRef aKext = (OSKextRef)cfObject;
CFAllocatorRef allocator = CFGetAllocator(cfObject);
CFStringRef bundleID = NULL; CFURLRef mkextURL = NULL;
bundleID = OSKextGetIdentifier(aKext);
result = CFStringCreateMutable(allocator, 0);
if (!result) {
OSKextLogMemError();
goto finish;
}
CFStringAppendFormat(result, NULL, CFSTR("<OSKext %p [%p]> { "),
cfObject, allocator);
if (OSKextIsFromMkext(aKext)) {
mkextURL = aKext->mkextInfo->mkextURL;
CFStringAppendFormat(result, NULL, CFSTR("mkext URL = \"%@\", "),
mkextURL ?(CFTypeRef)mkextURL : (CFTypeRef)CFSTR(__kStringUnknown));
if (aKext->bundleURL) {
CFStringAppendFormat(result, NULL, CFSTR("original URL = \"%@\", "),
aKext->bundleURL);
}
} else if (aKext->bundleURL) {
CFStringAppendFormat(result, NULL, CFSTR("URL = \"%@\", "),
aKext->bundleURL);
}
CFStringAppendFormat(result, NULL, CFSTR("ID = \"%@\""),
bundleID ? bundleID : CFSTR(__kStringUnknown));
CFStringAppendFormat(result, NULL, CFSTR(" }"));
finish:
return result;
}
#pragma mark Module Configuration
Boolean __OSKextReadRegistryNumberProperty(
io_registry_entry_t ioObject,
CFStringRef key,
CFNumberType numberType,
void * valuePtr)
{
Boolean result = false;
CFTypeRef regObj = NULL; CFNumberRef numberObj = NULL;
regObj = IORegistryEntryCreateCFProperty(ioObject,
key, kCFAllocatorDefault, kNilOptions);
if (!regObj || CFGetTypeID(regObj) != CFNumberGetTypeID()) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogGeneralFlag | kOSKextLogIPCFlag,
"Can't read kernel CPU info from IORegistry (absent or wrong type).");
goto finish;
}
numberObj = (CFNumberRef)regObj;
result = CFNumberGetValue(numberObj, numberType, valuePtr);
finish:
SAFE_RELEASE(regObj);
return result;
}
const NXArchInfo * OSKextGetRunningKernelArchitecture(void)
{
static const NXArchInfo * result = &__sOSKextUnknownArchInfo;
io_registry_entry_t ioRegRoot = IO_OBJECT_NULL; cpu_type_t kernelCPUType = CPU_TYPE_ANY;
cpu_subtype_t kernelCPUSubtype = CPU_SUBTYPE_MULTIPLE;
if (result != &__sOSKextUnknownArchInfo) {
goto finish;
}
#if (MAC_OS_X_VERSION_MIN_REQUIRED >= 1060) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)
ioRegRoot = IORegistryGetRootEntry(kIOMasterPortDefault);
if (ioRegRoot == IO_OBJECT_NULL) {
goto finish;
}
if (!__OSKextReadRegistryNumberProperty(ioRegRoot,
CFSTR(kOSKernelCPUTypeKey),
kCFNumberSInt32Type,
&kernelCPUType)) {
goto finish;
}
if (!__OSKextReadRegistryNumberProperty(ioRegRoot,
CFSTR(kOSKernelCPUSubtypeKey),
kCFNumberSInt32Type,
&kernelCPUSubtype)) {
goto finish;
}
#else
#pragma unused(ioRegRoot)
size_t size;
size = sizeof(kernelCPUType);
if (sysctlbyname("hw.cputype", &kernelCPUType, &size, NULL, 0) != 0) {
goto finish;
}
size = sizeof(kernelCPUSubtype);
if (sysctlbyname("hw.cpusubtype", &kernelCPUSubtype, &size, NULL, 0) != 0) {
goto finish;
}
#endif
result = NXGetArchInfoFromCpuType(kernelCPUType, kernelCPUSubtype);
if (result) {
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogKextBookkeepingFlag,
"Running kernel architecture is %s.", result->name);
}
finish:
#if defined(MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
if (ioRegRoot) {
IOObjectRelease(ioRegRoot);
}
#endif
if (!result) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogGeneralFlag | kOSKextLogIPCFlag,
"Can't read running kernel architecture.");
result = &__sOSKextUnknownArchInfo;
}
return result;
}
Boolean OSKextSetArchitecture(const NXArchInfo * archInfo)
{
Boolean result = true;
const NXArchInfo * oldArchInfo = __sOSKextArchInfo;
if (!__sOSKextInitializing) {
pthread_once(&__sOSKextInitialized, __OSKextInitialize);
}
if (oldArchInfo && (oldArchInfo == archInfo)) {
goto finish;
}
__sOSKextArchInfo = NULL;
if (archInfo) {
__sOSKextArchInfo = NXGetArchInfoFromCpuType(archInfo->cputype,
archInfo->cpusubtype);
if (!__sOSKextArchInfo) {
if (archInfo->name && archInfo->name[0]) {
OSKextLog( NULL,
kOSKextLogDebugLevel |
kOSKextLogGeneralFlag,
"Architecture %s not found by CPU info (type %d, subtype %d), "
"trying by name.",
archInfo->name, archInfo->cputype, archInfo->cpusubtype);
__sOSKextArchInfo = NXGetArchInfoFromName(archInfo->name);
if (!__sOSKextArchInfo) {
OSKextLog( NULL,
kOSKextLogDebugLevel |
kOSKextLogGeneralFlag,
"Architecture %s not found by name, "
"using running kernel architecture %s.",
archInfo->name,
OSKextGetRunningKernelArchitecture()->name);
result = false;
}
} else {
OSKextLog( NULL,
kOSKextLogDebugLevel,
"Unknown CPU info given (type %d, subtype %d), "
"using running kernel architecture %s.",
archInfo->cputype, archInfo->cpusubtype,
OSKextGetRunningKernelArchitecture()->name);
result = false;
}
}
}
if (!__sOSKextArchInfo) {
__sOSKextArchInfo = OSKextGetRunningKernelArchitecture();
}
if (!__sOSKextArchInfo) {
__sOSKextArchInfo = &__sOSKextUnknownArchInfo;
}
finish:
if (oldArchInfo == __sOSKextArchInfo) {
OSKextLog( NULL,
kOSKextLogDebugLevel | kOSKextLogGeneralFlag,
"Kext library architecture is %s (unchanged).",
__sOSKextArchInfo->name ? __sOSKextArchInfo->name : __kStringUnknown);
} else {
const char * auxMsg = "";
if (!__sOSKextInitializing && __sOSAllKexts &&
CFArrayGetCount(__sOSAllKexts)) {
auxMsg = "; reinitializing all kexts for new architecture";
}
OSKextLog( NULL,
kOSKextLogDetailLevel |
kOSKextLogGeneralFlag | kOSKextLogKextBookkeepingFlag,
"Kext library architecture set to %s%s.",
__sOSKextArchInfo->name ? __sOSKextArchInfo->name : __kStringUnknown,
auxMsg);
if (!__sOSKextInitializing) {
__OSKextReinit(NULL);
OSKextFlushLoadInfo(NULL, true);
}
}
return result;
}
const NXArchInfo * OSKextGetArchitecture(void)
{
pthread_once(&__sOSKextInitialized, __OSKextInitialize);
return __sOSKextArchInfo;
}
static Boolean __OSKextIsArchitectureLP64(void)
{
return ((OSKextGetArchitecture()->cputype & CPU_ARCH_ABI64) != 0);
}
__unused static Boolean __OSKextIsSplitKext(OSKextRef aKext)
{
if (aKext && aKext->loadInfo && aKext->loadInfo->linkInfo.vmaddr_TEXT_EXEC != 0) {
return true;
}
return false;
}
__unused static Boolean __OSKextIsSplitKextMacho64(struct mach_header_64 *kextHeader)
{
struct segment_command_64 *seg_cmd = NULL;
if (!kextHeader)
return false;
seg_cmd = macho_get_segment_by_name_64(kextHeader, SEG_TEXT_EXEC);
if (seg_cmd != NULL) {
return true;
}
return false;
}
CFURLRef OSKextGetExecutableURL(OSKextRef aKext)
{
CFURLRef executableURL = NULL; CFURLRef newURL = NULL; CFStringRef path = NULL; CFStringRef executableName = NULL; struct stat executableStat;
char kextPath[PATH_MAX];
char executablePath[PATH_MAX];
if (aKext->executableURL) {
return aKext->executableURL;
}
__OSKextGetFileSystemPath(aKext, NULL,
true, kextPath);
OSKextLog(aKext,
kOSKextLogDebugLevel | kOSKextLogFileAccessFlag,
"Checking CFBundle of %s for executable URL.",
kextPath);
executableURL = _CFBundleCopyExecutableURLInDirectory(OSKextGetURL(aKext));
if (!executableURL) {
executableName = OSKextGetValueForInfoDictionaryKey(aKext,
kCFBundleExecutableKey);
if (executableName) {
__OSKextAddDiagnostic(aKext,
kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticExecutableMissingKey,
executableName, NULL);
goto finish;
}
}
#if SHARED_EXECUTABLE
if (!executableURL) {
CFStringRef sharedExecutableIdentifier = NULL; sharedExecutableIdentifier = OSKextGetValueForInfoDictionaryKey(
aKext, CFSTR(kOSBundleSharedExecutableIdentifierKey));
if (sharedExecutableIdentifier) {
OSKextRef sharedExecutableKext =
OSKextGetKextWithIdentifier(sharedExecutableIdentifier);
if (sharedExecutableKext) {
executableURL = _CFBundleCopyExecutableURLInDirectory(
OSKextGetURL(sharedExecutableKext));
} else {
__OSKextAddDiagnostic(aKext,
kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticSharedExecutableKextMissingKey,
sharedExecutableIdentifier, NULL);
goto finish;
}
}
}
#endif
if (executableURL && sOSKextExecutableSuffix[0]) {
if (!__OSKextGetFileSystemPath( NULL,
executableURL, true, executablePath)) {
goto finish;
}
size_t len = strlen(executablePath);
strlcpy(executablePath + len, sOSKextExecutableSuffix, sizeof(executablePath) - len);
OSKextLog(aKext,
kOSKextLogDebugLevel | kOSKextLogFileAccessFlag,
"Statting %s for suffix.",
executablePath);
if (!stat(executablePath, &executableStat)) {
path = CFStringCreateWithCString(
CFGetAllocator(aKext), executablePath,
kCFStringEncodingUTF8);
if (path) {
newURL = CFURLCreateWithFileSystemPath(CFGetAllocator(aKext),
path, kCFURLPOSIXPathStyle, false);
CFRelease(path);
}
if (newURL) {
CFRelease(executableURL);
executableURL = newURL;
}
}
}
if (!executableURL) {
goto finish;
}
aKext->executableURL = CFRetain(executableURL);
finish:
SAFE_RELEASE(executableURL);
return aKext->executableURL;
}
void OSKextSetExecutableSuffix(const char * suffix, const char * kernelPath)
{
const char * nsuffix;
size_t len;
char * file;
char * copy;
if (suffix) {
strlcpy(sOSKextExecutableSuffix, suffix, sizeof(sOSKextExecutableSuffix));
} else if (kernelPath) {
copy = strndup(kernelPath, MAXPATHLEN);
if (copy)
{
file = basename(copy);
if (file && (suffix = strchr(file, '.'))) {
suffix++;
nsuffix = strchr(suffix, '.');
len = nsuffix ? (nsuffix - suffix) : strlen(suffix);
len += 2;
if (len > sizeof(sOSKextExecutableSuffix)) len = sizeof(sOSKextExecutableSuffix);
snprintf(sOSKextExecutableSuffix, len, "_%s", suffix);
}
free(copy);
}
}
if (!suffix) {
sOSKextExecutableSuffix[0] = 0;
}
}
void OSKextSetLogFilter(
OSKextLogSpec logFilter,
Boolean kernelFlag)
{
OSKextLogSpec oldLogFilter;
if (kernelFlag) {
oldLogFilter = __sKernelLogFilter;
__sKernelLogFilter = logFilter;
} else {
oldLogFilter = __sUserLogFilter;
__sUserLogFilter = logFilter;
}
if (oldLogFilter != logFilter) {
OSKextLog( NULL,
kOSKextLogDebugLevel |
kOSKextLogGeneralFlag,
"Kext %s-space log filter changed from 0x%x to 0x%x.",
kernelFlag ? "kernel" : "user",
oldLogFilter, kernelFlag ? __sKernelLogFilter : __sUserLogFilter);
}
return;
}
OSKextLogSpec OSKextGetLogFilter(Boolean kernelFlag)
{
return kernelFlag ? __sKernelLogFilter : __sUserLogFilter;
}
void OSKextSetLogOutputFunction(OSKextLogOutputFunction func)
{
__sOSKextLogOutputFunction = func;
return;
}
void OSKextSetSimulatedSafeBoot(Boolean flag)
{
Boolean oldSafeBootValue = __sOSKextSimulatedSafeBoot;
OSKextLog( NULL,
kOSKextLogDetailLevel | kOSKextLogKextBookkeepingFlag,
"Kext library setting simulated safe boot mode to %s.",
flag ? "true" : "false");
__sOSKextSimulatedSafeBoot = flag;
if (oldSafeBootValue != __sOSKextSimulatedSafeBoot) {
__OSKextReinit( NULL);
}
return;
}
Boolean OSKextGetSimulatedSafeBoot(void)
{
return __sOSKextSimulatedSafeBoot;
}
#define SYSCTL_MIB_LENGTH (2)
Boolean OSKextGetActualSafeBoot(void)
{
static Boolean result = false;
static Boolean gotIt = false;
int kern_safe_boot = 0;
size_t length = 0;
int mib_name[SYSCTL_MIB_LENGTH] = { CTL_KERN, KERN_SAFEBOOT };
if (gotIt) {
goto finish;
}
length = sizeof(kern_safe_boot);
if (!sysctl(mib_name, SYSCTL_MIB_LENGTH,
&kern_safe_boot, &length, NULL, 0)) {
result = kern_safe_boot ? true : false;
gotIt = true;
} else {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogGeneralFlag | kOSKextLogIPCFlag,
"Can't determine actual safe boot mode - "
"sysctl() failed for KERN_SAFEBOOT - %s.",
strerror(errno));
}
finish:
return result;
}
CFArrayRef OSKextGetSystemExtensionsFolderURLs(void)
{
pthread_once(&__sOSKextInitialized, __OSKextInitialize);
return __sOSKextSystemExtensionsFolderURLs;
}
void OSKextSetRecordsDiagnostics(OSKextDiagnosticsFlags flags)
{
OSKextLog( NULL,
kOSKextLogDetailLevel |
kOSKextLogValidationFlag | kOSKextLogAuthenticationFlag |
kOSKextLogDependenciesFlag | kOSKextLogLoadFlag |
kOSKextLogLinkFlag | kOSKextLogPatchFlag | kOSKextLogKextBookkeepingFlag,
"Kext library recording diagnostics%s%s%s%s%s.",
flags == kOSKextDiagnosticsFlagNone ? " off" : " for:",
flags & kOSKextDiagnosticsFlagValidation ? " validation" : "",
flags & kOSKextDiagnosticsFlagAuthentication ? " authentication" : "",
flags & kOSKextDiagnosticsFlagDependencies ? " dependencies" : "",
flags & kOSKextDiagnosticsFlagWarnings ? " warnings" : "");
__sOSKextRecordsDiagnositcs = flags;
return;
}
OSKextDiagnosticsFlags OSKextGetRecordsDiagnostics(void)
{
return __sOSKextRecordsDiagnositcs;
}
void OSKextSetUsesCaches(Boolean flag)
{
OSKextLog( NULL,
kOSKextLogDetailLevel | kOSKextLogKextBookkeepingFlag,
"Kext library %s using caches.", flag ? "now" : "not");
__sOSKextUsesCaches = flag;
}
Boolean OSKextGetUsesCaches(void)
{
return __sOSKextUsesCaches;
}
void _OSKextSetStrictRecordingByLastOpened(Boolean flag)
{
__sOSKextStrictRecordingByLastOpened = flag;
return;
}
void _OSKextSetStrictAuthentication(Boolean flag)
{
__sOSKextStrictAuthentication = flag;
return;
}
void _OSKextSetAuthenticationFunction(OSKextAuthFnPtr authFn, void *context)
{
__sOSKextAuthenticationFunction = authFn;
__sOSKextAuthenticationContext = context;
return;
}
#pragma mark Instance Management
static Boolean __OSKextInitWithURL(
OSKextRef aKext,
CFURLRef anURL)
{
Boolean result = false;
CFBundleRef kextBundle = NULL; char urlPath[PATH_MAX];
__OSKextGetFileSystemPath( NULL, anURL,
true, urlPath);
OSKextLog(aKext,
kOSKextLogDebugLevel | kOSKextLogFileAccessFlag,
"Opening CFBundle for %s.", urlPath);
kextBundle = CFBundleCreate(CFGetAllocator(aKext), anURL);
if (!kextBundle) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Can't open CFBundle for %s.", urlPath);
goto finish;
}
aKext->bundleURL = CFRetain(anURL);
if (!__OSKextReadInfoDictionary(aKext, kextBundle)) {
goto finish;
}
__OSKextProcessInfoDictionary(aKext, kextBundle);
result = __OSKextRecordKext(aKext);
finish:
if (kextBundle) {
OSKextLog(aKext,
kOSKextLogDebugLevel | kOSKextLogFileAccessFlag,
"Releasing CFBundle for %s",
urlPath);
}
SAFE_RELEASE(kextBundle);
return result;
}
static Boolean __OSKextInitFromMkext(
OSKextRef aKext,
CFDictionaryRef infoDict,
CFURLRef mkextURL,
CFDataRef mkextData)
{
Boolean result = false;
CFStringRef bundlePath = NULL;
aKext->staticFlags.isFromMkext = 1;
bundlePath = CFDictionaryGetValue(infoDict, CFSTR(kMKEXTBundlePathKey));
if (bundlePath) {
aKext->bundleURL = CFURLCreateWithFileSystemPath(CFGetAllocator(aKext),
bundlePath, kCFURLPOSIXPathStyle, true);
if (!aKext->bundleURL) {
OSKextLogMemError();
}
}
aKext->infoDictionary = CFDictionaryCreateMutableCopy(CFGetAllocator(infoDict), 0, infoDict);
if (!__OSKextCreateMkextInfo(aKext)) {
OSKextLogMemError();
goto finish;
}
if (mkextURL) {
aKext->mkextInfo->mkextURL = CFRetain(mkextURL);
}
aKext->mkextInfo->mkextData = CFRetain(mkextData);
if (!__OSKextProcessInfoDictionary(aKext, NULL)) {
goto finish; }
result = __OSKextRecordKext(aKext);
finish:
return result;
}
static void __OSKextReinitApplierFunction(
const void * vKey __unused,
const void * vValue,
void * vContext __unused)
{
OSKextRef aKext = (OSKextRef)vValue;
__OSKextReinit(aKext);
return;
}
void __OSKextReinit(OSKextRef aKext)
{
if (aKext) {
if (!aKext->staticFlags.isFromIdentifierCache) {
SAFE_RELEASE_NULL(aKext->bundleID);
OSKextFlushDiagnostics(aKext, kOSKextDiagnosticsFlagAll);
bzero(&aKext->flags, sizeof(aKext->flags));
__OSKextProcessInfoDictionary(aKext, NULL);
}
} else if (__sOSKextsByURL) {
CFDictionaryApplyFunction(__sOSKextsByURL,
__OSKextReinitApplierFunction, NULL);
}
return;
}
Boolean __OSKextRecordKext(OSKextRef aKext)
{
Boolean result = false;
CFStringRef kextID = NULL; CFURLRef kextURL = NULL; CFURLRef canonicalURL = NULL; char * kextIDCString = NULL; char versionCString[kOSKextVersionMaxLength];
char urlPath[PATH_MAX];
kextID = OSKextGetIdentifier(aKext);
if (kextID) {
kextIDCString = createUTF8CStringForCFString(kextID);
}
kextURL = OSKextGetURL(aKext);
if (kextURL) {
canonicalURL = CFURLCopyAbsoluteURL(kextURL);
if (!canonicalURL) {
OSKextLogMemError();
goto finish;
}
}
__OSKextGetFileSystemPath( NULL,
canonicalURL,
true, urlPath);
if (CFArrayGetFirstIndexOfValue(__sOSAllKexts, RANGE_ALL(__sOSAllKexts),
aKext) == kCFNotFound) {
CFArrayAppendValue(__sOSAllKexts, aKext);
}
if (canonicalURL && !OSKextIsFromMkext(aKext)) {
CFDictionarySetValue(__sOSKextsByURL, canonicalURL, aKext);
}
result = __OSKextRecordKextInIdentifierDict(aKext, __sOSKextsByIdentifier);
if (result) {
OSKextVersionGetString(OSKextGetVersion(aKext), versionCString,
sizeof(versionCString));
OSKextLog(aKext,
kOSKextLogStepLevel | kOSKextLogKextBookkeepingFlag,
"Recorded %s%s, id %s, version %s.",
urlPath,
OSKextIsFromMkext(aKext) ? " (from mkext)" : "",
kextIDCString ? kextIDCString : __kStringUnknown,
versionCString);
}
finish:
SAFE_RELEASE(canonicalURL);
SAFE_FREE(kextIDCString);
return result;
}
void __OSKextRemoveKext(OSKextRef aKext)
{
CFTypeRef foundEntry = NULL; CFURLRef kextURL = NULL; CFURLRef canonicalURL = NULL; CFStringRef kextIdentifier = NULL; char * allocatedCString = NULL; char * kextIdentifierCString = NULL;
char urlPath[PATH_MAX] = __kStringUnknown;
char versionCString[kOSKextVersionMaxLength];
CFIndex count, i;
count = CFArrayGetCount(__sOSAllKexts);
if (count) {
for (i = count - 1; i >= 0; i--) {
OSKextRef checkKext = (OSKextRef)CFArrayGetValueAtIndex(
__sOSAllKexts, i);
if (checkKext == aKext) {
CFArrayRemoveValueAtIndex(__sOSAllKexts, i);
}
}
}
kextIdentifier = OSKextGetIdentifier(aKext);
if (kextIdentifier) {
allocatedCString = createUTF8CStringForCFString(kextIdentifier);
kextIdentifierCString = allocatedCString;
} else {
kextIdentifierCString = __kOSKextUnknownIdentifier;
}
kextURL = OSKextGetURL(aKext);
if (kextURL) {
canonicalURL = CFURLCopyAbsoluteURL(kextURL);
if (canonicalURL) {
__OSKextGetFileSystemPath( NULL,
canonicalURL,
true, urlPath);
if (canonicalURL && !OSKextIsFromMkext(aKext)) {
foundEntry = CFDictionaryGetValue(__sOSKextsByURL, canonicalURL);
if (foundEntry == aKext) {
CFDictionaryRemoveValue(__sOSKextsByURL, canonicalURL);
}
}
}
}
__OSKextRemoveKextFromIdentifierDict(aKext, __sOSKextsByIdentifier);
OSKextVersionGetString(OSKextGetVersion(aKext), versionCString,
sizeof(versionCString));
OSKextLog(aKext, kOSKextLogStepLevel | kOSKextLogKextBookkeepingFlag,
"Removed %s, id %s%s, version %s.",
urlPath,
OSKextIsFromMkext(aKext) ? " (from mkext)" : "",
kextIdentifierCString,
versionCString);
SAFE_RELEASE(canonicalURL);
SAFE_FREE(allocatedCString);
return;
}
Boolean __OSKextRecordKextInIdentifierDict(
OSKextRef aKext,
CFMutableDictionaryRef identifierDict)
{
Boolean result = true;
CFStringRef kextID = NULL; CFTypeRef foundEntry = NULL; CFMutableArrayRef subsKexts = NULL; char * kextIDCString = NULL; int lookupIndex = 0;
kextID = OSKextGetIdentifier(aKext);
if (!kextID) {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogKextBookkeepingFlag,
"Can't record kext in identifier lookup dictionary; no identifier.");
result = false;
goto finish;
}
foundEntry = CFDictionaryGetValue(identifierDict, kextID);
if (!foundEntry) {
CFDictionarySetValue(identifierDict, kextID, aKext);
kextIDCString = createUTF8CStringForCFString(kextID);
goto finish;
}
if (foundEntry == aKext) {
kextIDCString = createUTF8CStringForCFString(kextID);
goto finish;
}
if (OSKextGetTypeID() == CFGetTypeID(foundEntry)) {
CFArrayCallBacks nonrefcountArrayCallBacks =
kCFTypeArrayCallBacks;
nonrefcountArrayCallBacks.retain = NULL;
nonrefcountArrayCallBacks.release = NULL;
subsKexts = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&nonrefcountArrayCallBacks);
if (!subsKexts) {
OSKextLogMemError();
result = false;
goto finish;
}
CFArrayAppendValue(subsKexts, foundEntry);
CFDictionarySetValue(identifierDict, kextID, subsKexts);
foundEntry = subsKexts;
}
if (CFArrayGetTypeID() == CFGetTypeID(foundEntry)) {
if (__sOSKextStrictRecordingByLastOpened) {
CFMutableArrayRef kextsWithSameID = (CFMutableArrayRef)foundEntry;
CFIndex i;
i = CFArrayGetFirstIndexOfValue(kextsWithSameID,
RANGE_ALL(kextsWithSameID), aKext);
if (i == kCFNotFound) {
CFArrayInsertValueAtIndex(kextsWithSameID, 0, aKext);
}
} else {
CFMutableArrayRef kextsWithSameID = (CFMutableArrayRef)foundEntry;
OSKextVersion addedKextVersion = OSKextGetVersion(aKext);
CFIndex addedKextCreateOrder = kCFNotFound;
CFIndex count, i;
i = CFArrayGetFirstIndexOfValue(kextsWithSameID,
RANGE_ALL(kextsWithSameID), aKext);
if (i != kCFNotFound) {
CFArrayRemoveValueAtIndex(kextsWithSameID, i);
}
addedKextCreateOrder = CFArrayGetFirstIndexOfValue(__sOSAllKexts,
RANGE_ALL(__sOSAllKexts), aKext);
count = CFArrayGetCount(kextsWithSameID);
for (i = 0; i < count; i++) {
OSKextRef existingKext = (OSKextRef)CFArrayGetValueAtIndex(
kextsWithSameID, i);
OSKextVersion existingKextVersion = OSKextGetVersion(existingKext);
CFIndex existingKextCreateOrder = kCFNotFound;
existingKextCreateOrder = CFArrayGetFirstIndexOfValue(__sOSAllKexts,
RANGE_ALL(__sOSAllKexts), existingKext);
if (addedKextVersion == existingKextVersion) {
if (addedKextCreateOrder > existingKextCreateOrder) {
break;
}
}
if (addedKextVersion > existingKextVersion) {
break;
}
}
CFArrayInsertValueAtIndex(kextsWithSameID, i, aKext);
kextIDCString = createUTF8CStringForCFString(kextID);
lookupIndex = i;
}
}
finish:
if (result && kextIDCString) {
char versionString[kOSKextVersionMaxLength];
OSKextVersionGetString(OSKextGetVersion(aKext), versionString,
sizeof(versionString));
if (foundEntry == aKext) {
OSKextLog(aKext, kOSKextLogStepLevel | kOSKextLogKextBookkeepingFlag,
"%s, version %s is already "
"in the identifier lookup dictionary at index %d.",
kextIDCString, versionString, lookupIndex);
} else {
OSKextLog(aKext, kOSKextLogDebugLevel | kOSKextLogKextBookkeepingFlag,
"%s, version %s recorded at index %d "
"in the identifier lookup dictionary.",
kextIDCString, versionString, lookupIndex);
}
}
SAFE_FREE(kextIDCString);
return result;
}
void __OSKextRemoveKextFromIdentifierDict(
OSKextRef aKext,
CFMutableDictionaryRef identifierDict)
{
CFStringRef kextID = NULL; CFTypeRef foundEntry = NULL; OSKextRef foundKext = NULL; char * kextIDCString = NULL;
kextID = OSKextGetIdentifier(aKext);
if (!kextID) {
goto finish;
}
kextIDCString = createUTF8CStringForCFString(kextID);
if (!kextIDCString) {
OSKextLogMemError();
goto finish;
}
foundEntry = CFDictionaryGetValue(identifierDict, kextID);
if (!foundEntry) {
goto finish;
}
if (foundEntry == aKext) {
foundKext = (OSKextRef)foundEntry;
CFDictionaryRemoveValue(identifierDict, kextID);
} else if (CFArrayGetTypeID() == CFGetTypeID(foundEntry)) {
CFMutableArrayRef kextsWithSameID = (CFMutableArrayRef)foundEntry;
CFIndex count, i;
count = CFArrayGetCount(kextsWithSameID);
for (i = 0; i < count; i++) {
OSKextRef thisKext = (OSKextRef)CFArrayGetValueAtIndex(
kextsWithSameID, i);
if (thisKext == aKext) {
foundKext = thisKext;
CFArrayRemoveValueAtIndex(kextsWithSameID, i);
break;
}
}
if (!CFArrayGetCount(kextsWithSameID)) {
CFDictionaryRemoveValue(identifierDict, kextID);
CFRelease(kextsWithSameID);
}
}
finish:
if (foundKext) {
char versionCString[kOSKextVersionMaxLength];
OSKextVersionGetString(OSKextGetVersion(aKext),
versionCString, sizeof(versionCString));
OSKextLog(aKext,
kOSKextLogStepLevel | kOSKextLogKextBookkeepingFlag,
"%s, version %s removed from identifier lookup dictionary.",
kextIDCString, versionCString);
} else if (kextIDCString) {
char * auxMsg = NULL;
if (!strncmp(kextIDCString, __kOSKextUnknownIdentifier,
sizeof(__kOSKextUnknownIdentifier) - 1)) {
auxMsg = " (expected while realizing)";
}
OSKextLog(aKext,
kOSKextLogDebugLevel | kOSKextLogKextBookkeepingFlag,
"%s not found in identifier lookup dictionary%s.",
kextIDCString, auxMsg);
}
SAFE_FREE(kextIDCString);
return;
}
OSKextRef OSKextCreate(
CFAllocatorRef allocator,
CFURLRef anURL)
{
OSKextRef result = NULL;
CFStringRef pathExtension = NULL; char relPath[PATH_MAX];
pthread_once(&__sOSKextInitialized, __OSKextInitialize);
pathExtension = CFURLCopyPathExtension(anURL);
if (!pathExtension || !CFEqual(pathExtension, CFSTR(kOSKextBundleExtension))) {
goto finish;
}
if (!__OSKextGetFileSystemPath( NULL, anURL,
false, relPath)) {
goto finish;
}
result = OSKextGetKextWithURL(anURL);
if (result) {
OSKextLog( NULL,
kOSKextLogDebugLevel |
kOSKextLogKextBookkeepingFlag | kOSKextLogFileAccessFlag,
"%s is already open; returning existing object.",
relPath);
CFRetain(result);
goto finish;
}
OSKextLog( NULL,
kOSKextLogDebugLevel |
kOSKextLogKextBookkeepingFlag | kOSKextLogFileAccessFlag,
"Creating %s.",
relPath);
result = __OSKextAlloc(allocator, NULL);
if (!result) {
OSKextLogMemError();
goto finish;
}
if (!__OSKextInitWithURL(result, anURL)) {
SAFE_RELEASE_NULL(result);
goto finish;
}
finish:
SAFE_RELEASE(pathExtension);
return result;
}
CFMutableArrayRef __OSKextCreateKextsFromURL(
CFAllocatorRef allocator,
CFURLRef anURL,
OSKextRef aKext, Boolean createPluginsFlag)
{
CFMutableArrayRef result = NULL;
CFStringRef pathExtension = NULL; OSKextRef theKext = NULL; CFArrayRef plugins = NULL; CFURLRef absURL = NULL; char urlPath[PATH_MAX];
CFBooleanRef dirExists = NULL; CFArrayRef urlContents = NULL; SInt32 error;
CFArrayRef kexts = NULL; CFIndex count, i;
pathExtension = CFURLCopyPathExtension(anURL);
if (pathExtension && CFEqual(pathExtension,
CFSTR(kOSKextBundleExtension))) {
result = CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks);
if (!result) {
OSKextLogMemError();
goto finish;
}
theKext = OSKextCreate(allocator, anURL);
if (theKext) {
CFArrayAppendValue(result, theKext);
if (createPluginsFlag) {
plugins = OSKextCopyPlugins(theKext);
if (plugins && CFArrayGetCount(plugins)) {
CFArrayAppendArray(result, plugins, RANGE_ALL(plugins));
}
}
}
goto finish;
}
absURL = CFURLCopyAbsoluteURL(anURL);
if (!absURL) {
OSKextLogMemError();
goto finish;
}
__OSKextGetFileSystemPath( NULL, absURL,
true, urlPath);
dirExists = CFURLCreatePropertyFromResource(allocator, anURL,
kCFURLFileExists, &error);
if (!dirExists) {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Failed to check path %s (CF error %ld).",
urlPath, (long)error);
goto finish;
} else if (!CFBooleanGetValue(dirExists)) {
if (!aKext) {
OSKextLog(aKext,
kOSKextLogProgressLevel | kOSKextLogFileAccessFlag,
"%s: %s - no such file or directory.",
__func__, urlPath);
}
goto finish;
}
if (_OSKextReadFromIdentifierCacheForFolder(absURL, &result)) {
goto finish;
}
result = CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks);
if (!result) {
OSKextLogMemError();
goto finish;
}
OSKextLog(aKext,
kOSKextLogProgressLevel | kOSKextLogDirectoryScanFlag,
"Scanning %s for kexts.", urlPath);
urlContents = CFURLCreatePropertyFromResource(allocator, anURL,
kCFURLFileDirectoryContents, &error);
if (!urlContents || error) {
if (error && error != kCFURLResourceNotFoundError) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Failed to read contents of %s, CFURL error %d.",
urlPath, (int)error);
}
goto finish;
}
count = CFArrayGetCount(urlContents);
for (i = 0; i < count; i++) {
CFURLRef thisURL = (CFURLRef)CFArrayGetValueAtIndex(urlContents, i);
SAFE_RELEASE_NULL(pathExtension);
SAFE_RELEASE_NULL(kexts);
pathExtension = CFURLCopyPathExtension(thisURL);
if (pathExtension && CFEqual(pathExtension,
CFSTR(kOSKextBundleExtension))) {
char kextURLPath[PATH_MAX];
__OSKextGetFileSystemPath( NULL, thisURL,
FALSE, kextURLPath);
if (aKext) {
OSKextLog(aKext,
kOSKextLogDetailLevel | kOSKextLogDirectoryScanFlag,
"Found plugin %s.",
kextURLPath);
} else {
OSKextLog(aKext,
kOSKextLogDetailLevel | kOSKextLogDirectoryScanFlag,
"Found %s.",
kextURLPath);
}
kexts = OSKextCreateKextsFromURL(allocator, thisURL);
if (kexts) {
CFArrayAppendArray(result, kexts, RANGE_ALL(kexts));
}
}
}
(void)_OSKextWriteIdentifierCacheForKextsInDirectory(result, absURL,
false);
finish:
SAFE_RELEASE(pathExtension);
SAFE_RELEASE(theKext);
SAFE_RELEASE(plugins);
SAFE_RELEASE(dirExists);
SAFE_RELEASE(urlContents);
SAFE_RELEASE(kexts);
SAFE_RELEASE(absURL);
return result;
}
CFArrayRef OSKextCreateKextsFromURL(
CFAllocatorRef allocator,
CFURLRef anURL)
{
pthread_once(&__sOSKextInitialized, __OSKextInitialize);
return __OSKextCreateKextsFromURL(allocator, anURL,
NULL, true);
}
CFMutableArrayRef __OSKextCreateKextsFromURLs(
CFAllocatorRef allocator,
CFArrayRef arrayOfURLs,
Boolean createPluginsFlag)
{
CFMutableArrayRef result = NULL;
CFArrayRef scannedKexts = NULL; CFIndex count, i;
result = CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks);
if (!result) {
OSKextLogMemError();
goto finish;
}
count = CFArrayGetCount(arrayOfURLs);
for (i = 0; i < count; i++) {
CFURLRef theURL = (CFURLRef)CFArrayGetValueAtIndex(arrayOfURLs, i);
SAFE_RELEASE_NULL(scannedKexts);
scannedKexts = __OSKextCreateKextsFromURL(allocator, theURL,
NULL, createPluginsFlag);
if (scannedKexts) {
CFArrayAppendArray(result, scannedKexts, RANGE_ALL(scannedKexts));
}
}
finish:
SAFE_RELEASE(scannedKexts);
return result;
}
CFArrayRef OSKextCreateKextsFromURLs(
CFAllocatorRef allocator,
CFArrayRef arrayOfURLs)
{
CFMutableArrayRef result = NULL;
CFIndex count, i, j;
pthread_once(&__sOSKextInitialized, __OSKextInitialize);
result = __OSKextCreateKextsFromURLs(allocator, arrayOfURLs, true);
if (!result) {
goto finish;
}
count = CFArrayGetCount(result);
if (!count) {
goto finish;
}
for (i = 0; i < CFArrayGetCount(result); i++) {
OSKextRef thisKext = (OSKextRef)CFArrayGetValueAtIndex(result, i);
for (j = i + 1; j < CFArrayGetCount(result); ) {
OSKextRef thatKext = (OSKextRef)CFArrayGetValueAtIndex(result, j);
if (thisKext == thatKext) {
CFArrayRemoveValueAtIndex(result, j);
} else {
j++;
}
}
}
finish:
return result;
}
#pragma mark (Plist Caches)
#define GZIP_RATIO (5)
#define MAX_REALLOCS (16)
Boolean _OSKextReadFromIdentifierCacheForFolder(
CFURLRef anURL,
CFMutableArrayRef * kextsOut)
{
Boolean result = false;
CFMutableArrayRef kexts = NULL; CFURLRef absURL = NULL; char absPath[PATH_MAX] = "";
CFDictionaryRef cacheDict = NULL; CFStringRef basePath = NULL; CFNumberRef cacheVersion = NULL; SInt32 cacheVersionValue = 0;
CFArrayRef kextInfoArray = NULL; OSKextRef newKext = NULL; CFIndex count, i;
if (!OSKextGetUsesCaches()) {
goto finish;
}
if (!__OSKextGetFileSystemPath( NULL, anURL,
true, absPath)) {
OSKextLogStringError( NULL);
goto finish;
}
if (!_OSKextReadCache(anURL,
CFSTR(_kOSKextIdentifierCacheBasename),
NULL,
_kOSKextCacheFormatCFBinary,
true,
kextsOut ? (CFPropertyListRef *)&cacheDict : NULL)) {
goto finish;
}
if (!kextsOut) {
OSKextLog( NULL,
kOSKextLogDebugLevel | kOSKextLogKextBookkeepingFlag,
"Kext identifier->path cache for %s is up to date.",
absPath);
result = true;
goto finish;
}
if (CFDictionaryGetTypeID() != CFGetTypeID(cacheDict)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogKextBookkeepingFlag,
"Kext identifier->path cache for %s - not a dictionary.",
absPath);
goto finish;
}
cacheVersion = CFDictionaryGetValue(cacheDict,
CFSTR(__kOSKextIdentifierCacheVersionKey));
if (!cacheVersion ||
CFNumberGetTypeID() != CFGetTypeID(cacheVersion) ||
!CFNumberGetValue(cacheVersion, kCFNumberSInt32Type, &cacheVersionValue)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogKextBookkeepingFlag,
"Kext identifier->cache for %s - cache version missing/invalid.",
absPath);
goto finish;
}
if (cacheVersionValue != __kOSKextIdentifierCacheCurrentVersion) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogKextBookkeepingFlag,
"Kext identifier->path cache for %s - version %d unsupported.",
absPath, (int)cacheVersionValue);
goto finish;
}
basePath = CFDictionaryGetValue(cacheDict,
CFSTR(__kOSKextIdentifierCacheBasePathKey));
if (!basePath ||
CFStringGetTypeID() != CFGetTypeID(basePath)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogKextBookkeepingFlag,
"Kext identifier->path cache for %s - base path missing or invalid.",
absPath);
goto finish;
}
kextInfoArray = CFDictionaryGetValue(cacheDict,
CFSTR(__kOSKextIdentifierCacheKextInfoKey));
if (!kextInfoArray ||
CFArrayGetTypeID() != CFGetTypeID(kextInfoArray)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogKextBookkeepingFlag,
"Kext identifier->path cache - kext info is not an array.");
goto finish;
}
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogKextBookkeepingFlag,
"Creating kexts from identifier->path cache for %s.",
absPath);
kexts = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
if (!kexts) {
OSKextLogMemError();
goto finish;
}
count = CFArrayGetCount(kextInfoArray);
for (i = 0; i < count; i++) {
CFDictionaryRef cacheDict = (CFDictionaryRef)CFArrayGetValueAtIndex(
kextInfoArray, i);
if (CFDictionaryGetTypeID() != CFGetTypeID(cacheDict)) {
OSKextLog( NULL, kOSKextLogErrorLevel,
"Kext identifier->path cache for %s - kext entry not a dictionary.",
absPath);
goto finish;
}
SAFE_RELEASE_NULL(newKext);
newKext = __OSKextCreateFromIdentifierCacheDict(CFGetAllocator(absURL),
cacheDict, basePath, i);
if (!newKext) {
goto finish;
}
if (kCFNotFound == CFArrayGetFirstIndexOfValue(kexts, RANGE_ALL(kexts),
newKext)) {
CFArrayAppendValue(kexts, newKext);
}
}
count = CFArrayGetCount(kexts);
if (count) {
for (i = count - 1; i >= 0; i--) {
OSKextRef aKext = (OSKextRef)CFArrayGetValueAtIndex(
kexts, i);
if (!__OSKextRecordKext(aKext)) {
CFArrayRemoveValueAtIndex(kexts, i);
}
}
}
OSKextLog( NULL,
kOSKextLogProgressLevel |
kOSKextLogKextBookkeepingFlag | kOSKextLogFileAccessFlag,
"Finished reading identifier->path cache for %s.",
absPath);
result = true;
if (kextsOut) {
*kextsOut = (CFMutableArrayRef)CFRetain(kexts);
}
finish:
SAFE_RELEASE(kexts);
SAFE_RELEASE(absURL);
SAFE_RELEASE(cacheDict);
SAFE_RELEASE(newKext);
return result;
}
OSKextRef __OSKextCreateFromIdentifierCacheDict(
CFAllocatorRef allocator,
CFDictionaryRef cacheDict,
CFStringRef basePath,
CFIndex entryIndex)
{
OSKextRef result = NULL;
OSKextRef newKext = NULL; OSKextRef existingKext = NULL; CFStringRef bundleID = NULL; CFStringRef bundlePath = NULL; CFStringRef bundleVersion = NULL; CFStringRef fullPath = NULL; CFURLRef bundleURL = NULL; OSKextVersion kextVersion = -1;
CFBooleanRef scratchBool = NULL; char kextPath[PATH_MAX];
bundlePath = (CFStringRef)CFDictionaryGetValue(cacheDict,
CFSTR("OSBundlePath"));
if (!bundlePath || (CFGetTypeID(bundlePath) != CFStringGetTypeID())) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogKextBookkeepingFlag,
"Can't create kext: missing or non-string path "
"in identifier cache entry %d.",
(int)entryIndex);
goto finish;
}
if (!CFStringHasSuffix(bundlePath, CFSTR(kOSKextBundleExtension))) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogKextBookkeepingFlag,
"Can't create kext: path in identifier cache entry %d "
"doesn't name a kext.",
(int)entryIndex);
goto finish;
}
fullPath = CFStringCreateWithFormat(allocator,
0, CFSTR("%@/%@"), basePath, bundlePath);
if (!fullPath) {
OSKextLogMemError();
goto finish;
}
bundleURL = CFURLCreateWithFileSystemPath(allocator,
fullPath, kCFURLPOSIXPathStyle, true);
if (!bundleURL) {
OSKextLogMemError();
goto finish;
}
__OSKextGetFileSystemPath( NULL, bundleURL,
TRUE, kextPath);
existingKext = (OSKextRef)CFDictionaryGetValue(__sOSKextsByURL, bundleURL);
if (existingKext) {
result = existingKext;
} else {
newKext = __OSKextAlloc(allocator, NULL);
if (!newKext) {
OSKextLogMemError();
goto finish;
}
newKext->staticFlags.isFromIdentifierCache = 1;
newKext->bundleURL = CFRetain(bundleURL);
}
bundleID = (CFStringRef)CFDictionaryGetValue(cacheDict,
kCFBundleIdentifierKey);
if (!bundleID || (CFGetTypeID(bundleID) != CFStringGetTypeID())) {
OSKextLog(existingKext,
kOSKextLogErrorLevel | kOSKextLogKextBookkeepingFlag,
"Can't create kext: missing or non-string CFBundleIdentifier "
"in identifier cache entry %d.",
(int)entryIndex);
goto finish;
}
if (existingKext) {
if (!CFEqual(bundleID, OSKextGetIdentifier(existingKext))) {
OSKextLog(existingKext,
kOSKextLogErrorLevel | kOSKextLogKextBookkeepingFlag,
"Can't create kext from cache: %s is already open and "
"has a different CFBundleIdentifier "
"from identifier->path cache entry %d.",
kextPath, (int)entryIndex);
goto finish;
}
} else {
newKext->bundleID = CFRetain(bundleID);
}
bundleVersion = CFDictionaryGetValue(cacheDict, kCFBundleVersionKey);
if (!bundleVersion || (CFGetTypeID(bundleVersion) != CFStringGetTypeID())) {
OSKextLog(existingKext,
kOSKextLogErrorLevel | kOSKextLogKextBookkeepingFlag,
"Can't create kext: missing or non-string version "
"in identifier cache entry %d.",
(int)entryIndex);
goto finish;
}
kextVersion = OSKextParseVersionCFString(bundleVersion);
if (kextVersion < 0) {
OSKextLog(existingKext,
kOSKextLogErrorLevel | kOSKextLogKextBookkeepingFlag,
"Can't create kext: invalid CFBundleVersion "
"in identifier cache entry entry %d.",
(int)entryIndex);
goto finish;
}
if (existingKext) {
if (kextVersion != OSKextGetVersion(existingKext)) {
OSKextLog(existingKext,
kOSKextLogErrorLevel | kOSKextLogKextBookkeepingFlag,
"Can't create kext from cache: %s is already open and "
"has a different CFBundleVersion "
"from identifier->path cache entry %d.",
kextPath, (int)entryIndex);
goto finish;
}
} else {
newKext->version = kextVersion;
}
if (newKext) {
scratchBool = (CFBooleanRef)CFDictionaryGetValue(cacheDict,
CFSTR(kOSBundleEnableKextLoggingKey));
if (scratchBool) {
if (CFGetTypeID(scratchBool) != CFBooleanGetTypeID()) {
OSKextLog(existingKext,
kOSKextLogErrorLevel | kOSKextLogKextBookkeepingFlag,
"Can't create kext from cache: non-boolean "
"OSKextEnableKextLogging in identifier cache entry %d.",
(int)entryIndex);
goto finish;
}
newKext->flags.plistHasEnableLoggingSet = CFBooleanGetValue(scratchBool) ? 1 : 0;
newKext->flags.loggingEnabled = CFBooleanGetValue(scratchBool) ? 1 : 0;
}
}
if (!existingKext) {
result = newKext;
}
finish:
if (result) {
CFRetain(result);
}
SAFE_RELEASE(newKext);
SAFE_RELEASE(fullPath);
SAFE_RELEASE(bundleURL);
return result;
}
void __OSKextRealize(const void * vKext, void * context __unused)
{
OSKextRef aKext = (OSKextRef)vKext;
CFStringRef cachedBundleID = NULL;
OSKextVersion cachedVersion = -1;
Boolean removeCache = false;
char kextPath[PATH_MAX];
if (!aKext->staticFlags.isFromIdentifierCache) {
goto finish;
}
__OSKextGetFileSystemPath(aKext, NULL,
false, kextPath);
OSKextLog(aKext,
kOSKextLogDetailLevel | kOSKextLogKextBookkeepingFlag,
"Realizing %s from identifier cache object.",
kextPath);
__OSKextRemoveKextFromIdentifierDict(aKext, __sOSKextsByIdentifier);
cachedBundleID = aKext->bundleID;
cachedVersion = aKext->version;
aKext->version = -1;
aKext->bundleID = CFSTR(__kOSKextUnknownIdentifier);
__OSKextProcessInfoDictionary(aKext, NULL);
if (aKext->bundleID == CFSTR(__kOSKextUnknownIdentifier)) {
removeCache = true;
}
if (cachedVersion != aKext->version ||
!CFEqual(cachedBundleID, aKext->bundleID)) {
removeCache = true;
}
finish:
aKext->staticFlags.isFromIdentifierCache = 0;
if (removeCache) {
__OSKextRemoveIdentifierCacheForKext(aKext);
}
SAFE_RELEASE(cachedBundleID); return;
}
void __OSKextRealizeKextsWithIdentifier(
CFStringRef kextIdentifier)
{
CFTypeRef foundEntry = NULL; OSKextRef theKext = NULL;
if (!__sOSKextsByIdentifier) {
goto finish;
}
foundEntry = CFDictionaryGetValue(__sOSKextsByIdentifier, kextIdentifier);
if (!foundEntry) {
goto finish;
}
CFRetain(foundEntry);
if (OSKextGetTypeID() == CFGetTypeID(foundEntry)) {
theKext = (OSKextRef)foundEntry;
__OSKextRealize(theKext, NULL);
} else if (CFArrayGetTypeID() == CFGetTypeID(foundEntry)) {
CFMutableArrayRef kexts = (CFMutableArrayRef)foundEntry;
if (!CFArrayGetCount(kexts)) {
goto finish;
}
CFArrayApplyFunction(kexts, RANGE_ALL(kexts),
&__OSKextRealize, NULL);
}
finish:
SAFE_RELEASE(foundEntry);
return;
}
CFURLRef __OSKextCreateCacheFileURL(
CFTypeRef folderURLsOrURL,
CFStringRef cacheName,
const NXArchInfo * arch,
_OSKextCacheFormat format)
{
CFURLRef result = NULL;
Boolean isStartup = FALSE;
CFStringRef folderAbsPath = NULL; CFStringRef cacheFileString = NULL; char * suffix = "";
if (CFGetTypeID(folderURLsOrURL) == CFURLGetTypeID()) {
CFURLRef folderURL = (CFURLRef)folderURLsOrURL;
folderAbsPath = _CFURLCopyAbsolutePath(folderURL);
if (!folderAbsPath) {
OSKextLogMemError();
goto finish;
}
if (!__OSKextURLIsSystemFolder(folderURL)) {
OSKextLogCFString( NULL,
kOSKextLogProgressLevel | kOSKextLogKextBookkeepingFlag,
CFSTR("%@ is not a system extensions folder; not looking for a cache."),
folderAbsPath);
goto finish;
}
} else if (folderURLsOrURL == OSKextGetSystemExtensionsFolderURLs()) {
isStartup = TRUE;
} else {
goto finish;
}
switch (format) {
case _kOSKextCacheFormatRaw:
break;
case _kOSKextCacheFormatCFXML: case _kOSKextCacheFormatCFBinary:
suffix = ".plist.gz";
break;
case _kOSKextCacheFormatIOXML:
suffix = ".ioplist.gz";
break;
}
cacheFileString = CFStringCreateWithFormat(kCFAllocatorDefault,
NULL, CFSTR("%s/%s%@/%@%s%s%s"),
_kOSKextCachesRootFolder,
isStartup ? _kOSKextStartupCachesSubfolder : _kOSKextDirectoryCachesSubfolder,
isStartup ? CFSTR("") : folderAbsPath,
cacheName,
arch ? "_" : "",
arch ? arch->name : "",
suffix);
if (!cacheFileString) {
OSKextLogMemError();
goto finish;
}
result = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
cacheFileString, kCFURLPOSIXPathStyle, false);
if (!result) {
OSKextLogMemError();
goto finish;
}
finish:
SAFE_RELEASE(folderAbsPath);
SAFE_RELEASE(cacheFileString);
return result;
}
Boolean __OSKextCheckURL(CFURLRef anURL, Boolean writeCreateFlag)
{
Boolean result = false;
char path[PATH_MAX] = "";
char * statPath = NULL; char * slashPtr = NULL; struct stat statBuffer;
if (!__OSKextGetFileSystemPath( NULL, anURL,
TRUE, path)) {
goto finish;
}
if (path[0] != '/') {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Internal error, invalid argument to __OSKextCheckURL.");
goto finish;
}
slashPtr = &path[0];
while (1) {
if (slashPtr == path) {
statPath = "/";
} else {
statPath = path;
if (slashPtr) {
slashPtr[0] = '\0';
}
}
if (writeCreateFlag) {
struct statfs statfsBuffer;
if (0 == statfs(statPath, &statfsBuffer)) {
if (statfsBuffer.f_flags & MNT_RDONLY) {
OSKextLogCFString( NULL,
kOSKextLogProgressLevel | kOSKextLogFileAccessFlag,
CFSTR("Not saving %s - read-only filesystem."), path);
goto finish;
}
}
}
if (0 != stat(statPath, &statBuffer)) {
if (errno == ENOENT) {
if (writeCreateFlag) {
if (0 != mkdir(path, _kOSKextCacheFolderMode) &&
errno != EEXIST) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Failed to create directory %s - %s.",
statPath, strerror(errno));
goto finish;
}
} else {
goto finish;
}
} else {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Can't stat path %s - %s.",
statPath, strerror(errno));
goto finish;
}
}
if (statBuffer.st_uid != 0) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Can't create kext cache under %s - owner not root.",
statPath);
goto finish;
}
if (!S_ISDIR(statBuffer.st_mode) && statBuffer.st_gid != 0) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Can't create kext cache under %s - group not wheel.",
statPath);
goto finish;
}
if (!slashPtr) {
break;
} else if (slashPtr == path) {
slashPtr = index(path + 1, '/');
statPath = path;
} else {
slashPtr[0] = '/';
slashPtr++;
slashPtr = index(slashPtr, '/');
}
}
result = true;
finish:
if (slashPtr && slashPtr != path) {
slashPtr[0] = '/';
}
return result;
}
Boolean _OSKextCreateFolderForCacheURL(CFURLRef cacheFileURL)
{
Boolean result = false;
CFURLRef folderURL = NULL;
folderURL = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault,
cacheFileURL);
if (!folderURL) {
OSKextLogMemError();
goto finish;
}
result = __OSKextCheckURL(folderURL, true);
finish:
SAFE_RELEASE(folderURL);
return result;
}
Boolean __OSKextURLIsSystemFolder(CFURLRef folderURL)
{
Boolean result = false;
CFURLRef folderAbsURL = NULL;
folderAbsURL = CFURLCopyAbsoluteURL(folderURL);
if (!folderAbsURL) {
OSKextLogMemError();
goto finish;
}
if (kCFNotFound == CFArrayGetFirstIndexOfValue(
__sOSKextSystemExtensionsFolderURLs,
RANGE_ALL(__sOSKextSystemExtensionsFolderURLs),
folderAbsURL)) {
goto finish;
}
result = true;
finish:
SAFE_RELEASE(folderAbsURL);
return result;
}
Boolean __OSKextStatURL(
CFURLRef anURL,
Boolean * missingOut,
struct stat * statOut)
{
Boolean result = FALSE;
char path[PATH_MAX];
struct stat statBuf;
if (missingOut) {
*missingOut = FALSE;
}
if (!__OSKextGetFileSystemPath( NULL, anURL,
TRUE, path)) {
goto finish;
}
if (0 != stat(path, &statBuf)) {
if ((errno == ENOENT || errno == ENOTDIR) && missingOut) {
*missingOut = TRUE;
} else {
OSKextLogCFString( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
CFSTR("Can't stat %s - %s."),
path, strerror(errno));
}
goto finish;
}
result = TRUE;
finish:
if (statOut) {
*statOut = statBuf;
}
return result;
}
Boolean __OSKextStatURLsOrURL(
CFTypeRef folderURLsOrURL,
Boolean * missingOut,
struct stat * latestStatOut)
{
Boolean result = FALSE;
Boolean localMissing = FALSE;
struct stat newStatBuf;
struct stat latestStatBuf;
bzero(&latestStatBuf, sizeof(latestStatBuf));
if (CFGetTypeID(folderURLsOrURL) == CFURLGetTypeID()) {
result = __OSKextStatURL((CFURLRef)folderURLsOrURL,
missingOut, &latestStatBuf);
} else if (CFGetTypeID(folderURLsOrURL) == CFArrayGetTypeID()) {
CFArrayRef folderURLs = (CFArrayRef)folderURLsOrURL;
CFIndex count, i;
CFIndex successCount = 0;
count = CFArrayGetCount(folderURLs);
for (i = 0; i < count; i++) {
result = __OSKextStatURL((CFURLRef)
CFArrayGetValueAtIndex(folderURLs, i),
missingOut ? &localMissing : NULL,
&newStatBuf);
if (!result) {
continue;
}
successCount++;
if (i == 0 || (newStatBuf.st_mtime > latestStatBuf.st_mtime)) {
latestStatBuf = newStatBuf;
}
}
if (successCount > 0) {
result = TRUE;
}
if ((successCount < count) && missingOut) {
*missingOut = TRUE;
}
}
if (latestStatOut) {
*latestStatOut = latestStatBuf;
}
return result;
}
Boolean _OSKextWriteCache(
CFTypeRef folderURLsOrURL,
CFStringRef cacheName,
const NXArchInfo * arch,
_OSKextCacheFormat format,
CFPropertyListRef plist)
{
Boolean result = false;
CFURLRef folderAbsURL = NULL; CFStringRef folderAbsPath = NULL; CFStringRef cacheFileString = NULL; CFURLRef cacheFileURL = NULL; char tmpPath[PATH_MAX] = "";
char cachePath[PATH_MAX] = "";
char * unlinkPath = NULL; int fileDescriptor = -1; mode_t realUmask = 0;
CFWriteStreamRef plistStream = NULL; CFDataRef cacheData = NULL; const UInt8 * cacheDataPtr = NULL; gzFile outputGZFile = Z_NULL; CFIndex cacheDataLength = 0;
CFIndex bytesWritten = 0;
struct stat latestStat;
if (CFGetTypeID(folderURLsOrURL) == CFURLGetTypeID()) {
folderAbsURL = CFURLCopyAbsoluteURL((CFURLRef)folderURLsOrURL);
if (!folderAbsURL) {
OSKextLogMemError();
goto finish;
}
folderAbsPath = _CFURLCopyAbsolutePath((CFURLRef)folderURLsOrURL);
if (!folderAbsPath) {
OSKextLogMemError();
goto finish;
}
}
cacheFileURL = __OSKextCreateCacheFileURL(folderURLsOrURL,
cacheName, arch, format);
if (!cacheFileURL) {
goto finish;
}
if (!__OSKextGetFileSystemPath( NULL, cacheFileURL,
true, cachePath)) {
OSKextLogStringError( NULL);
goto finish;
}
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogKextBookkeepingFlag | kOSKextLogFileAccessFlag,
"Saving cache file %s.",
cachePath);
if (!_OSKextCreateFolderForCacheURL(cacheFileURL)) {
goto finish;
}
strlcpy(tmpPath, cachePath, sizeof(tmpPath));
if (strlcat(tmpPath, ".XXXXXX", sizeof(tmpPath)) > sizeof(tmpPath)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Temp cache file name too long: %s.",
tmpPath);
goto finish;
}
fileDescriptor = mkstemp(tmpPath);
if (-1 == fileDescriptor) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Can't create %s - %s.",
tmpPath, strerror(errno));
goto finish;
}
unlinkPath = tmpPath;
realUmask = umask(0);
umask(realUmask);
if (-1 == fchmod(fileDescriptor, _kOSKextCacheFileMode & ~realUmask)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Failed to set permissions for %s - %s.",
tmpPath, strerror(errno));
goto finish;
}
if (format == _kOSKextCacheFormatCFXML ||
format == _kOSKextCacheFormatCFBinary) {
CFPropertyListFormat cfPlistFormat;
if (format == _kOSKextCacheFormatCFXML) {
cfPlistFormat = kCFPropertyListBinaryFormat_v1_0;
} else {
cfPlistFormat = kCFPropertyListXMLFormat_v1_0;
}
plistStream = CFWriteStreamCreateWithAllocatedBuffers(
kCFAllocatorDefault, kCFAllocatorDefault);
if (!plistStream) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogKextBookkeepingFlag,
"Can't create CFWriteStream to save cache %s.",
cachePath);
goto finish;
}
CFWriteStreamOpen(plistStream);
CFPropertyListWriteToStream(plist, plistStream,
cfPlistFormat, NULL);
CFWriteStreamClose(plistStream);
cacheData = CFWriteStreamCopyProperty(plistStream,
kCFStreamPropertyDataWritten);
} else {
cacheData = IOCFSerialize(plist, 0);
}
if (!cacheData) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogKextBookkeepingFlag,
"Failed to serialize data for cache %s.",
cachePath);
goto finish;
}
cacheDataPtr = CFDataGetBytePtr(cacheData);
cacheDataLength = CFDataGetLength(cacheData);
if (!cacheDataPtr) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Unable to get data to create cache file %s.",
cachePath);
goto finish;
}
errno = 0;
outputGZFile = gzdopen(fileDescriptor, "w");
if (!outputGZFile) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Failed to open compression stream for %s - %s.",
cachePath, strerror(errno));
goto finish;
}
fileDescriptor = -1;
bytesWritten = 0;
while (bytesWritten < cacheDataLength) {
int bytesJustWritten = 0;
errno = 0;
bytesJustWritten = gzwrite(outputGZFile,
cacheDataPtr + bytesWritten, cacheDataLength - bytesWritten);
if (bytesJustWritten < 0) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Compressed write error for cache file %s - %s.",
cachePath, strerror(errno));
goto finish;
}
bytesWritten += bytesJustWritten;
}
errno = 0;
if (gzclose(outputGZFile) != Z_OK) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Failed to close compression stream for %s - %s.",
tmpPath, strerror(errno));
}
outputGZFile = Z_NULL;
if (-1 == rename(tmpPath, cachePath)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Can't rename temp cache file to %s - %s.",
cachePath, strerror(errno));
goto finish;
}
unlinkPath = cachePath;
if (!__OSKextStatURLsOrURL(folderURLsOrURL,
FALSE, &latestStat)) {
goto finish;
} else {
struct timeval cacheFileTimes[2];
cacheFileTimes[0].tv_sec = latestStat.st_mtime + 1;
cacheFileTimes[0].tv_usec = 0;
cacheFileTimes[1].tv_sec = latestStat.st_mtime + 1;
cacheFileTimes[1].tv_usec = 0;
if (-1 == utimes(cachePath, cacheFileTimes)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Can't update mod time of cache file %s - %s.",
cachePath, strerror(errno));
if (errno == ENOENT) {
unlinkPath = NULL;
}
goto finish;
}
}
unlinkPath = NULL;
result = true;
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogKextBookkeepingFlag | kOSKextLogFileAccessFlag,
"Saved cache file %s.", cachePath);
finish:
if (-1 != fileDescriptor) close(fileDescriptor);
if (Z_NULL != outputGZFile && gzclose(outputGZFile) != Z_OK) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Failed to close compression stream for %s - %s.",
tmpPath, strerror(errno));
}
if (unlinkPath) {
if (-1 == unlink(unlinkPath)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Failed to remove temp cache file %s - %s.",
unlinkPath, strerror(errno));
}
}
SAFE_RELEASE(folderAbsURL);
SAFE_RELEASE(folderAbsPath);
SAFE_RELEASE(cacheFileString);
SAFE_RELEASE(cacheFileURL);
SAFE_RELEASE(cacheData);
SAFE_RELEASE(plistStream);
return result;
}
Boolean __OSKextCacheNeedsUpdate(
CFURLRef cacheURL,
CFTypeRef folderURLsOrURL)
{
Boolean result = TRUE; Boolean missing = FALSE;
CFStringRef cachePath = NULL; struct stat cacheFileStat;
struct stat latestFolderStat;
cachePath = _CFURLCopyAbsolutePath(cacheURL);
if (!cachePath) {
goto finish;
}
if (!__OSKextStatURL(cacheURL, &missing, &cacheFileStat)) {
if (missing) {
OSKextLogCFString( NULL,
kOSKextLogDebugLevel |
kOSKextLogKextBookkeepingFlag | kOSKextLogFileAccessFlag,
CFSTR("Cache file %@ does not exist."),
cachePath);
}
goto finish;
}
if (!(cacheFileStat.st_mode & S_IFREG)) {
OSKextLogCFString( NULL,
kOSKextLogProgressLevel | kOSKextLogKextBookkeepingFlag | kOSKextLogFileAccessFlag,
CFSTR("Cache file %@ is not a regular file; ignoring."),
cachePath);
goto finish;
}
if (!__OSKextCheckURL(cacheURL, false)) {
goto finish;
}
if (cacheFileStat.st_uid != 0) {
OSKextLogCFString( NULL,
kOSKextLogWarningLevel |
kOSKextLogKextBookkeepingFlag | kOSKextLogFileAccessFlag,
CFSTR("Cache file %@ - owner not root; not using."),
cachePath);
goto finish;
}
if (cacheFileStat.st_gid != 0) {
OSKextLogCFString( NULL,
kOSKextLogWarningLevel |
kOSKextLogKextBookkeepingFlag | kOSKextLogFileAccessFlag,
CFSTR("Cache file %@ - group not wheel; not using."), cachePath);
goto finish;
}
if ((cacheFileStat.st_mode & _kOSKextCacheFileModeMask) != _kOSKextCacheFileMode) {
OSKextLogCFString( NULL,
kOSKextLogWarningLevel |
kOSKextLogKextBookkeepingFlag | kOSKextLogFileAccessFlag,
CFSTR("Cache file %@ - wrong permissions (%#o, should be %#o); not using."),
cachePath,
cacheFileStat.st_mode, _kOSKextCacheFileMode);
goto finish;
}
if (!__OSKextStatURLsOrURL(folderURLsOrURL,
&missing, &latestFolderStat)) {
OSKextLogCFString( NULL,
kOSKextLogWarningLevel |
kOSKextLogKextBookkeepingFlag | kOSKextLogFileAccessFlag,
CFSTR("Can't stat source folders for cache file %@."),
cachePath);
goto finish;
}
if (cacheFileStat.st_mtime != (latestFolderStat.st_mtime + 1)) {
OSKextLogCFString( NULL,
kOSKextLogWarningLevel |
kOSKextLogKextBookkeepingFlag | kOSKextLogFileAccessFlag,
CFSTR("Cache file %@ is out of date; not using."),
cachePath);
goto finish;
}
result = FALSE;
finish:
SAFE_RELEASE(cachePath);
return result;
}
Boolean _OSKextReadCache(
CFTypeRef folderURLsOrURL,
CFStringRef cacheName,
const NXArchInfo * arch,
_OSKextCacheFormat format,
Boolean parseXMLFlag,
CFPropertyListRef * cacheContentsOut)
{
Boolean result = false;
CFURLRef cacheFileURL = NULL; char cachePath[PATH_MAX] = "";
CFDataRef cacheData = NULL; SInt32 error = 0;
CFStringRef errorString = NULL; char * errorCString = NULL; CFDataRef uncompressedCacheData = NULL; ssize_t uncompressedByteSize = 0;
u_char * uncompressedBytes = NULL; z_stream zstream;
int zlibResult = Z_UNKNOWN;
int numReallocs;
Boolean inflateTried = false;
CFPropertyListRef cacheContents = NULL;
cacheFileURL = __OSKextCreateCacheFileURL(folderURLsOrURL,
cacheName, arch, format);
if (!cacheFileURL) {
goto finish;
}
if (!__OSKextGetFileSystemPath( NULL, cacheFileURL,
TRUE, cachePath)) {
goto finish;
}
if (__OSKextCacheNeedsUpdate(cacheFileURL, folderURLsOrURL)) {
goto finish;
}
if (!cacheContentsOut) {
result = true;
goto finish;
}
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogKextBookkeepingFlag | kOSKextLogFileAccessFlag,
"Reading cache file %s.",
cachePath);
if (!CFURLCreateDataAndPropertiesFromResource(
CFGetAllocator(cacheFileURL), cacheFileURL, &cacheData, NULL,
NULL, &error)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Can't open cache file %s, CF error %d.",
cachePath, (int)error);
goto finish;
}
zstream.next_in = (UInt8 *)CFDataGetBytePtr(cacheData);
zstream.avail_in = CFDataGetLength(cacheData);
zstream.zalloc = NULL;
zstream.zfree = NULL;
zstream.opaque = NULL;
uncompressedByteSize = GZIP_RATIO * zstream.avail_in;
uncompressedBytes = (u_char *)malloc(uncompressedByteSize);
if (!uncompressedBytes) {
OSKextLogMemError();
goto finish;
}
zstream.next_out = uncompressedBytes;
zstream.avail_out = uncompressedByteSize;
zlibResult = inflateInit2(&zstream, 15 + 32);
if (zlibResult != Z_OK) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Error initializing zlib uncompression for %s.",
cachePath);
goto finish;
}
inflateTried = true;
numReallocs = 0;
while (numReallocs < MAX_REALLOCS && zlibResult == Z_OK) {
zlibResult = inflate(&zstream, Z_NO_FLUSH);
if (zlibResult == Z_STREAM_END) {
break;
} else if ((zlibResult == Z_OK) || (zlibResult == Z_BUF_ERROR)) {
numReallocs++;
uncompressedByteSize *= 2;
uncompressedBytes = realloc(uncompressedBytes, uncompressedByteSize);
if (!uncompressedBytes) {
OSKextLogMemError();
goto finish;
}
zstream.next_out = uncompressedBytes + zstream.total_out;
zstream.avail_out = uncompressedByteSize - zstream.total_out;
zlibResult = Z_OK; } else {
break;
}
}
if (zlibResult != Z_STREAM_END) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Error uncompressing kext cache file %s - zlib returned %d - %s.",
cachePath, zlibResult, zstream.msg ? zstream.msg : "(unknown)");
goto finish;
}
uncompressedCacheData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
(const UInt8 *)uncompressedBytes, zstream.total_out, kCFAllocatorMalloc);
if (!uncompressedCacheData) {
OSKextLogMemError();
goto finish;
}
if (parseXMLFlag) {
if (format == _kOSKextCacheFormatCFXML ||
format == _kOSKextCacheFormatCFBinary) {
cacheContents = CFPropertyListCreateFromXMLData(kCFAllocatorDefault,
uncompressedCacheData, kCFPropertyListImmutable, &errorString);
} else if (format == _kOSKextCacheFormatIOXML) {
cacheContents = IOCFUnserialize((const char *)uncompressedBytes,
kCFAllocatorDefault,
0, &errorString);
} else {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogKextBookkeepingFlag | kOSKextLogFileAccessFlag,
"Invalid cache format %d specified.", format);
goto finish;
}
if (!cacheContents) {
errorCString = createUTF8CStringForCFString(errorString);
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogKextBookkeepingFlag | kOSKextLogFileAccessFlag,
"Can't read plist from cache file %s - %s.",
cachePath, errorCString ? errorCString : __kStringUnknown);
goto finish;
}
} else {
cacheContents = CFRetain(uncompressedCacheData);
}
result = true;
finish:
OSKextLog( NULL,
kOSKextLogProgressLevel |
kOSKextLogKextBookkeepingFlag | kOSKextLogFileAccessFlag,
"Finished reading cache file %s.",
cachePath);
if (result && cacheContentsOut && cacheContents) {
*cacheContentsOut = CFRetain(cacheContents);
}
SAFE_RELEASE(cacheFileURL);
SAFE_RELEASE(cacheContents);
SAFE_RELEASE(cacheData);
SAFE_RELEASE(errorString);
SAFE_RELEASE(uncompressedCacheData);
SAFE_FREE(errorCString);
if (inflateTried) {
inflateEnd(&zstream);
}
return result;
}
void __OSKextRemoveIdentifierCacheForKext(OSKextRef aKext)
{
char scratchPath[PATH_MAX];
const char * delRoot = NULL;
if (geteuid() != 0) {
return;
}
if (!__OSKextGetFileSystemPath(aKext, NULL,
true, scratchPath)) {
goto finish;
}
if (!strncmp(scratchPath,
_kOSKextSystemLibraryExtensionsFolder,
strlen(_kOSKextSystemLibraryExtensionsFolder))) {
delRoot = _kOSKextSystemLibraryExtensionsFolder;
} else if (!strncmp(scratchPath,
_kOSKextLibraryExtensionsFolder,
strlen(_kOSKextLibraryExtensionsFolder))){
delRoot = _kOSKextSystemLibraryExtensionsFolder;
} else if (!strncmp(scratchPath,
_kOSKextAppleInternalLibraryExtensionsFolder,
strlen(_kOSKextAppleInternalLibraryExtensionsFolder))){
delRoot = _kOSKextSystemLibraryExtensionsFolder;
}
if (!delRoot) {
goto finish;
}
OSKextLog( NULL,
kOSKextLogProgressLevel |
kOSKextLogKextBookkeepingFlag | kOSKextLogFileAccessFlag,
"Removing identifier->path cache %s.",
scratchPath);
scratchPath[0] = '\0';
strlcpy(scratchPath, _kOSKextCachesRootFolder, sizeof(scratchPath));
strlcat(scratchPath, delRoot, sizeof(scratchPath));
strlcat(scratchPath, _kOSKextIdentifierCacheBasename, sizeof(scratchPath));
if (unlink(scratchPath)) {
if (errno != ENOENT && errno != ENOTDIR) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Failed to remove identifier->path cache %s - %s.",
scratchPath, strerror(errno));
}
}
finish:
return;
}
Boolean _OSKextWriteIdentifierCacheForKextsInDirectory(
CFArrayRef kextArray,
CFURLRef directoryURL,
Boolean forceFlag)
{
Boolean result = false;
CFURLRef cacheFileURL = NULL; CFStringRef basePath = NULL; CFNumberRef cacheVersion = NULL; SInt32 cacheVersionValue = 0;
CFMutableDictionaryRef cacheDict = NULL; CFMutableArrayRef kextInfoArray = NULL; CFDictionaryRef kextDict = NULL; OSKextRef aKext = NULL; char origDirPath[PATH_MAX] = "";
CFIndex count, i;
if (!OSKextGetUsesCaches() && !forceFlag) {
goto finish;
}
if (geteuid() != 0) {
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogKextBookkeepingFlag,
"Not running as root; skipping save of identifier->path cache.");
goto finish;
}
cacheFileURL = __OSKextCreateCacheFileURL(
directoryURL,
CFSTR(_kOSKextIdentifierCacheBasename),
NULL,
_kOSKextCacheFormatCFBinary);
if (!cacheFileURL) {
goto finish;
}
cacheDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (!cacheDict) {
OSKextLogMemError();
goto finish;
}
if (!__OSKextGetFileSystemPath( NULL, directoryURL,
true, origDirPath)) {
goto finish;
}
basePath = CFStringCreateWithBytes(kCFAllocatorDefault,
(UInt8 *)origDirPath, strlen(origDirPath), kCFStringEncodingUTF8,
false);
if (!basePath) {
OSKextLogMemError();
goto finish;
}
CFDictionarySetValue(cacheDict, CFSTR(__kOSKextIdentifierCacheBasePathKey),
basePath);
kextInfoArray = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks);
if (!kextInfoArray) {
OSKextLogMemError();
goto finish;
}
CFDictionarySetValue(cacheDict, CFSTR(__kOSKextIdentifierCacheKextInfoKey),
kextInfoArray);
cacheVersionValue = __kOSKextIdentifierCacheCurrentVersion;
cacheVersion = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type,
&cacheVersionValue);
if (!kextInfoArray) {
OSKextLogMemError();
goto finish;
}
CFDictionarySetValue(cacheDict, CFSTR(__kOSKextIdentifierCacheVersionKey),
cacheVersion);
count = CFArrayGetCount(kextArray);
for (i = 0; i < count; i++) {
SAFE_RELEASE(kextDict);
aKext = (OSKextRef)CFArrayGetValueAtIndex(kextArray, i);
kextDict = __OSKextCreateIdentifierCacheDict(aKext, basePath);
if (!kextDict) {
continue;
}
CFArrayAppendValue(kextInfoArray, kextDict);
}
result = _OSKextWriteCache(directoryURL,
CFSTR(_kOSKextIdentifierCacheBasename),
NULL, _kOSKextCacheFormatCFBinary,
cacheDict);
finish:
SAFE_RELEASE(cacheFileURL);
SAFE_RELEASE(cacheDict);
SAFE_RELEASE(kextInfoArray);
SAFE_RELEASE(kextDict);
SAFE_RELEASE(cacheVersion);
SAFE_RELEASE(basePath);
return result;
}
CFDictionaryRef __OSKextCreateIdentifierCacheDict(
OSKextRef aKext,
CFStringRef basePath)
{
CFMutableDictionaryRef result = NULL;
CFMutableDictionaryRef preResult = NULL;
CFURLRef absURL = NULL; CFStringRef bundlePath = NULL; CFStringRef relativePath = NULL; CFStringRef scratchString = NULL; char bundlePathCString[PATH_MAX];
char basePathCString[PATH_MAX];
CFIndex baseLength, fullLength;
preResult = CFDictionaryCreateMutable(CFGetAllocator(aKext), 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (!preResult) {
OSKextLogMemError();
goto finish;
}
absURL = CFURLCopyAbsoluteURL(OSKextGetURL(aKext));
if (!absURL) {
OSKextLogMemError();
goto finish;
}
bundlePath = CFURLCopyFileSystemPath(absURL, kCFURLPOSIXPathStyle);
if (!bundlePath) {
OSKextLogMemError();
goto finish;
}
if (!CFStringHasPrefix(bundlePath, basePath)) {
CFStringGetCString(bundlePath, bundlePathCString,
sizeof(bundlePathCString), kCFStringEncodingUTF8);
CFStringGetCString(basePath, basePathCString,
sizeof(basePathCString), kCFStringEncodingUTF8);
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogKextBookkeepingFlag,
"%s not in base path %s for identifier->path cache.",
bundlePathCString, basePathCString);
}
fullLength = CFStringGetLength(bundlePath);
baseLength = 1 + CFStringGetLength(basePath); relativePath = CFStringCreateWithSubstring(CFGetAllocator(aKext),
bundlePath,
CFRangeMake(baseLength, fullLength - baseLength));
if (!relativePath) {
OSKextLogMemError();
goto finish;
}
CFDictionarySetValue(preResult, CFSTR("OSBundlePath"), relativePath);
scratchString = OSKextGetIdentifier(aKext);
if (!scratchString) {
goto finish;
}
CFDictionarySetValue(preResult, kCFBundleIdentifierKey, scratchString);
scratchString = OSKextGetValueForInfoDictionaryKey(aKext,
kCFBundleVersionKey);
if (!scratchString) {
goto finish;
}
CFDictionarySetValue(preResult, kCFBundleVersionKey, scratchString);
if (aKext->flags.loggingEnabled) {
CFDictionarySetValue(preResult, CFSTR(kOSBundleEnableKextLoggingKey),
kCFBooleanTrue);
}
result = preResult;
preResult = NULL;
finish:
SAFE_RELEASE(preResult);
SAFE_RELEASE(absURL);
SAFE_RELEASE(bundlePath);
SAFE_RELEASE(relativePath);
return result;
}
#pragma mark Instance Management (Continued)
OSKextRef OSKextCreateWithIdentifier(
CFAllocatorRef allocator,
CFStringRef kextIdentifier)
{
OSKextRef result = NULL;
CFArrayRef kextIDs = NULL; CFDictionaryRef loadedKextsInfo = NULL; CFDictionaryRef loadedKextInfo = NULL; CFStringRef kextPath = NULL; CFURLRef createdKextURL = NULL; OSKextRef createdKext = NULL; CFArrayRef allSystemKexts = NULL; OSKextRef lookedUpKext = NULL; CFStringRef kextVersionString = NULL; OSKextVersion kextVersion = -1;
char * pathCString = NULL;
kextIDs = CFArrayCreate(kCFAllocatorDefault,
(const void **)&kextIdentifier, 1,
&kCFTypeArrayCallBacks);
if (!kextIDs) {
OSKextLogMemError();
goto finish;
}
do {
loadedKextsInfo = OSKextCopyLoadedKextInfo(kextIDs, __sOSKextInfoEssentialKeys);
if (!loadedKextsInfo || (CFGetTypeID(loadedKextsInfo) != CFDictionaryGetTypeID())) {
break;
}
loadedKextInfo = (CFDictionaryRef)CFDictionaryGetValue(loadedKextsInfo, kextIdentifier);
if (!loadedKextInfo) {
break;
}
if (CFGetTypeID(loadedKextInfo) != CFDictionaryGetTypeID()) {
break;
}
kextPath = (CFStringRef)CFDictionaryGetValue(loadedKextInfo,
CFSTR(kOSBundlePathKey));
if (!kextPath || CFGetTypeID(kextPath) != CFStringGetTypeID()) {
kextPath = NULL;
}
kextVersionString = (CFStringRef)CFDictionaryGetValue(
loadedKextInfo, kCFBundleVersionKey);
if (!kextVersionString || CFGetTypeID(kextVersionString) != CFStringGetTypeID()) {
kextVersionString = NULL;
}
kextVersion = OSKextParseVersionCFString(kextVersionString);
} while (0);
if (kextPath) {
pathCString = createUTF8CStringForCFString(kextPath);
OSKextLog( NULL,
kOSKextLogDebugLevel | kOSKextLogKextBookkeepingFlag,
"Creating kext with path %s.", pathCString);
createdKextURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
kextPath, kCFURLPOSIXPathStyle, true);
if (!createdKextURL) {
OSKextLogMemError();
goto finish;
}
createdKext = OSKextCreate(allocator, createdKextURL);
if (result && CFEqual(OSKextGetIdentifier(result), kextIdentifier)) {
result = (OSKextRef)CFRetain(createdKext); }
}
if (!result) {
allSystemKexts = OSKextCreateKextsFromURLs(allocator,
OSKextGetSystemExtensionsFolderURLs());
if (kextVersion != -1) {
lookedUpKext = OSKextGetKextWithIdentifierAndVersion(kextIdentifier,
kextVersion);
}
if (!lookedUpKext) {
lookedUpKext = OSKextGetKextWithIdentifier(kextIdentifier);
}
if (lookedUpKext) {
result = (OSKextRef)CFRetain(lookedUpKext);
}
}
if (result && loadedKextInfo) {
__OSKextProcessLoadInfo(kextIdentifier, loadedKextInfo, NULL);
}
finish:
SAFE_FREE(pathCString);
SAFE_RELEASE(kextIDs);
SAFE_RELEASE(loadedKextsInfo);
SAFE_RELEASE(createdKextURL);
SAFE_RELEASE(createdKext);
SAFE_RELEASE(allSystemKexts);
return result;
}
CFArrayRef OSKextGetAllKexts(void)
{
pthread_once(&__sOSKextInitialized, __OSKextInitialize);
if (__sOSAllKexts) {
CFArrayApplyFunction(__sOSAllKexts, RANGE_ALL(__sOSAllKexts),
&__OSKextRealize, NULL);
}
return __sOSAllKexts;
}
OSKextRef OSKextGetKextWithURL(
CFURLRef anURL)
{
OSKextRef result = NULL;
CFURLRef canonicalURL = NULL; char relPath[PATH_MAX];
char absPath[PATH_MAX];
pthread_once(&__sOSKextInitialized, __OSKextInitialize);
if (!__OSKextGetFileSystemPath( NULL, anURL,
false, relPath) ||
!__OSKextGetFileSystemPath( NULL, anURL,
true, absPath)) {
goto finish;
}
canonicalURL = CFURLCreateFromFileSystemRepresentation(CFGetAllocator(anURL),
(uint8_t *)absPath, strlen(absPath), true);
if (!canonicalURL) {
OSKextLogMemError();
goto finish;
}
if (__sOSKextsByURL) {
result = (OSKextRef)CFDictionaryGetValue(__sOSKextsByURL, canonicalURL);
if (result) {
if (result->staticFlags.isFromIdentifierCache) {
__OSKextRealize(result, NULL);
}
goto finish;
}
}
finish:
SAFE_RELEASE(canonicalURL);
return result;
}
OSKextRef OSKextGetKextWithIdentifier(
CFStringRef aBundleID)
{
OSKextRef result = NULL;
CFTypeRef foundEntry = NULL; OSKextRef theKext = NULL;
if (!__sOSKextsByIdentifier) {
goto finish;
}
__OSKextRealizeKextsWithIdentifier(aBundleID);
foundEntry = CFDictionaryGetValue(__sOSKextsByIdentifier, aBundleID);
if (!foundEntry) {
goto finish;
}
if (OSKextGetTypeID() == CFGetTypeID(foundEntry)) {
theKext = (OSKextRef)foundEntry;
result = theKext;
} else if (CFArrayGetTypeID() == CFGetTypeID(foundEntry)) {
CFMutableArrayRef kexts = (CFMutableArrayRef)foundEntry;
if (CFArrayGetCount(kexts)) {
result = (OSKextRef)CFArrayGetValueAtIndex(kexts, 0);
}
}
finish:
return result;
}
OSKextRef OSKextGetKextWithIdentifierAndVersion(
CFStringRef aBundleID, OSKextVersion aVersion)
{
OSKextRef result = NULL;
CFTypeRef foundEntry = NULL; OSKextRef theKext = NULL;
if (!__sOSKextsByIdentifier) {
goto finish;
}
__OSKextRealizeKextsWithIdentifier(aBundleID);
foundEntry = CFDictionaryGetValue(__sOSKextsByIdentifier, aBundleID);
if (!foundEntry) {
goto finish;
}
if (OSKextGetTypeID() == CFGetTypeID(foundEntry)) {
theKext = (OSKextRef)foundEntry;
if (theKext->version == aVersion) {
result = theKext;
}
} else if (CFArrayGetTypeID() == CFGetTypeID(foundEntry)) {
CFMutableArrayRef kexts = (CFMutableArrayRef)foundEntry;
CFIndex count, i;
count = CFArrayGetCount(kexts);
for (i = 0; i < count; i++) {
theKext = (OSKextRef)CFArrayGetValueAtIndex(kexts, i);
if (theKext->version == aVersion) {
result = theKext;
goto finish;
}
}
}
finish:
return result;
}
OSKextRef OSKextGetLoadedKextWithIdentifier(
CFStringRef aBundleID)
{
OSKextRef result = NULL;
CFTypeRef foundEntry = NULL; OSKextRef theKext = NULL;
__OSKextRealizeKextsWithIdentifier(aBundleID);
foundEntry = CFDictionaryGetValue(__sOSKextsByIdentifier, aBundleID);
if (!foundEntry) {
goto finish;
}
if (OSKextGetTypeID() == CFGetTypeID(foundEntry)) {
theKext = (OSKextRef)foundEntry;
if (OSKextIsLoaded(theKext)) {
result = theKext;
}
} else if (CFArrayGetTypeID() == CFGetTypeID(foundEntry)) {
CFMutableArrayRef kexts = (CFMutableArrayRef)foundEntry;
CFIndex count, i;
count = CFArrayGetCount(kexts);
for (i = 0; i < count; i++) {
theKext = (OSKextRef)CFArrayGetValueAtIndex(kexts, i);
if (OSKextIsLoaded(theKext)) {
result = theKext;
goto finish;
}
}
}
finish:
return result;
}
OSKextRef OSKextGetCompatibleKextWithIdentifier(
CFStringRef aBundleID,
OSKextVersion requestedVersion)
{
OSKextRef result = NULL;
CFTypeRef foundEntry = NULL;
if (!__sOSKextsByIdentifier) {
goto finish;
}
__OSKextRealizeKextsWithIdentifier(aBundleID);
foundEntry = CFDictionaryGetValue(__sOSKextsByIdentifier, aBundleID);
if (!foundEntry) {
goto finish;
}
if (OSKextGetTypeID() == CFGetTypeID(foundEntry)) {
OSKextRef theKext = (OSKextRef)foundEntry;
if (OSKextIsCompatibleWithVersion(theKext, requestedVersion)) {
if (__sOSKextStrictAuthentication) {
if (OSKextIsValid(theKext) && OSKextIsAuthentic(theKext)) {
result = theKext;
} else {
OSKextLogCFString( NULL,
kOSKextLogErrorLevel | kOSKextLogValidationFlag,
CFSTR("Rejecting invalid/inauthentic kext for bundle id %@ at location %@."),
aBundleID, OSKextGetURL(theKext));
}
} else {
result = theKext;
}
}
} else if (CFArrayGetTypeID() == CFGetTypeID(foundEntry)) {
CFMutableArrayRef kexts = (CFMutableArrayRef)foundEntry;
CFIndex count, i;
count = CFArrayGetCount(kexts);
for (i = 0; i < count; i++) {
OSKextRef thisKext = (OSKextRef)CFArrayGetValueAtIndex(kexts, i);
if (OSKextIsCompatibleWithVersion(thisKext, requestedVersion)) {
if (__sOSKextStrictAuthentication) {
if (OSKextIsValid(thisKext) && OSKextIsAuthentic(thisKext)) {
result = thisKext;
goto finish;
} else {
OSKextLogCFString( NULL,
kOSKextLogErrorLevel | kOSKextLogValidationFlag,
CFSTR("Rejecting invalid/inauthentic kext for bundle id %@ at location %@."),
aBundleID, OSKextGetURL(thisKext));
}
} else {
result = thisKext;
goto finish;
}
}
}
}
finish:
return result;
}
CFArrayRef OSKextCopyKextsWithIdentifier(
CFStringRef aBundleID)
{
CFArrayRef result = NULL;
CFTypeRef foundEntry = NULL; CFArrayRef realizeArray = NULL;
pthread_once(&__sOSKextInitialized, __OSKextInitialize);
__OSKextRealizeKextsWithIdentifier(aBundleID);
if (__sOSKextsByIdentifier) {
foundEntry = CFDictionaryGetValue(__sOSKextsByIdentifier, aBundleID);
}
if (!foundEntry) {
goto finish;
} else if (OSKextGetTypeID() == CFGetTypeID(foundEntry)) {
OSKextRef theKext = (OSKextRef)foundEntry;
result = CFArrayCreate(kCFAllocatorDefault, (const void **)&theKext, 1,
&kCFTypeArrayCallBacks);
} else if (CFArrayGetTypeID() == CFGetTypeID(foundEntry)) {
CFMutableArrayRef kexts = (CFMutableArrayRef)foundEntry;
result = CFArrayCreateCopy(kCFAllocatorDefault, kexts);
}
finish:
SAFE_RELEASE(realizeArray);
if (!result) {
result = CFArrayCreate(kCFAllocatorDefault, NULL, 0,
&kCFTypeArrayCallBacks);
}
return result;
}
CFComparisonResult __OSKextBundleIDCompare(const void *val1,
const void *val2, void *context __unused)
{
return CFStringCompare(val1, val2, 0);
}
CFArrayRef OSKextCopyAllRequestedIdentifiers(void)
{
CFMutableArrayRef result = NULL;
CFRange resultRange;
CFSetRef requestedIdentifiers = NULL; OSReturn op_result = kOSReturnError;
CFMutableDictionaryRef requestDict = NULL; const void ** values = NULL; int i = 0;
OSKextLog( NULL,
kOSKextLogDebugLevel | kOSKextLogIPCFlag,
"Reading list of all kexts requested by kernel since startup.");
requestDict = __OSKextCreateKextRequest(
CFSTR(kKextRequestPredicateGetAllLoadRequests),
NULL, NULL);
op_result = __OSKextSendKextRequest( NULL, requestDict,
(CFTypeRef *)&requestedIdentifiers, NULL,
NULL);
if (op_result != kOSReturnSuccess) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Failed to read kexts requested by kernel since startup - %s.",
safe_mach_error_string(op_result));
goto finish;
}
if (!requestedIdentifiers ||
CFSetGetTypeID() != CFGetTypeID(requestedIdentifiers))
{
goto finish;
}
values = malloc(CFSetGetCount(requestedIdentifiers) * sizeof(*values));
if (!values) {
OSKextLogMemError();
goto finish;
}
CFSetGetValues(requestedIdentifiers, values);
result = CFArrayCreateMutable(kCFAllocatorDefault,
CFSetGetCount(requestedIdentifiers), &kCFTypeArrayCallBacks);
if (!result) {
OSKextLogMemError();
goto finish;
}
for (i = 0; i < CFSetGetCount(requestedIdentifiers); ++i) {
CFArrayAppendValue(result, values[i]);
}
resultRange.location = 0;
resultRange.length = CFArrayGetCount(result);
CFArraySortValues(result, resultRange, &__OSKextBundleIDCompare, NULL);
finish:
SAFE_RELEASE(requestDict);
SAFE_RELEASE(requestedIdentifiers);
SAFE_FREE(values);
return result;
}
CFMutableArrayRef OSKextCopyKextsWithIdentifiers(CFArrayRef kextIdentifiers)
{
CFMutableArrayRef result = NULL;
CFMutableArrayRef kexts = NULL; CFArrayRef kextsWithIdentifier = NULL; char * kextIdentifierCString = NULL; int count, i;
count = CFArrayGetCount(kextIdentifiers);
kexts = CFArrayCreateMutable(kCFAllocatorDefault,
count, &kCFTypeArrayCallBacks);
if (!kexts) {
OSKextLogMemError();
goto finish;
}
for (i = 0; i < count; ++i) {
CFStringRef kextIdentifier = NULL;
SAFE_RELEASE_NULL(kextsWithIdentifier);
SAFE_FREE_NULL(kextIdentifierCString);
kextIdentifier = CFArrayGetValueAtIndex(kextIdentifiers, i);
kextsWithIdentifier = OSKextCopyKextsWithIdentifier(kextIdentifier);
if (kextsWithIdentifier) {
CFArrayAppendArray(kexts, kextsWithIdentifier,
RANGE_ALL(kextsWithIdentifier));
} else {
kextIdentifierCString = createUTF8CStringForCFString(kextIdentifier);
OSKextLog( NULL,
kOSKextLogDebugLevel | kOSKextLogKextBookkeepingFlag,
"Note: OSKextCopyKextsWithIdentifiers() - identifier %s not found.",
kextIdentifierCString);
}
}
result = kexts;
kexts = NULL;
finish:
SAFE_RELEASE(kexts);
SAFE_RELEASE(kextsWithIdentifier);
SAFE_FREE(kextIdentifierCString);
return result;
}
CFMutableArrayRef OSKextCopyLoadListForKexts(
CFArrayRef kexts,
Boolean needAllFlag)
{
CFMutableArrayRef result = NULL;
CFMutableArrayRef globalLoadList = NULL;
CFMutableSetRef resolvedKexts = NULL;
CFArrayRef loadList = NULL;
CFIndex kextCount, loadListCount, i, j;
resolvedKexts = CFSetCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeSetCallBacks);
if (!resolvedKexts) {
OSKextLogMemError();
goto finish;
}
globalLoadList = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks);
if (!globalLoadList) {
OSKextLogMemError();
goto finish;
}
kextCount = CFArrayGetCount(kexts);
for (i = 0; i < kextCount; ++i) {
Boolean valid = false;
SAFE_RELEASE_NULL(loadList);
OSKextRef theKext = (OSKextRef) CFArrayGetValueAtIndex(kexts, i);
if (CFSetGetValue(resolvedKexts, theKext)) continue;
valid = __OSKextIsValid(theKext);
if (!valid) {
char kextPath[PATH_MAX];
OSKextLogSpec logLevel = needAllFlag ? kOSKextLogErrorLevel :
kOSKextLogWarningLevel;
__OSKextGetFileSystemPath(theKext, NULL,
FALSE, kextPath);
OSKextLog(theKext,
logLevel | kOSKextLogGeneralFlag | kOSKextLogArchiveFlag,
"%s is not valid.",
kextPath);
if (needAllFlag) {
goto finish;
} else {
continue;
}
}
loadList = OSKextCopyLoadList(theKext, needAllFlag);
if (!loadList) {
goto finish;
}
loadListCount = CFArrayGetCount(loadList);
for (j = 0; j < loadListCount; ++j) {
OSKextRef listKext = (OSKextRef)CFArrayGetValueAtIndex(loadList, j);
if (CFSetGetValue(resolvedKexts, listKext)) continue;
CFArrayAppendValue(globalLoadList, listKext);
CFSetSetValue(resolvedKexts, listKext);
}
}
result = globalLoadList;
globalLoadList = NULL;
finish:
SAFE_RELEASE(resolvedKexts);
SAFE_RELEASE(globalLoadList);
SAFE_RELEASE(loadList);
return result;
}
#pragma mark Basic Accessors
CFURLRef OSKextGetURL(OSKextRef aKext)
{
return aKext->bundleURL;
}
Boolean __OSKextGetFileSystemPath(
OSKextRef aKext,
CFURLRef anURL,
Boolean resolveToBase,
char * pathBuffer)
{
Boolean result = false;
CFURLRef urlToUse = NULL;
if (aKext) {
if (aKext->bundleURL) {
urlToUse = aKext->bundleURL;
}
} else {
urlToUse = anURL;
}
if (!urlToUse) {
goto finish;
}
result = CFURLGetFileSystemRepresentation(urlToUse,
resolveToBase, (UInt8 *)pathBuffer, PATH_MAX);
finish:
if (!result) {
OSKextLogStringError(aKext);
memcpy(pathBuffer, __kStringUnknown, sizeof(__kStringUnknown));
}
return result;
}
CFStringRef OSKextGetIdentifier(OSKextRef aKext)
{
return aKext->bundleID;
}
#define COMPOSITE_KEY_SEPARATOR "_"
CFStringRef __OSKextCreateCompositeKey(
CFStringRef baseKey,
const char * auxKey)
{
return CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
CFSTR("%@%s%s"), baseKey, COMPOSITE_KEY_SEPARATOR, auxKey);
}
CFTypeRef __CFDictionaryGetValueForCompositeKey(
CFDictionaryRef aDict,
CFStringRef baseKey,
const char * auxKey)
{
CFTypeRef result = NULL;
CFStringRef compositeKey = NULL;
compositeKey = __OSKextCreateCompositeKey(baseKey, auxKey);
if (!compositeKey) {
OSKextLogMemError();
goto finish;
}
result = CFDictionaryGetValue(aDict, compositeKey);
if (!result) {
result = CFDictionaryGetValue(aDict, baseKey);
}
finish:
SAFE_RELEASE(compositeKey);
return result;
}
CFTypeRef OSKextGetValueForInfoDictionaryKey(
OSKextRef aKext,
CFStringRef key)
{
CFTypeRef result = NULL;
if (!__OSKextReadInfoDictionary(aKext, NULL)) {
goto finish;
}
if (CFStringHasPrefix(key, CFSTR("OS")) ||
CFStringHasPrefix(key, CFSTR("IO"))) {
const NXArchInfo * lookupArchInfo = NXGetArchInfoFromCpuType(
OSKextGetArchitecture()->cputype, CPU_SUBTYPE_MULTIPLE);
if (lookupArchInfo) {
result = __CFDictionaryGetValueForCompositeKey(aKext->infoDictionary,
key, lookupArchInfo->name);
}
}
if (!result) {
result = CFDictionaryGetValue(aKext->infoDictionary, key);
}
finish:
return result;
}
CFMutableDictionaryRef OSKextCopyInfoDictionary(OSKextRef aKext)
{
CFMutableDictionaryRef result = NULL;
if (!aKext->infoDictionary) {
if (!__OSKextReadInfoDictionary(aKext, NULL)) {
goto finish;
}
}
result = CFDictionaryCreateMutableCopy(CFGetAllocator(aKext), 0,
aKext->infoDictionary);
finish:
return result;
}
static void __OSKextFlushInfoDictionaryApplierFunction(
const void * vKey __unused,
const void * vValue,
void * vContext __unused)
{
OSKextRef theKext = (OSKextRef)vValue;
OSKextFlushInfoDictionary(theKext);
return;
}
void OSKextFlushInfoDictionary(OSKextRef aKext)
{
static Boolean flushingAll = false;
char kextPath[PATH_MAX];
pthread_once(&__sOSKextInitialized, __OSKextInitialize);
if (aKext) {
if (!flushingAll) {
if (OSKextGetURL(aKext)) {
__OSKextGetFileSystemPath(aKext, NULL,
false, kextPath);
}
OSKextLog( NULL,
kOSKextLogDetailLevel | kOSKextLogKextBookkeepingFlag,
"Flushing info dictionary for %s.",
kextPath);
}
if (!OSKextIsFromMkext(aKext)) {
SAFE_RELEASE_NULL(aKext->infoDictionary);
if (!aKext->flags.sip_protected) {
aKext->flags.valid = 0;
aKext->flags.invalid = 0;
aKext->flags.validated = 0;
aKext->flags.authentic = 0;
aKext->flags.inauthentic = 0;
aKext->flags.authenticated = 0;
aKext->flags.isSigned = 0;
}
}
} else if (__sOSKextsByURL) {
flushingAll = true;
OSKextLog( NULL,
kOSKextLogDetailLevel | kOSKextLogKextBookkeepingFlag,
"Flushing info dictionaries for all kexts.");
CFDictionaryApplyFunction(__sOSKextsByURL,
__OSKextFlushInfoDictionaryApplierFunction, NULL);
flushingAll = false;
}
return;
}
OSKextVersion OSKextGetVersion(OSKextRef aKext)
{
return aKext->version;
}
OSKextVersion OSKextGetCompatibleVersion(OSKextRef aKext)
{
return aKext->compatibleVersion;
}
struct _uuid_stuff {
unsigned int uuid_size;
char * uuid;
};
macho_seek_result __OSKextUUIDCallback(
struct load_command * load_command,
const void * file_end,
uint8_t swap __unused,
void * user_data)
{
struct _uuid_stuff * uuid_stuff = (struct _uuid_stuff *)user_data;
if (load_command->cmd == LC_UUID) {
struct uuid_command * uuid_command = (struct uuid_command *)load_command;
if (((void *)load_command + load_command->cmdsize) > file_end) {
return macho_seek_result_error;
}
uuid_stuff->uuid_size = sizeof(uuid_command->uuid);
uuid_stuff->uuid = (char *)uuid_command->uuid;
return macho_seek_result_found;
}
return macho_seek_result_not_found;
}
CFDataRef OSKextCopyUUIDForArchitecture(
OSKextRef aKext,
const NXArchInfo * arch)
{
CFDataRef result = NULL;
CFDataRef executable = NULL; const struct mach_header * mach_header = NULL;
const void * file_end;
macho_seek_result seek_result;
struct _uuid_stuff seek_uuid;
int swap = 0;
if (!arch) {
arch = OSKextGetArchitecture();
}
executable = OSKextCopyExecutableForArchitecture(aKext, arch);
if (!executable) {
goto finish;
}
mach_header = (const struct mach_header *)CFDataGetBytePtr(executable);
file_end = (((const char *)mach_header) + CFDataGetLength(executable));
if (ISSWAPPEDMACHO(MAGIC32(mach_header))) {
swap = 1;
}
seek_result = macho_scan_load_commands(
mach_header, file_end,
__OSKextUUIDCallback, (const void **)&seek_uuid);
if (seek_result == macho_seek_result_error) {
__OSKextSetDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticExecutableBadKey);
goto finish;
} else if (seek_result != macho_seek_result_found) {
goto finish;
}
result = CFDataCreate(CFGetAllocator(aKext), (u_char *)seek_uuid.uuid,
CondSwapInt32(swap, seek_uuid.uuid_size));
finish:
if (executable) {
(void)posix_madvise((void *)CFDataGetBytePtr(executable),
CFDataGetLength(executable),
POSIX_MADV_DONTNEED);
}
SAFE_RELEASE(executable);
return result;
}
Boolean OSKextIsKernelComponent(OSKextRef aKext)
{
return aKext->flags.isKernelComponent ? true : false;
}
Boolean OSKextIsInterface(OSKextRef aKext)
{
return aKext->flags.isInterface ? true : false;
}
Boolean OSKextIsLibrary(OSKextRef aKext)
{
return (aKext->compatibleVersion > 0) ? true : false;
}
Boolean OSKextDeclaresExecutable(OSKextRef aKext)
{
return aKext->flags.declaresExecutable ? true : false;
}
Boolean OSKextHasLogOrDebugFlags(OSKextRef aKext)
{
return aKext->flags.plistHasEnableLoggingSet ||
aKext->flags.plistHasIOKitDebugFlags;
}
Boolean OSKextIsLoggingEnabled(OSKextRef aKext)
{
return aKext->flags.loggingEnabled;
}
void OSKextSetLoggingEnabled(
OSKextRef aKext,
Boolean flag)
{
unsigned int oldValue = aKext->flags.loggingEnabled;
aKext->flags.loggingEnabled = flag ? 1 : 0;
if (oldValue != aKext->flags.loggingEnabled) {
char kextPath[PATH_MAX];
__OSKextGetFileSystemPath(aKext, NULL,
false, kextPath);
OSKextLog(aKext,
kOSKextLogDebugLevel | kOSKextLogKextBookkeepingFlag,
"Kext logging %sabled for %s.",
aKext->flags.loggingEnabled ? "en" : "dis",
kextPath);
}
return;
}
Boolean OSKextIsLoadableInSafeBoot(OSKextRef aKext)
{
return aKext->flags.isLoadableInSafeBoot ? true : false;
}
Boolean OSKextDependenciesAreLoadableInSafeBoot(OSKextRef aKext)
{
Boolean result = true;
CFArrayRef allDependencies = NULL; CFIndex count, i;
allDependencies = OSKextCopyAllDependencies(aKext,
true);
if (!allDependencies) {
result = false;
goto finish;
}
count = CFArrayGetCount(allDependencies);
for (i = 0; i < count; i++) {
OSKextRef thisKext = (OSKextRef)CFArrayGetValueAtIndex(
allDependencies, i);
if ((OSKextGetActualSafeBoot() || OSKextGetSimulatedSafeBoot()) &&
!OSKextIsLoadableInSafeBoot(thisKext)) {
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagBootLevel,
kOSKextDependencyIneligibleInSafeBoot,
OSKextGetIdentifier(thisKext), NULL);
result = false;
}
}
finish:
SAFE_RELEASE(allDependencies);
return result;
}
const NXArchInfo ** OSKextCopyArchitectures(OSKextRef aKext)
{
const NXArchInfo ** result = NULL;
const UInt8 * executable = NULL; const UInt8 * executableEnd = NULL; fat_iterator fatIterator = NULL; char urlPath[PATH_MAX];
int numArches = 0;
int resultSize = 0;
struct mach_header * machHeader = NULL;
uint32_t index;
if (!OSKextDeclaresExecutable(aKext) || !__OSKextReadExecutable(aKext)) {
goto finish;
}
executable = CFDataGetBytePtr(aKext->loadInfo->executable);
executableEnd = executable + CFDataGetLength(aKext->loadInfo->executable);
fatIterator = fat_iterator_for_data(executable, executableEnd,
1 );
if (!fatIterator) {
__OSKextGetFileSystemPath(aKext,
OSKextGetExecutableURL(aKext),
false, urlPath);
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Can't read mach-o file %s.",
urlPath);
goto finish;
}
numArches = fat_iterator_num_arches(fatIterator);
resultSize = (1 + numArches) * sizeof(NXArchInfo *);
result = (const NXArchInfo **)malloc(resultSize);
if (!result) {
goto finish;
}
bzero(result, resultSize);
for (index = 0;
(machHeader = (struct mach_header *)fat_iterator_next_arch(
fatIterator, NULL));
index++) {
int swap = ISSWAPPEDMACHO(machHeader->magic);
cpu_type_t cputype = CondSwapInt32(swap, machHeader->cputype);
cpu_subtype_t cpusubtype = CondSwapInt32(swap, machHeader->cpusubtype);
result[index] = NXGetArchInfoFromCpuType(cputype, cpusubtype);
}
finish:
if (executable) {
(void)posix_madvise((void *)executable, executableEnd - executable,
POSIX_MADV_DONTNEED);
}
if (fatIterator) {
fat_iterator_close(fatIterator);
}
return result;
}
Boolean OSKextSupportsArchitecture(OSKextRef aKext,
const NXArchInfo * archInfo)
{
Boolean result = false;
CFDataRef executable = NULL; fat_iterator fatIterator = NULL; const UInt8 * exec = NULL; void * thinExec;
void * thinExecEnd;
if (!OSKextDeclaresExecutable(aKext)) {
result = true;
goto finish;
}
if (!archInfo) {
archInfo = OSKextGetArchitecture();
}
if (!__OSKextReadExecutable(aKext)) {
goto finish;
}
if (aKext->staticFlags.isFromMkext) {
if (aKext->mkextInfo && aKext->mkextInfo->executable) {
executable = CFRetain(aKext->mkextInfo->executable);
}
} else {
if (aKext->loadInfo && aKext->loadInfo->executable) {
executable = CFRetain(aKext->loadInfo->executable);
}
}
if (!executable) {
goto finish;
}
exec = CFDataGetBytePtr(executable);
fatIterator = fat_iterator_for_data(exec,
exec + CFDataGetLength(executable), 1 );
if (!fatIterator) {
goto finish;
}
thinExec = fat_iterator_find_arch(fatIterator,
archInfo->cputype, archInfo->cpusubtype, &thinExecEnd);
if (!thinExec || (thinExec == thinExecEnd)) {
goto finish;
}
result = true;
finish:
if (executable) {
(void)posix_madvise((void *)CFDataGetBytePtr(executable),
CFDataGetLength(executable),
POSIX_MADV_DONTNEED);
}
SAFE_RELEASE(executable);
if (fatIterator) fat_iterator_close(fatIterator);
return result;
}
CFArrayRef OSKextCopyPlugins(OSKextRef aKext)
{
CFArrayRef result = NULL;
CFBundleRef kextBundle = NULL; CFURLRef pluginsURL = NULL; CFArrayRef pluginURLs = NULL;
if (OSKextIsPlugin(aKext)) {
result = CFArrayCreate(CFGetAllocator(aKext), NULL, 0,
&kCFTypeArrayCallBacks);
goto finish;
}
kextBundle = CFBundleCreate(kCFAllocatorDefault, aKext->bundleURL);
if (!kextBundle) {
goto finish;
}
pluginsURL = CFBundleCopyBuiltInPlugInsURL(kextBundle);
if (!pluginsURL) {
result = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks);
goto finish;
}
result = __OSKextCreateKextsFromURL(kCFAllocatorDefault, pluginsURL,
aKext, false);
finish:
SAFE_RELEASE(kextBundle);
SAFE_RELEASE(pluginsURL);
SAFE_RELEASE(pluginURLs);
return result;
}
Boolean OSKextIsPlugin(OSKextRef aKext)
{
Boolean result = false;
CFURLRef absURL = NULL; CFURLRef parentURL = NULL; CFStringRef parentPath = NULL; CFRange findRange;
if (aKext->staticFlags.isPluginChecked) {
result = aKext->staticFlags.isPlugin;
goto finish;
}
if (!aKext->bundleURL) {
OSKextLog(aKext,
kOSKextLogErrorLevel |
kOSKextLogGeneralFlag | kOSKextLogKextBookkeepingFlag,
"Bundle URL unexpectedly NULL!");
goto finish;
}
absURL = CFURLCopyAbsoluteURL(aKext->bundleURL);
if (!absURL) {
OSKextLogMemError();
goto finish;
}
parentURL = CFURLCreateCopyDeletingLastPathComponent(
kCFAllocatorDefault, absURL);
if (!parentURL) {
OSKextLogMemError();
goto finish;
}
parentPath = CFURLCopyFileSystemPath(parentURL, kCFURLPOSIXPathStyle);
if (!parentPath) {
OSKextLogMemError();
}
findRange = CFStringFind(parentPath, CFSTR(__sOSKextFullBundleExtension),
0);
aKext->staticFlags.isPlugin = (findRange.location == kCFNotFound) ? 0 : 1;
aKext->staticFlags.isPluginChecked = 1;
result = aKext->staticFlags.isPlugin;
finish:
SAFE_RELEASE(absURL);
SAFE_RELEASE(parentURL);
SAFE_RELEASE(parentPath);
return result;
}
OSKextRef OSKextCopyContainerForPluginKext(OSKextRef aKext)
{
OSKextRef result = NULL;
CFURLRef absURL = NULL; CFURLRef parentURL = NULL; CFStringRef parentPath = NULL; CFStringRef containerPath = NULL; CFURLRef containerURL = NULL; CFRange findRange;
OSKextRef potentialContainer = NULL; CFBundleRef pContainerBundle = NULL; CFURLRef pluginsURL = NULL; CFURLRef checkURL = NULL; char potentialContainerPath[PATH_MAX];
char canonicalPath[PATH_MAX];
char scratchPath[PATH_MAX];
if (aKext->staticFlags.isPluginChecked && !aKext->staticFlags.isPlugin) {
goto finish;
}
absURL = CFURLCopyAbsoluteURL(aKext->bundleURL);
if (!absURL) {
OSKextLogMemError();
goto finish;
}
parentURL = CFURLCreateCopyDeletingLastPathComponent(
kCFAllocatorDefault, absURL);
if (!parentURL) {
OSKextLogMemError();
goto finish;
}
parentPath = CFURLCopyFileSystemPath(parentURL, kCFURLPOSIXPathStyle);
if (!parentPath) {
OSKextLogMemError();
goto finish;
}
findRange = CFStringFind(parentPath, CFSTR(__sOSKextFullBundleExtension),
kCFCompareBackwards);
aKext->staticFlags.isPlugin = (findRange.location == kCFNotFound) ? 0 : 1;
aKext->staticFlags.isPluginChecked = 1;
if (!aKext->staticFlags.isPlugin) {
goto finish;
}
containerPath = CFStringCreateWithSubstring(kCFAllocatorDefault,
parentPath, CFRangeMake(0, findRange.location + findRange.length));
if (!containerPath) {
OSKextLogMemError();
goto finish;
}
if (!CFStringGetCString(containerPath, scratchPath, PATH_MAX,
kCFStringEncodingUTF8)) {
OSKextLogStringError(aKext);
goto finish;
}
containerURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
(const UInt8 *)scratchPath, strlen(scratchPath), true);
if (!containerURL) {
OSKextLogMemError();
goto finish;
}
potentialContainer = OSKextCreate(kCFAllocatorDefault, containerURL);
if (!potentialContainer) {
goto finish;
}
__OSKextGetFileSystemPath(potentialContainer, NULL,
false, potentialContainerPath);
OSKextLog(aKext, kOSKextLogDebugLevel | kOSKextLogFileAccessFlag,
"Opening CFBundle for %s.", potentialContainerPath);
pContainerBundle = CFBundleCreate(kCFAllocatorDefault,
potentialContainer->bundleURL);
if (!pContainerBundle) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Failed to open CFBundle for %s.", potentialContainerPath);
goto finish;
}
pluginsURL = CFBundleCopyBuiltInPlugInsURL(pContainerBundle);
checkURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault,
pluginsURL, CFURLCopyLastPathComponent(aKext->bundleURL), true);
if (!checkURL) {
OSKextLogMemError();
goto finish;
}
if (!__OSKextGetFileSystemPath( NULL, checkURL,
true, canonicalPath)) {
goto finish;
}
SAFE_RELEASE_NULL(checkURL);
checkURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
(uint8_t *)canonicalPath, strlen(canonicalPath), true);
if (!checkURL) {
OSKextLogMemError();
goto finish;
}
if (CFEqual(absURL, checkURL)) {
result = (OSKextRef)CFRetain(potentialContainer);
}
finish:
SAFE_RELEASE(absURL);
SAFE_RELEASE(parentURL);
SAFE_RELEASE(parentPath);
SAFE_RELEASE(containerPath);
SAFE_RELEASE(containerURL);
SAFE_RELEASE(potentialContainer);
if (pContainerBundle) {
OSKextLog(aKext,
kOSKextLogDebugLevel | kOSKextLogFileAccessFlag,
"Releasing CFBundle for %s.",
potentialContainerPath);
}
SAFE_RELEASE(pContainerBundle);
SAFE_RELEASE(pluginsURL);
SAFE_RELEASE(checkURL);
return result;
}
typedef struct {
OSKextRef kext;
CFMutableArrayRef personalities;
} __OSKextPersonalityBundleIdentifierContext;
static void __OSKextPersonalityBundleIdentifierApplierFunction(
const void * vKey,
const void * vValue,
void * vContext)
{
CFStringRef personalityName = (CFStringRef)vKey;
CFMutableDictionaryRef personality = (CFMutableDictionaryRef)vValue;
__OSKextPersonalityBundleIdentifierContext * context =
(__OSKextPersonalityBundleIdentifierContext *)vContext;
OSKextRef aKext = context->kext;
CFMutableArrayRef personalities = context->personalities;
CFStringRef bundleID = NULL;
CFMutableDictionaryRef personalityCopy = NULL; CFStringRef personalityBundleID = NULL; char kextPath[PATH_MAX];
char * bundleIDCString = NULL; char * personalityCString = NULL;
if (CFGetTypeID(personality) != CFDictionaryGetTypeID()) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogKextBookkeepingFlag,
"Kext personality %s subentry is not a dictionary",
CFStringGetCStringPtr(personalityName, kCFStringEncodingUTF8));
goto finish;
}
bundleID = OSKextGetIdentifier(aKext);
if (!bundleID) {
goto finish; }
personalityCopy = CFDictionaryCreateMutableCopy(CFGetAllocator(aKext),
0, personality);
if (!personalityCopy) {
OSKextLogMemError();
goto finish;
}
personalityBundleID = CFDictionaryGetValue(personality,
kCFBundleIdentifierKey);
if (!personalityBundleID) {
CFDictionarySetValue(personalityCopy, kCFBundleIdentifierKey, bundleID);
} else if (!CFEqual(bundleID, personalityBundleID)) {
CFDictionarySetValue(personalityCopy, CFSTR(kIOPersonalityPublisherKey),
bundleID);
}
if (__OSKextShouldLog(aKext, kOSKextLogDetailLevel | kOSKextLogLoadFlag)) {
__OSKextGetFileSystemPath(aKext, NULL,
false, kextPath);
bundleIDCString = createUTF8CStringForCFString(bundleID);
personalityCString = createUTF8CStringForCFString(personalityName);
if (!personalityBundleID) {
OSKextLog(aKext,
kOSKextLogDetailLevel | kOSKextLogLoadFlag,
"Adding CFBundleIdentifier %s to %s personality %s.",
bundleIDCString, kextPath, personalityCString);
} else if (!CFEqual(bundleID, personalityBundleID)) {
OSKextLog(aKext,
kOSKextLogDetailLevel | kOSKextLogLoadFlag,
"Adding IOBundlePublisher %s to %s personality %s.",
bundleIDCString, kextPath, personalityCString);
}
}
CFArrayAppendValue(personalities, personalityCopy);
finish:
SAFE_FREE(bundleIDCString);
SAFE_FREE(personalityCString);
SAFE_RELEASE(personalityCopy);
return;
}
CFArrayRef OSKextCopyPersonalitiesArray(OSKextRef aKext)
{
CFMutableArrayRef result = NULL;
CFDictionaryRef personalities = NULL; __OSKextPersonalityBundleIdentifierContext context;
result = CFArrayCreateMutable(CFGetAllocator(aKext), 0,
&kCFTypeArrayCallBacks);
if (!result) {
OSKextLogMemError();
goto finish;
}
personalities = OSKextGetValueForInfoDictionaryKey(aKext,
CFSTR(kIOKitPersonalitiesKey));
if (!personalities) {
goto finish;
} else if (CFGetTypeID(personalities) != CFDictionaryGetTypeID()) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogKextBookkeepingFlag,
"Kext personality for kext %s is not a dictionary",
CFStringGetCStringPtr(CFURLGetString(aKext->bundleURL), kCFStringEncodingUTF8));
goto finish;
} else if (!CFDictionaryGetCount(personalities)) {
goto finish;
}
context.kext = aKext;
context.personalities = result;
CFDictionaryApplyFunction(personalities,
__OSKextPersonalityBundleIdentifierApplierFunction,
&context);
finish:
return result;
}
CFArrayRef OSKextCopyPersonalitiesOfKexts(CFArrayRef kextArray)
{
CFMutableArrayRef result = NULL;
CFDictionaryRef kextPersonalities = NULL; __OSKextPersonalityBundleIdentifierContext context;
CFIndex count, i;
if (!kextArray) {
pthread_once(&__sOSKextInitialized, __OSKextInitialize);
kextArray = OSKextGetAllKexts();
}
result = CFArrayCreateMutable(CFGetAllocator(kextArray),
0, &kCFTypeArrayCallBacks);
if (!result) {
OSKextLogMemError();
goto finish;
}
context.personalities = result;
count = CFArrayGetCount(kextArray);
for (i = 0; i < count; i++) {
OSKextRef thisKext = (OSKextRef)CFArrayGetValueAtIndex(kextArray, i);
kextPersonalities = OSKextGetValueForInfoDictionaryKey(thisKext,
CFSTR(kIOKitPersonalitiesKey));
if (!kextPersonalities) {
continue;
} else if (CFGetTypeID(kextPersonalities) != CFDictionaryGetTypeID()) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogKextBookkeepingFlag,
"Kext personality for kext %s is not a dictionary",
CFStringGetCStringPtr(CFURLGetString(thisKext->bundleURL), kCFStringEncodingUTF8));
continue;
} else if (!CFDictionaryGetCount(kextPersonalities)) {
continue;
}
context.kext = thisKext;
CFDictionaryApplyFunction(kextPersonalities,
__OSKextPersonalityBundleIdentifierApplierFunction,
&context);
}
finish:
return result;
}
typedef struct {
size_t length;
} __OSKextMmapBufferInfo;
void __OSKextDeallocateMmapBuffer(void * pointer, void * vInfo)
{
__OSKextMmapBufferInfo * info = (__OSKextMmapBufferInfo *)vInfo;
munmap(pointer, info->length);
free(info);
return;
}
CFDataRef __OSKextMapExecutable(
OSKextRef aKext,
off_t offset,
off_t length);
CFDataRef __OSKextMapExecutable(
OSKextRef aKext,
off_t offset,
off_t length)
{
CFDataRef result = NULL;
int localErrno = 0;
CFURLRef executableURL = NULL; CFStringRef executableName = NULL; char executablePath[PATH_MAX];
struct stat executableStat;
int executableFD = -1; void * executableBuffer = NULL; __OSKextMmapBufferInfo * mmapAllocatorInfo = NULL; CFAllocatorContext mmapAllocatorContext;
CFAllocatorRef mmapAllocator = NULL;
if (!__OSKextCreateLoadInfo(aKext)) {
goto finish;
}
executableURL = OSKextGetExecutableURL(aKext);
if (executableURL) {
if (!__OSKextGetFileSystemPath( NULL,
executableURL, true, executablePath)) {
goto finish;
}
OSKextLog(aKext,
kOSKextLogDebugLevel | kOSKextLogFileAccessFlag,
"Statting %s for map.",
executablePath);
if (-1 == stat(executablePath, &executableStat)) {
localErrno = errno;
if (localErrno == ENOENT) {
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticExecutableMissingKey,
executableName, NULL);
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticFileNotFoundKey,
aKext->executableURL, NULL);
} else {
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticStatFailureKey,
aKext->executableURL, NULL);
}
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Stat failed for %s - %s.",
executablePath, strerror(localErrno));
goto finish;
}
if (!length) {
length = executableStat.st_size;
} else if ((offset + length) > executableStat.st_size) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Internal error; overrun mapping executable file %s.",
executablePath);
goto finish;
}
OSKextLog(aKext,
kOSKextLogDebugLevel | kOSKextLogFileAccessFlag,
"Opening %s for map.",
executablePath);
executableFD = open(executablePath, O_RDONLY);
if (executableFD == -1) {
localErrno = errno;
if (localErrno == ENOENT) {
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticExecutableMissingKey, executableName,
NULL);
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticFileNotFoundKey,
aKext->executableURL, NULL);
} else {
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticFileAccessKey,
aKext->executableURL, NULL);
}
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Open failed for %s - %s.",
executablePath, strerror(localErrno));
goto finish;
}
executableBuffer = mmap( NULL, length,
PROT_READ|PROT_WRITE, MAP_FILE|MAP_PRIVATE, executableFD, offset);
if (!executableBuffer) {
localErrno = errno;
if (length == 0) {
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticFileAccessKey, aKext->executableURL,
NULL);
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticExecutableMissingKey, executableName,
NULL);
aKext->flags.invalid = 1;
aKext->flags.valid = 0;
}
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Failed to map executable file %s (offset %lu, %lu bytes) - %s.",
executablePath, (unsigned long)offset, (unsigned long)length,
strerror(localErrno));
goto finish;
}
OSKextLog(aKext, kOSKextLogDetailLevel | kOSKextLogFileAccessFlag,
"Mapped executable file %s (offset %lu, %lu bytes).",
executablePath, (unsigned long)offset, (unsigned long)length);
CFAllocatorGetContext(kCFAllocatorDefault, &mmapAllocatorContext);
mmapAllocatorInfo = (__OSKextMmapBufferInfo *)malloc(
sizeof(__OSKextMmapBufferInfo));
if (!mmapAllocatorInfo) {
OSKextLogMemError();
goto finish;
}
mmapAllocatorInfo->length = length;
mmapAllocatorContext.info = mmapAllocatorInfo;
mmapAllocatorContext.deallocate = &__OSKextDeallocateMmapBuffer;
mmapAllocator = CFAllocatorCreate(kCFAllocatorDefault,
&mmapAllocatorContext);
if (!mmapAllocator) {
OSKextLogMemError();
goto finish;
}
result = CFDataCreateWithBytesNoCopy(
CFGetAllocator(aKext), executableBuffer, length,
mmapAllocator);
}
finish:
SAFE_RELEASE(mmapAllocator);
if (executableFD != -1) {
close(executableFD);
}
if (!result) {
SAFE_FREE(mmapAllocatorInfo);
if (executableBuffer) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Error encountered, unmapping executable file %s (offset %lu, %lu bytes).",
executablePath, (unsigned long)offset, (unsigned long)length);
munmap(executableBuffer, length);
}
}
return result;
}
Boolean __OSKextReadExecutable(OSKextRef aKext)
{
Boolean result = false;
if (!OSKextDeclaresExecutable(aKext)) {
result = false; goto finish;
} else if (aKext->staticFlags.isFromMkext) {
if (aKext->mkextInfo && aKext->mkextInfo->executable) {
result = true;
goto finish;
} else {
CFNumberRef executableOffsetNum = NULL;
if (!__OSKextCreateMkextInfo(aKext)) {
goto finish;
}
executableOffsetNum = CFDictionaryGetValue(aKext->infoDictionary,
CFSTR(kMKEXTExecutableKey));
if (executableOffsetNum) {
aKext->mkextInfo->executable = __OSKextExtractMkext2FileEntry(
aKext,
aKext->mkextInfo->mkextData,
executableOffsetNum,
NULL);
if (!aKext->mkextInfo->executable) {
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticExecutableMissingKey,
CFSTR("(executable from mkext)"), NULL);
aKext->flags.invalid = 1;
aKext->flags.valid = 0;
goto finish;
}
}
result = true;
goto finish;
}
} else {
if (aKext->loadInfo && aKext->loadInfo->executable) {
result = true;
goto finish;
} else {
if (!__OSKextCreateLoadInfo(aKext)) {
goto finish;
}
aKext->loadInfo->executable = __OSKextMapExecutable(aKext,
0, 0);
if (!aKext->loadInfo->executable) {
goto finish;
}
}
}
result = true;
finish:
if (aKext->loadInfo && aKext->loadInfo->executable) {
(void)posix_madvise(
(void *)CFDataGetBytePtr(aKext->loadInfo->executable),
CFDataGetLength(aKext->loadInfo->executable),
POSIX_MADV_RANDOM);
}
return result;
}
CFDataRef OSKextCopyExecutableForArchitecture(
OSKextRef aKext,
const NXArchInfo * archInfo)
{
CFDataRef result = NULL;
CFBundleRef kextBundle = NULL; CFURLRef execURL = NULL; CFDataRef executable = NULL; CFStringRef archName = NULL; fat_iterator fatIterator = NULL; char kextPath[PATH_MAX];
if (!__OSKextReadExecutable(aKext)) {
goto finish;
}
if (aKext->staticFlags.isFromMkext) {
if (aKext->mkextInfo && aKext->mkextInfo->executable) {
executable = CFRetain(aKext->mkextInfo->executable);
}
} else {
if (aKext->loadInfo && aKext->loadInfo->executable) {
executable = CFRetain(aKext->loadInfo->executable);
}
}
if (!executable) {
goto finish;
}
if (!archInfo) {
if (aKext->staticFlags.isFromMkext) {
result = CFDataCreate(CFGetAllocator(executable),
CFDataGetBytePtr(executable), CFDataGetLength(executable));
} else {
result = __OSKextMapExecutable(aKext,
0,
0);
}
} else {
const UInt8 * exec = CFDataGetBytePtr(executable);
void * thinExec = NULL; void * thinExecEnd = NULL;
fatIterator = fat_iterator_for_data(exec,
exec + CFDataGetLength(executable), 1 );
if (!fatIterator) {
__OSKextSetDiagnostic(aKext,
kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticExecutableBadKey);
goto finish;
}
if (!aKext->staticFlags.isFromMkext) {
struct fat_arch fatArchInfo;
if (!fat_iterator_find_fat_arch(fatIterator,
archInfo->cputype, archInfo->cpusubtype, &fatArchInfo)) {
goto finish;
}
if ((1 << fatArchInfo.align) == PAGE_SIZE) {
result = __OSKextMapExecutable(aKext,
fatArchInfo.offset,
fatArchInfo.size);
goto finish;
}
}
thinExec = fat_iterator_find_arch(fatIterator,
archInfo->cputype, archInfo->cpusubtype, &thinExecEnd);
if (thinExec) {
result = CFDataCreate(CFGetAllocator(aKext), thinExec,
thinExecEnd - thinExec);
}
}
if (!result) {
goto finish;
}
finish:
if (!result && archInfo && fatIterator) {
__OSKextGetFileSystemPath(aKext, NULL,
false, kextPath);
archName = CFStringCreateWithCString(
CFGetAllocator(aKext), archInfo->name,
kCFStringEncodingUTF8);
if (archName) {
__OSKextAddDiagnostic(aKext,
kOSKextDiagnosticsFlagWarnings,
kOSKextDiagnosticExecutableArchNotFoundKey,
archName, NULL);
}
}
SAFE_RELEASE(archName);
SAFE_RELEASE(execURL);
SAFE_RELEASE(kextBundle);
SAFE_RELEASE(executable);
if (fatIterator) fat_iterator_close(fatIterator);
return result;
}
CFDataRef OSKextCopyResource(
OSKextRef aKext,
CFStringRef resourceName,
CFStringRef resourceType)
{
CFDataRef result = NULL;
CFDataRef resource = NULL; CFStringRef resourceNamePlusType = NULL; CFBundleRef kextBundle = NULL; CFURLRef resourceURL = NULL; SInt32 error;
char * resourceCString = NULL; char kextPath[PATH_MAX];
char resourcePath[PATH_MAX];
if (!aKext->staticFlags.isFromMkext) {
__OSKextGetFileSystemPath(aKext, NULL,
false, kextPath);
OSKextLog(aKext, kOSKextLogDetailLevel | kOSKextLogFileAccessFlag,
"Opening CFBundle for %s.", kextPath);
kextBundle = CFBundleCreate(CFGetAllocator(aKext),
aKext->bundleURL);
if (!kextBundle) {
OSKextLog(aKext, kOSKextLogProgressLevel | kOSKextLogFileAccessFlag,
"Couldn't open CFBundle for %s.", kextPath);
goto finish;
}
resourceURL = CFBundleCopyResourceURL(kextBundle,
resourceName, resourceType, NULL);
if (!resourceURL) {
resourceCString = createUTF8CStringForCFString(resourceName);
OSKextLog(aKext, kOSKextLogProgressLevel | kOSKextLogFileAccessFlag,
"Couldn't read resource URL in %s for resource %s.",
kextPath, resourceCString);
goto finish;
}
__OSKextGetFileSystemPath( NULL, resourceURL,
false, resourcePath);
OSKextLog(aKext, kOSKextLogDetailLevel | kOSKextLogFileAccessFlag,
"Reading resource %s.", resourcePath);
if (!CFURLCreateDataAndPropertiesFromResource(CFGetAllocator(aKext),
resourceURL, &resource, NULL, NULL, &error)) {
OSKextLog(aKext, kOSKextLogProgressLevel | kOSKextLogFileAccessFlag,
"Couldn't read resource file %s.",
resourcePath);
goto finish;
}
}
result = resource;
finish:
if (resourceURL) {
OSKextLogSpec logLevel = kOSKextLogDebugLevel;
if (!result) {
logLevel = kOSKextLogProgressLevel;
}
OSKextLog(aKext, logLevel | kOSKextLogFileAccessFlag,
"Reading resource file %s%s.",
resourcePath,
result ? "" : " failed");
}
SAFE_RELEASE(resourceNamePlusType);
SAFE_RELEASE(resourceURL);
SAFE_FREE(resourceCString);
if (kextBundle) {
OSKextLog(aKext,
kOSKextLogDebugLevel | kOSKextLogFileAccessFlag,
"Releasing CFBundle for %s.",
kextPath);
}
SAFE_RELEASE(kextBundle);
return result;
}
#ifndef IOKIT_EMBEDDED
Boolean OSKextIsInExcludeList(OSKextRef theKext, Boolean useCache)
{
Boolean result = false;
CFStringRef kextID = NULL; OSKextRef excludelistKext = NULL; CFDictionaryRef excludelistDict = NULL; static CFDictionaryRef myDictionary = NULL;
if (useCache == false || myDictionary == NULL) {
if (myDictionary != NULL) {
SAFE_RELEASE_NULL(myDictionary);
}
kextID = CFStringCreateWithCString(kCFAllocatorDefault,
"com.apple.driver.KextExcludeList",
kCFStringEncodingUTF8);
if (!kextID) {
OSKextLogStringError( NULL);
goto finish;
}
excludelistKext = OSKextCreateWithIdentifier(kCFAllocatorDefault,
kextID);
if (!excludelistKext) {
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
"Warning: %s could not find com.apple.driver.KextExcludeList",
__FUNCTION__);
goto finish;
}
excludelistDict = OSKextGetValueForInfoDictionaryKey(
excludelistKext,
__kOSKextExcludeListKey);
if (!excludelistDict) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s could not get excludelistDict",
__FUNCTION__);
goto finish;
}
if ((unsigned int)CFDictionaryGetCount(excludelistDict) > 0) {
myDictionary = CFDictionaryCreateCopy(NULL, excludelistDict);
}
if (myDictionary == NULL) {
OSKextLogMemError();
goto finish;
}
}
#define __kOSKextExcludeListAnyMatchKey CFSTR("Any_Match")
#define __kOSKextExcludeListAllMatchKey CFSTR("All_Match")
if (theKext != NULL) {
CFArrayRef excludedKextArray = NULL; CFStringRef bundleID = NULL; CFStringRef excludedKextVersString = NULL; OSKextVersion kextVers = -1;
CFDictionaryRef excludedKextDict = NULL;
bundleID = OSKextGetIdentifier(theKext);
if (!bundleID) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s could not get bundleID",
__FUNCTION__);
goto finish;
}
kextVers = OSKextGetVersion(theKext);
if (!kextVers) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s could not get kextVers",
__FUNCTION__);
goto finish;
}
excludedKextVersString = CFDictionaryGetValue(myDictionary,
bundleID);
if (_isString(excludedKextVersString)) {
result = _excludeThisVersion(kextVers, excludedKextVersString);
goto finish;
}
excludedKextDict = CFDictionaryGetValue(myDictionary,
bundleID);
if (_isDictionary(excludedKextDict) == FALSE) {
goto finish;
}
excludedKextArray = CFDictionaryGetValue(excludedKextDict,
__kOSKextExcludeListAnyMatchKey);
if (_isArray(excludedKextArray)) {
CFIndex len = CFArrayGetCount(excludedKextArray);
if (len > 0) {
Boolean match = false;
for (int i = 0; i < len; i++) {
CFDictionaryRef matchDict; matchDict = CFArrayGetValueAtIndex(excludedKextArray, i);
if (_isDictionary(matchDict)) {
io_service_t serv;
CFRetain(matchDict); serv = IOServiceGetMatchingService(kIOMasterPortDefault,
matchDict);
if (serv) {
match = true;
IOObjectRelease(serv);
break;
}
}
} if (!match) {
goto finish;
}
}
}
excludedKextArray = CFDictionaryGetValue(excludedKextDict,
__kOSKextExcludeListAllMatchKey);
if (_isArray(excludedKextArray)) {
CFIndex len = CFArrayGetCount(excludedKextArray);
if (len > 0) {
int i;
for (i = 0; i < len; i++) {
CFDictionaryRef matchDict; matchDict = CFArrayGetValueAtIndex(excludedKextArray, i);
if (_isDictionary(matchDict)) {
io_service_t serv;
CFRetain(matchDict); serv = IOServiceGetMatchingService(kIOMasterPortDefault,
matchDict);
if (serv) {
IOObjectRelease(serv);
continue;
}
break;
}
} if (i < len) {
goto finish;
}
}
}
excludedKextVersString = CFDictionaryGetValue(excludedKextDict,
__kOSKextExcludeListVersionKey);
if (_isString(excludedKextVersString)) {
result = _excludeThisVersion(kextVers, excludedKextVersString);
}
}
finish:
SAFE_RELEASE(kextID);
SAFE_RELEASE(excludelistKext);
return result;
}
#define GET_CSTRING_PTR(the_cfstring, the_ptr, the_buffer, the_size) \
do { \
the_ptr = CFStringGetCStringPtr(the_cfstring, kCFStringEncodingUTF8); \
if (the_ptr == NULL) { \
the_buffer[0] = 0x00; \
the_ptr = the_buffer; \
CFStringGetCString(the_cfstring, the_buffer, the_size, kCFStringEncodingUTF8); \
} \
} while(0)
#define isWhiteSpace(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n')
static Boolean _excludeThisVersion(OSKextVersion theKextVers,
CFStringRef theExcludedKextVers)
{
Boolean result = false;
size_t i, j;
const char * versCString = NULL; Boolean wantLessThan = FALSE;
Boolean wantLessThanEqualTo = FALSE;
Boolean needUpperRange = FALSE;
char versBuffer[256];
GET_CSTRING_PTR(theExcludedKextVers,
versCString,
versBuffer,
sizeof(versBuffer));
if (strlen(versCString) < 1) {
goto finish;
}
if (strlen(versCString) > 1) {
if (*versCString == 'L' && *(versCString + 1) == 'T') {
wantLessThan = true;
versCString +=2;
}
else if (*versCString == 'L' && *(versCString + 1) == 'E') {
wantLessThanEqualTo = true;
versCString +=2;
}
}
for (i = 0, j = 0; i < strlen(versCString) + 1; i++) {
Boolean excludeIt = FALSE;
char myBuffer[32];
char myUpperBuffer[32];
if ( isWhiteSpace(*(versCString + i)) ) {
continue;
}
if (*(versCString + i) == ',' ||
*(versCString + i) == 0x00 ||
strncmp(versCString + i, "thru", strlen("thru")) == 0) {
if (needUpperRange) {
myUpperBuffer[j] = 0x00;
}
else {
if (strncmp(versCString + i, "thru", strlen("thru")) == 0) {
needUpperRange = TRUE;
myBuffer[j] = 0x00;
i += 3;
j = 0;
continue;
}
else {
myBuffer[j] = 0x00;
}
}
OSKextVersion excludedKextVers;
OSKextVersion excludedKextVersUpper;
excludedKextVers = OSKextParseVersionString(myBuffer);
if (needUpperRange) {
excludedKextVersUpper = OSKextParseVersionString(myUpperBuffer);
if (theKextVers >= excludedKextVers &&
theKextVers <= excludedKextVersUpper) {
excludeIt = TRUE;
}
}
else {
if (wantLessThanEqualTo) {
if (theKextVers <= excludedKextVers) {
excludeIt = TRUE;
}
}
else if (wantLessThan) {
if (theKextVers < excludedKextVers) {
excludeIt = TRUE;
}
}
else if (theKextVers == excludedKextVers) {
excludeIt = TRUE;
}
}
if (excludeIt) {
result = true;
goto finish;
}
j = 0;
wantLessThan = FALSE;
wantLessThanEqualTo = FALSE;
needUpperRange = FALSE;
}
else {
if (needUpperRange) {
myUpperBuffer[j++] = *(versCString + i);
if ( j >= sizeof(myUpperBuffer) ) {
break;
}
}
else {
myBuffer[j++] = *(versCString + i);
if ( j >= sizeof(myBuffer) ) {
break;
}
}
}
} finish:
return result;
}
#endif
#pragma mark Dependency Resolution
Boolean __OSKextHasAllDependencies(OSKextRef aKext)
{
if (aKext->flags.isKernelComponent ||
(aKext->loadInfo && aKext->loadInfo->flags.hasAllDependencies)) {
return true;
}
return false;
}
static Boolean __OSKextHasSuffix(OSKextRef aKext, const char *suffix)
{
char path[PATH_MAX];
CFURLRef executableURL = OSKextGetExecutableURL(aKext);
if (!executableURL) {
return false;
}
if (__OSKextGetFileSystemPath(NULL, executableURL, true, path)) {
const size_t plen = strlen(path);
const size_t slen = strlen(suffix);
if ((plen > slen) && !strncmp(suffix, &path[plen - slen], slen)) {
return true;
}
}
return false;
}
Boolean __OSKextResolveDependencies(
OSKextRef aKext,
OSKextRef rootKext,
CFMutableSetRef resolvedSet,
CFMutableArrayRef loopStack)
{
Boolean result = false;
Boolean error = false;
Boolean addedToLoopStack = false;
CFDictionaryRef declaredDependencies = NULL; CFStringRef * libIDs = NULL; CFStringRef * libVersions = NULL; char * libIDCString = NULL; char kextPath[PATH_MAX];
char dependencyPath[PATH_MAX];
CFIndex count = 0, i = 0;
__OSKextGetFileSystemPath(aKext, NULL,
false, kextPath);
if (!__OSKextReadInfoDictionary(aKext, NULL) ||
!aKext->infoDictionary) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogDependenciesFlag,
"%s has no info dictionary; can't resolve dependencies.",
kextPath);
goto finish;
}
if (!__OSKextIsValid(aKext)) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogDependenciesFlag,
"%s is invalid; can't resolve dependencies.",
kextPath);
goto finish;
}
if (CFSetContainsValue(resolvedSet, aKext)) {
OSKextLog(aKext, kOSKextLogDebugLevel | kOSKextLogDependenciesFlag,
"%s already has dependencies resolved.", kextPath);
result = true;
goto finish;
}
if (!OSKextIsKernelComponent(aKext)) {
OSKextLog(aKext, kOSKextLogStepLevel | kOSKextLogDependenciesFlag,
"Resolving dependencies for %s.", kextPath);
}
if (CFArrayGetCountOfValue(loopStack, RANGE_ALL(loopStack), aKext)) {
__OSKextAddDiagnostic(rootKext, kOSKextDiagnosticsFlagDependencies,
kOSKextDependencyCircularReference, aKext->bundleID, NULL);
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagDependencies,
kOSKextDependencyCircularReference, aKext->bundleID, NULL);
goto finish;
}
CFArrayAppendValue(loopStack, aKext);
addedToLoopStack = true;
OSKextFlushDependencies(aKext);
if (!__OSKextCreateLoadInfo(aKext)) {
goto finish;
}
aKext->loadInfo->dependencies = CFArrayCreateMutable(
CFGetAllocator(aKext), 0, &kCFTypeArrayCallBacks);
if (!aKext->loadInfo->dependencies) {
OSKextLogMemError();
goto finish;
}
declaredDependencies = (CFDictionaryRef)OSKextGetValueForInfoDictionaryKey(
aKext, CFSTR(kOSBundleLibrariesKey));
if (OSKextIsKernelComponent(aKext)) {
if (aKext == rootKext) {
OSKextLog(aKext, kOSKextLogDependenciesFlag,
"%s is a kernel component with no dependencies.", kextPath);
}
result = true;
goto finish;
}
if (declaredDependencies) {
count = CFDictionaryGetCount(declaredDependencies);
if (count) {
libIDs = (CFStringRef *)malloc(count * sizeof(CFStringRef));
libVersions = (CFStringRef *)malloc(count * sizeof(CFStringRef));
if (!libIDs || !libVersions) {
OSKextLogMemError();
goto finish;
}
CFDictionaryGetKeysAndValues(declaredDependencies,
(const void **)libIDs, (const void **)libVersions);
}
}
for (i = 0; i < count; i++) {
CFStringRef libID = libIDs[i];
CFStringRef libVersion = libVersions[i];
OSKextVersion requestedVersion = OSKextParseVersionCFString(libVersion);
OSKextRef dependency = NULL;
Boolean loaded = false;
Boolean compatible = false;
SAFE_FREE_NULL(libIDCString);
libIDCString = createUTF8CStringForCFString(libID);
dependency = OSKextGetLoadedKextWithIdentifier(libID);
if (dependency) {
loaded = true;
compatible = OSKextIsCompatibleWithVersion(dependency,
requestedVersion);
if (!compatible) {
char requestedVersionCString[kOSKextVersionMaxLength];
char actualVersionCString[kOSKextVersionMaxLength];
OSKextVersionGetString(requestedVersion,
requestedVersionCString, sizeof(requestedVersionCString));
OSKextVersionGetString(OSKextGetVersion(dependency),
actualVersionCString, sizeof(actualVersionCString));
if (OSKextGetCompatibleVersion(dependency) > 0) {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogDependenciesFlag,
"%s - loaded dependency %s, v%s is "
"not compatible with requested version %s.",
kextPath, libIDCString,
actualVersionCString, requestedVersionCString);
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagDependencies,
kOSKextDependencyLoadedIsIncompatible, libID, NULL);
} else {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogDependenciesFlag,
"%s - loaded dependency %s lacks valid OSBundleCompatibleVersion.",
kextPath, libIDCString);
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagDependencies,
kOSKextDependencyLoadedCompatibleVersionUndeclared, libID, NULL);
}
error = true;
continue;
}
} else {
dependency = OSKextGetCompatibleKextWithIdentifier(libID,
requestedVersion);
if (dependency) {
compatible = true;
}
}
if (CFEqual(libID, __kOSKextKernelLibBundleID)) {
aKext->loadInfo->flags.hasRawKernelDependency = 1;
} else if (CFStringHasPrefix(libID, __kOSKextKernelLibPrefix)) {
aKext->loadInfo->flags.hasKernelDependency = 1;
} else if (CFStringHasPrefix(libID, __kOSKextKPIPrefix)) {
aKext->loadInfo->flags.hasKPIDependency = 1;
if (CFEqual(libID, __kOSKextPrivateKPI)) {
aKext->loadInfo->flags.hasPrivateKPIDependency = 1;
}
}
if (dependency) {
Boolean kernelComponent = OSKextIsKernelComponent(dependency);
CFTypeRef promotion = OSKextGetValueForInfoDictionaryKey(dependency, CFSTR("OSBundleRequiredPromotion"));
Boolean promotable = promotion && CFEqual(promotion, kCFBooleanTrue);
__OSKextGetFileSystemPath(dependency, NULL,
false, dependencyPath);
OSKextLog(aKext, kOSKextLogDetailLevel | kOSKextLogDependenciesFlag,
"%s found %s%s%sdependency %s for %s%s.",
kextPath,
compatible ? "compatible " : "incompatible ",
promotable ? "promotable " : "",
loaded ? "loaded " : "",
dependencyPath, libIDCString,
kernelComponent ? " (kernel component)" : "");
CFArrayAppendValue(aKext->loadInfo->dependencies, dependency);
if (promotable) {
CFStringRef current = (CFStringRef)OSKextGetValueForInfoDictionaryKey(aKext, CFSTR(kOSBundleRequiredKey));
if (!current || (CFStringCompare(current, CFSTR(kOSBundleRequiredSafeBoot), 0) == kCFCompareEqualTo)) {
CFDictionarySetValue(aKext->infoDictionary, CFSTR(kOSBundleRequiredKey), CFSTR(kOSBundleRequiredConsole));
}
}
} else {
dependency = OSKextGetKextWithIdentifier(libID);
if (dependency) {
if (OSKextGetCompatibleVersion(dependency) > 0) {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogDependenciesFlag,
"%s - no compatible dependency found for %s.",
kextPath, libIDCString);
__OSKextAddDiagnostic(aKext,
kOSKextDiagnosticsFlagDependencies,
kOSKextDependencyNoCompatibleVersion, libID, NULL);
} else {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogDependenciesFlag,
"%s - dependency for %s lacks valid OSBundleCompatibleVersion.",
kextPath, libIDCString);
__OSKextAddDiagnostic(aKext,
kOSKextDiagnosticsFlagDependencies,
kOSKextDependencyCompatibleVersionUndeclared, libID, NULL);
}
} else {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogDependenciesFlag,
"%s - dependency '%s' not found.",
kextPath, libIDCString);
__OSKextAddDiagnostic(aKext,
kOSKextDiagnosticsFlagDependencies,
kOSKextDependencyUnavailable, libID, NULL);
}
error = true;
continue;
}
}
if (aKext->loadInfo->flags.hasRawKernelDependency) {
__OSKextSetDiagnostic(aKext, kOSKextDiagnosticsFlagDependencies,
kOSKextDiagnosticRawKernelDependency);
error = true;
}
if (OSKextIsInterface(aKext) &&
CFArrayGetCount(aKext->loadInfo->dependencies) != 1)
{
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogDependenciesFlag,
"%s - Interface kext must have exactly one dependency.",
kextPath);
__OSKextSetDiagnostic(aKext,
kOSKextDiagnosticsFlagDependencies,
kOSKextDiagnosticsInterfaceDependencyCount);
error = true;
}
count = CFArrayGetCount(aKext->loadInfo->dependencies);
for (i = 0; i < count; i++) {
OSKextRef dependency = (OSKextRef)CFArrayGetValueAtIndex(
aKext->loadInfo->dependencies, i);
CFStringRef libID = OSKextGetIdentifier(dependency);
if (!__OSKextResolveDependencies(dependency, rootKext, resolvedSet,
loopStack)) {
CFIndex stackCount, stackIndex;
stackCount = CFArrayGetCount(loopStack);
for (stackIndex = 0; stackIndex < stackCount; stackIndex++) {
OSKextRef stackKext = (OSKextRef)CFArrayGetValueAtIndex(
loopStack, stackIndex);
__OSKextAddDiagnostic(stackKext,
kOSKextDiagnosticsFlagDependencies,
kOSKextDependencyIndirectDependencyUnresolvable,
libID, NULL);
}
error = true;
}
}
if (OSKextDeclaresExecutable(aKext) && __OSKextHasSuffix(aKext, "_kasan")) {
OSKextRef kasan_kext = OSKextGetKextWithIdentifier(__kOSKextKasanKPI);
if (kasan_kext) {
OSKextLog(aKext, kOSKextLogDetailLevel | kOSKextLogDependenciesFlag,
"%s adding implicit KASan dependency", kextPath);
CFArrayAppendValue(aKext->loadInfo->dependencies, kasan_kext);
}
}
if (__OSKextIsArchitectureLP64()) {
if (OSKextDeclaresExecutable(aKext) && OSKextSupportsArchitecture(aKext, OSKextGetArchitecture())) {
if (aKext->loadInfo->flags.hasKernelDependency) {
__OSKextSetDiagnostic(aKext, kOSKextDiagnosticsFlagDependencies,
kOSKextDiagnosticDeclaresNonKPIDependenciesKey);
goto finish;
}
if (!aKext->loadInfo->flags.hasKPIDependency) {
__OSKextSetDiagnostic(aKext, kOSKextDiagnosticsFlagWarnings,
kOSKextDiagnosticDeclaresNoKPIsWarningKey);
}
}
} else {
if (OSKextDeclaresExecutable(aKext) &&
!aKext->loadInfo->flags.hasKernelDependency &&
!aKext->loadInfo->flags.hasKPIDependency) {
OSKextLog(aKext,
kOSKextLogWarningLevel | kOSKextLogDependenciesFlag,
"%s does not declare a kernel dependency; using %s.",
kextPath, __kOSKextCompatibilityBundleID);
OSKextRef kernel6 = OSKextGetKextWithIdentifier(
CFSTR(__kOSKextCompatibilityBundleID));
if (!kernel6) {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogDependenciesFlag,
"%s - dependency '%s' not found.",
kextPath, __kOSKextCompatibilityBundleID);
goto finish;
}
CFArrayAppendValue(aKext->loadInfo->dependencies, kernel6);
__OSKextSetDiagnostic(aKext, kOSKextDiagnosticsFlagWarnings,
kOSKextDiagnosticNoExplicitKernelDependencyKey);
}
if (aKext->loadInfo->flags.hasKPIDependency &&
aKext->loadInfo->flags.hasKernelDependency) {
__OSKextSetDiagnostic(aKext, kOSKextDiagnosticsFlagWarnings,
kOSKextDiagnosticDeclaresBothKernelAndKPIDependenciesKey);
}
}
#ifndef IOKIT_EMBEDDED
if (aKext->loadInfo->flags.hasPrivateKPIDependency)
{
CFStringRef infoString = NULL; CFStringRef readableString = NULL; Boolean hasApplePrefix = false;
Boolean infoCopyrightIsValid = false;
Boolean readableCopyrightIsValid = false;
hasApplePrefix = CFStringHasPrefix(aKext->bundleID, __kOSKextApplePrefix);
infoString = (CFStringRef) OSKextGetValueForInfoDictionaryKey(aKext,
CFSTR("CFBundleGetInfoString"));
if (infoString) {
char *infoCString = createUTF8CStringForCFString(infoString);
infoCopyrightIsValid = kxld_validate_copyright_string(infoCString);
SAFE_FREE(infoCString);
}
readableString = (CFStringRef) OSKextGetValueForInfoDictionaryKey(aKext,
CFSTR("NSHumanReadableCopyright"));
if (readableString) {
char *readableCString = createUTF8CStringForCFString(readableString);
readableCopyrightIsValid =
kxld_validate_copyright_string(readableCString);
SAFE_FREE(readableCString);
}
if (!hasApplePrefix || (!infoCopyrightIsValid && !readableCopyrightIsValid)) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogDependenciesFlag,
"%s has an Apple prefix but no copyright.", kextPath);
__OSKextSetDiagnostic(aKext, kOSKextDiagnosticsFlagDependencies,
kOSKextDiagnosticNonAppleKextDeclaresPrivateKPIDependencyKey);
result = false;
goto finish;
}
}
#endif
if (!error) {
result = true;
}
finish:
SAFE_FREE(libIDCString);
SAFE_FREE(libIDs);
SAFE_FREE(libVersions);
if (result && aKext->loadInfo) {
aKext->loadInfo->flags.hasAllDependencies = 1;
}
CFSetAddValue(resolvedSet, aKext);
if (addedToLoopStack) {
CFArrayRemoveValueAtIndex(loopStack, CFArrayGetCount(loopStack) - 1);
}
return result;
}
typedef struct {
Boolean result;
} __OSKextResolveDependenciesContext;
static void __OSKextResolveDependenciesApplierFunction(
const void * vKey __unused,
const void * vValue,
void * vContext)
{
OSKextRef aKext = (OSKextRef)vValue;
Boolean resolved = false;
__OSKextResolveDependenciesContext * context =
(__OSKextResolveDependenciesContext *)vContext;
resolved = OSKextResolveDependencies(aKext);
if (!resolved) {
context->result = false;
}
return;
}
Boolean OSKextResolveDependencies(OSKextRef aKext)
{
Boolean result = false;
CFMutableSetRef resolvedSet = NULL; CFMutableArrayRef loopStack = NULL; CFArrayRef loadList = NULL; CFStringRef kextID = NULL; CFIndex count, i, j;
char kextPath[PATH_MAX];
resolvedSet = CFSetCreateMutable(
CFGetAllocator(aKext), 0, &kCFTypeSetCallBacks);
loopStack = CFArrayCreateMutable(
CFGetAllocator(aKext), 0, &kCFTypeArrayCallBacks);
if (!resolvedSet || !loopStack) {
OSKextLogMemError();
goto finish;
}
if (aKext) {
if (__OSKextHasAllDependencies(aKext)) {
__OSKextGetFileSystemPath(aKext, NULL,
false, kextPath);
OSKextLog(aKext,
kOSKextLogDebugLevel | kOSKextLogDependenciesFlag,
"%s - dependencies already resolved.", kextPath);
result = true;
goto finish;
}
result = __OSKextResolveDependencies(aKext, aKext, resolvedSet,
loopStack);
if (result) {
CFStringRef required = NULL; Boolean checkRootOrConsoleRequired = FALSE;
Boolean checkLocalRequired = FALSE;
Boolean checkNetworkRequired = FALSE;
loadList = OSKextCopyLoadList(aKext, true);
if (!loadList) {
result = false;
goto finish;
}
required = OSKextGetValueForInfoDictionaryKey(aKext, CFSTR(kOSBundleRequiredKey));
if (required) {
if (CFEqual(required, CFSTR(kOSBundleRequiredRoot)) ||
CFEqual(required, CFSTR(kOSBundleRequiredConsole))) {
checkRootOrConsoleRequired = TRUE;
} else if (CFEqual(required, CFSTR(kOSBundleRequiredLocalRoot))) {
checkLocalRequired = TRUE;
} else if (CFEqual(required, CFSTR(kOSBundleRequiredNetworkRoot))) {
checkNetworkRequired = TRUE;
}
}
count = CFArrayGetCount(loadList);
for (i = 0; i < count; i++) {
OSKextRef thisKext = (OSKextRef)CFArrayGetValueAtIndex(
loadList, i);
CFStringRef thisRequired = OSKextGetValueForInfoDictionaryKey(thisKext,
CFSTR(kOSBundleRequiredKey));
kextID = OSKextGetIdentifier(thisKext);
if (checkRootOrConsoleRequired) {
if (!thisRequired ||
CFEqual(CFSTR(kOSBundleRequiredSafeBoot), thisRequired)) {
__OSKextAddDiagnostic(aKext,
kOSKextDiagnosticsFlagWarnings,
kOSKextDiagnosticsDependencyNotOSBundleRequired,
kextID,
thisRequired ? thisRequired : CFSTR("OSBundleRequired not set"));
}
}
if (checkLocalRequired) {
if (!thisRequired ||
!(CFEqual(CFSTR(kOSBundleRequiredRoot), thisRequired) ||
CFEqual(CFSTR(kOSBundleRequiredLocalRoot), thisRequired) ||
CFEqual(CFSTR(kOSBundleRequiredConsole), thisRequired))) {
__OSKextAddDiagnostic(aKext,
kOSKextDiagnosticsFlagWarnings,
kOSKextDiagnosticsDependencyNotOSBundleRequired,
kextID,
thisRequired ? thisRequired : CFSTR("OSBundleRequired not set"));
}
}
if (checkNetworkRequired) {
if (!thisRequired ||
!(CFEqual(CFSTR(kOSBundleRequiredRoot), thisRequired) ||
CFEqual(CFSTR(kOSBundleRequiredNetworkRoot), thisRequired) ||
CFEqual(CFSTR(kOSBundleRequiredConsole), thisRequired))) {
__OSKextAddDiagnostic(aKext,
kOSKextDiagnosticsFlagWarnings,
kOSKextDiagnosticsDependencyNotOSBundleRequired,
kextID,
thisRequired ? thisRequired : CFSTR("OSBundleRequired not set"));
}
}
for (j = i+1; j < count; j++) {
OSKextRef thatKext = (OSKextRef)CFArrayGetValueAtIndex(
loadList, j);
if (CFEqual(kextID, OSKextGetIdentifier(thatKext))) {
__OSKextAddDiagnostic(aKext,
kOSKextDiagnosticsFlagDependencies,
kOSKextDependencyMultipleVersionsDetected,
kextID, NULL);
result = false;
}
}
}
}
} else if (__sOSKextsByURL) {
__OSKextResolveDependenciesContext context;
context.result = true; CFDictionaryApplyFunction(__sOSKextsByURL,
__OSKextResolveDependenciesApplierFunction, &context);
}
finish:
SAFE_RELEASE(resolvedSet);
SAFE_RELEASE(loopStack);
SAFE_RELEASE(loadList);
return result;
}
static void __OSKextFlushDependenciesApplierFunction(
const void * vKey __unused,
const void * vValue,
void * vContext __unused)
{
OSKextRef theKext = (OSKextRef)vValue;
OSKextFlushDependencies(theKext);
return;
}
void __OSKextClearHasAllDependenciesOnKext(OSKextRef aKext)
{
char kextPath[PATH_MAX];
CFIndex count, i;
count = CFArrayGetCount(__sOSAllKexts);
for (i = 0; i < count; i++) {
OSKextRef checkKext = (OSKextRef)CFArrayGetValueAtIndex(__sOSAllKexts, i);
if (!checkKext->loadInfo || !checkKext->loadInfo->dependencies ||
!__OSKextHasAllDependencies(checkKext)) {
continue;
}
if (CFArrayContainsValue(checkKext->loadInfo->dependencies,
RANGE_ALL(checkKext->loadInfo->dependencies), aKext)) {
__OSKextGetFileSystemPath(checkKext, NULL,
false, kextPath);
OSKextLog(aKext,
kOSKextLogDebugLevel | kOSKextLogKextBookkeepingFlag,
"Clearing \"has all dependencies\" for %s.", kextPath);
checkKext->loadInfo->flags.hasAllDependencies = 0;
__OSKextClearHasAllDependenciesOnKext(checkKext);
}
}
return;
}
void OSKextFlushDependencies(OSKextRef aKext)
{
static Boolean flushingAll = false;
char kextPath[PATH_MAX];
pthread_once(&__sOSKextInitialized, __OSKextInitialize);
if (aKext) {
if (!flushingAll) {
if (OSKextGetURL(aKext)) {
__OSKextGetFileSystemPath(aKext, NULL,
false, kextPath);
}
OSKextLog(aKext,
kOSKextLogDetailLevel | kOSKextLogKextBookkeepingFlag,
"Flushing dependencies for %s.", kextPath);
}
if (aKext->loadInfo) {
aKext->loadInfo->flags.hasRawKernelDependency = 0;
aKext->loadInfo->flags.hasKernelDependency = 0;
aKext->loadInfo->flags.hasKPIDependency = 0;
if (aKext->loadInfo->dependencies) {
SAFE_RELEASE_NULL(aKext->loadInfo->dependencies);
aKext->loadInfo->flags.hasAllDependencies = 0;
aKext->loadInfo->flags.dependenciesValid = 0;
aKext->loadInfo->flags.dependenciesAuthentic = 0;
__OSKextClearHasAllDependenciesOnKext(aKext);
}
OSKextFlushDiagnostics(aKext, kOSKextDiagnosticsFlagDependencies);
}
} else if (__sOSKextsByURL) {
flushingAll = true;
OSKextLog( NULL,
kOSKextLogDetailLevel | kOSKextLogKextBookkeepingFlag,
"Flushing dependencies for all kexts.");
CFDictionaryApplyFunction(__sOSKextsByURL,
__OSKextFlushDependenciesApplierFunction, NULL);
flushingAll = false;
}
return;
}
Boolean OSKextValidateDependencies(OSKextRef aKext)
{
Boolean result = true;
CFArrayRef allDependencies = NULL; CFIndex count, i;
if (aKext->loadInfo && aKext->loadInfo->flags.dependenciesValid) {
goto finish;
}
allDependencies = OSKextCopyAllDependencies(aKext,
true);
if (!allDependencies) {
result = false;
goto finish;
}
count = CFArrayGetCount(allDependencies);
for (i = 0; i < count; i++) {
OSKextRef thisKext = (OSKextRef)CFArrayGetValueAtIndex(
allDependencies, i);
if (!__OSKextIsValid(thisKext)) {
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagDependencies,
kOSKextDependencyInvalid, OSKextGetIdentifier(thisKext),
NULL);
result = false;
}
}
if (result) {
if (!__OSKextCreateLoadInfo(aKext)) {
result = false;
goto finish;
}
aKext->loadInfo->flags.dependenciesValid = 1;
}
finish:
SAFE_RELEASE(allDependencies);
return result;
}
Boolean OSKextAuthenticateDependencies(OSKextRef aKext)
{
Boolean result = true;
CFArrayRef allDependencies = NULL; CFIndex count, i;
if (aKext->loadInfo && aKext->loadInfo->flags.dependenciesAuthentic) {
goto finish;
}
allDependencies = OSKextCopyAllDependencies(aKext,
true);
if (!allDependencies) {
result = false;
goto finish;
}
count = CFArrayGetCount(allDependencies);
for (i = 0; i < count; i++) {
OSKextRef thisKext = (OSKextRef)CFArrayGetValueAtIndex(
allDependencies, i);
if (!OSKextIsAuthentic(thisKext)) {
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagDependencies,
kOSKextDependencyInauthentic,
OSKextGetIdentifier(thisKext), NULL);
result = false;
}
}
if (result) {
if (!__OSKextCreateLoadInfo(aKext)) {
OSKextLogMemError();
goto finish;
}
aKext->loadInfo->flags.dependenciesAuthentic = 1;
}
finish:
SAFE_RELEASE(allDependencies);
return result;
}
CFArrayRef OSKextCopyDeclaredDependencies(
OSKextRef aKext,
Boolean needAllFlag)
{
CFArrayRef result = NULL;
Boolean resolved = false;
resolved = OSKextResolveDependencies(aKext);
if (needAllFlag && !resolved) {
goto finish;
}
if (aKext->loadInfo && aKext->loadInfo->dependencies) {
result = CFArrayCreateCopy(CFGetAllocator(aKext),
aKext->loadInfo->dependencies);
} else {
result = CFArrayCreate(CFGetAllocator(aKext), NULL, 0,
&kCFTypeArrayCallBacks);
}
finish:
return result;
}
Boolean
__OSKextGetBleedthroughFlag(OSKextRef aKext)
{
Boolean result = false;
if (__OSKextIsArchitectureLP64()) {
result = false;
} else {
result = !aKext->loadInfo->flags.hasKPIDependency;
}
return result;
}
Boolean
__OSKextAddLinkDependencies(
OSKextRef aKext,
CFMutableArrayRef linkDependencies,
Boolean needAllFlag,
Boolean bleedthroughFlag)
{
Boolean result = false;
CFIndex count, i;
if (OSKextIsKernelComponent(aKext)) {
result = true;
goto finish;
}
if (!aKext->loadInfo || !aKext->loadInfo->dependencies) {
result = !needAllFlag;
goto finish;
}
bleedthroughFlag = bleedthroughFlag || __OSKextGetBleedthroughFlag(aKext);
count = CFArrayGetCount(aKext->loadInfo->dependencies);
for (i = 0; i < count; i++) {
OSKextRef dependency = (OSKextRef)CFArrayGetValueAtIndex(
aKext->loadInfo->dependencies, i);
if (bleedthroughFlag || !OSKextDeclaresExecutable(dependency)) {
if (!__OSKextAddLinkDependencies(dependency,
linkDependencies,
needAllFlag,
bleedthroughFlag))
{
goto finish;
}
}
if (OSKextDeclaresExecutable(dependency)) {
if (kCFNotFound == CFArrayGetFirstIndexOfValue(linkDependencies,
RANGE_ALL(linkDependencies), dependency)) {
CFArrayAppendValue(linkDependencies, dependency);
}
}
}
result = true;
finish:
return result;
}
CFArrayRef OSKextCopyLinkDependencies(
OSKextRef aKext,
Boolean needAllFlag)
{
CFMutableArrayRef result = NULL;
Boolean resolved = OSKextResolveDependencies(aKext);
if (needAllFlag && !resolved) {
goto finish;
}
result = CFArrayCreateMutable(CFGetAllocator(aKext), 0,
&kCFTypeArrayCallBacks);
if (!result) {
OSKextLogMemError();
goto finish;
}
if (!__OSKextAddLinkDependencies(aKext, result,
needAllFlag, false)) {
SAFE_RELEASE_NULL(result);
}
finish:
return result;
}
Boolean __OSKextReadSymbolReferences(
OSKextRef aKext,
CFMutableDictionaryRef symbols)
{
Boolean result = false;
char kextPath[PATH_MAX];
CFDataRef executable = NULL; const struct mach_header * mach_header = NULL;
const void * file_end = NULL;
Boolean sixtyfourbit = false;
uint8_t swap = 0;
macho_seek_result symtab_result = macho_seek_result_not_found;
struct symtab_command * symtab = NULL;
char * syms_address = NULL;
const void * string_list = NULL;
unsigned int sym_offset = 0;
unsigned int str_offset = 0;
unsigned int num_syms = 0;
unsigned int syms_bytes = 0;
unsigned int sym_index = 0;
__OSKextGetFileSystemPath(aKext, NULL,
false, kextPath);
if (OSKextIsKernelComponent(aKext) || !OSKextDeclaresExecutable(aKext)) {
result = true;
goto finish;
}
executable = OSKextCopyExecutableForArchitecture(aKext, OSKextGetArchitecture());
if (!executable) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogLinkFlag,
"%s has no executable for architecture %s.",
kextPath, OSKextGetArchitecture()->name);
goto finish;
}
mach_header = (const struct mach_header *)CFDataGetBytePtr(executable);
file_end = (((const char *)mach_header) + CFDataGetLength(executable));
if (ISMACHO64(MAGIC32(mach_header))) {
sixtyfourbit = true;
}
if (ISSWAPPEDMACHO(MAGIC32(mach_header))) {
swap = 1;
}
symtab_result = macho_find_symtab(mach_header, file_end, &symtab);
if (symtab_result != macho_seek_result_found) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogLinkFlag,
"%s has no symtab in its executable (%s)",
kextPath, OSKextGetArchitecture()->name);
goto finish;
}
sym_offset = CondSwapInt32(swap, symtab->symoff);
str_offset = CondSwapInt32(swap, symtab->stroff);
num_syms = CondSwapInt32(swap, symtab->nsyms);
syms_address = (char *)mach_header + sym_offset;
string_list = (char *)mach_header + str_offset;
syms_bytes = num_syms * sizeof(struct nlist);
if (syms_address + syms_bytes > (char *)file_end) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogLinkFlag,
"%s: internal overrun in executable file (%s).",
kextPath, OSKextGetArchitecture()->name);
goto finish;
}
for (sym_index = 0; sym_index < num_syms; sym_index++) {
struct nlist * seekptr;
struct nlist_64 * seekptr_64;
uint32_t string_index;
uint8_t n_type;
if (sixtyfourbit) {
seekptr_64 = &((struct nlist_64 *)syms_address)[sym_index];
string_index = CondSwapInt32(swap, seekptr_64->n_un.n_strx);
n_type = seekptr_64->n_type;
} else {
seekptr = &((struct nlist *)syms_address)[sym_index];
string_index = CondSwapInt32(swap, seekptr->n_un.n_strx);
n_type = seekptr->n_type;
}
if (string_index == 0 || n_type & N_STAB) {
continue;
}
if ((n_type & N_TYPE) == N_UNDF) {
char * symbol_name;
CFStringRef cfSymbolName;
symbol_name = (char *)(string_list + string_index);
cfSymbolName = CFStringCreateWithCString(kCFAllocatorDefault,
symbol_name, kCFStringEncodingASCII);
if (!cfSymbolName) {
OSKextLogMemError();
goto finish;
}
CFDictionarySetValue(symbols, cfSymbolName, kCFBooleanTrue);
CFRelease(cfSymbolName);
}
}
result = true;
finish:
if (executable) {
(void)posix_madvise((void *)CFDataGetBytePtr(executable),
CFDataGetLength(executable),
POSIX_MADV_DONTNEED);
}
SAFE_RELEASE(executable);
return result;
}
Boolean __OSKextIsSearchableForSymbols(
OSKextRef aKext,
Boolean nonKPIFlag,
Boolean allowUnsupportedFlag)
{
CFStringRef kextID = NULL;
if (!OSKextIsLibrary(aKext)) {
return false;
}
if (!OSKextDeclaresExecutable(aKext)) {
return false;
}
kextID = OSKextGetIdentifier(aKext);
if (!allowUnsupportedFlag) {
if (CFEqual(kextID, CFSTR("com.apple.kernel.unsupported")) ||
CFEqual(kextID, CFSTR("com.apple.kpi.unsupported")) ||
CFEqual(kextID, CFSTR("com.apple.kpi.private")) ||
CFEqual(kextID, CFSTR("com.apple.kpi.dsep"))) {
return false;
}
}
if (nonKPIFlag) {
if (CFStringHasPrefix(kextID, __kOSKextKPIPrefix)) {
return false;
}
} else {
if (CFStringHasPrefix(kextID, __kOSKextKernelLibPrefix)) {
return false;
}
}
return true;
}
Boolean __OSKextFindSymbols(
OSKextRef aKext,
CFMutableDictionaryRef undefSymbols,
CFMutableDictionaryRef onedefSymbols,
CFMutableDictionaryRef multdefSymbols,
CFMutableArrayRef multdefLibs)
{
Boolean result = false;
char kextPath[PATH_MAX];
CFDataRef executable = NULL; const struct mach_header * mach_header = NULL;
const void * file_end = NULL;
Boolean sixtyfourbit = false;
uint8_t swap = 0;
macho_seek_result symtab_result = macho_seek_result_not_found;
struct symtab_command * symtab = NULL;
char * syms_address = NULL;
const void * string_list = NULL;
unsigned int sym_offset = 0;
unsigned int str_offset = 0;
unsigned int num_syms = 0;
unsigned int syms_bytes = 0;
unsigned int sym_index = 0;
CFMutableArrayRef libsArray = NULL; OSKextRef libKext = NULL; char * symbol_name = NULL; Boolean eligible = false;
Boolean notedMultdef = false;
CFStringRef cfSymbolName = NULL;
__OSKextGetFileSystemPath(aKext, NULL,
false, kextPath);
executable = OSKextCopyExecutableForArchitecture(aKext, OSKextGetArchitecture());
if (!executable) {
goto finish;
}
mach_header = (const struct mach_header *)CFDataGetBytePtr(executable);
file_end = (((const char *)mach_header) + CFDataGetLength(executable));
if (ISMACHO64(MAGIC32(mach_header))) {
sixtyfourbit = true;
}
if (ISSWAPPEDMACHO(MAGIC32(mach_header))) {
swap = 1;
}
symtab_result = macho_find_symtab(mach_header, file_end, &symtab);
if (symtab_result != macho_seek_result_found) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogLinkFlag,
"%s has no symtab in its executable (%s)",
kextPath, OSKextGetArchitecture()->name);
goto finish;
}
sym_offset = CondSwapInt32(swap, symtab->symoff);
str_offset = CondSwapInt32(swap, symtab->stroff);
num_syms = CondSwapInt32(swap, symtab->nsyms);
syms_address = (char *)mach_header + sym_offset;
string_list = (char *)mach_header + str_offset;
syms_bytes = num_syms * sizeof(struct nlist);
if (syms_address + syms_bytes > (char *)file_end) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogLinkFlag,
"%s - internal overrun in executable file (%s).",
kextPath, OSKextGetArchitecture()->name);
goto finish;
}
for (sym_index = 0; sym_index < num_syms; sym_index++) {
struct nlist * seekptr;
struct nlist_64 * seekptr_64;
uint32_t string_index;
uint8_t n_type;
if (sixtyfourbit) {
seekptr_64 = &((struct nlist_64 *)syms_address)[sym_index];
string_index = CondSwapInt32(swap, seekptr_64->n_un.n_strx);
n_type = seekptr_64->n_type;
} else {
seekptr = &((struct nlist *)syms_address)[sym_index];
string_index = CondSwapInt32(swap, seekptr->n_un.n_strx);
n_type = seekptr->n_type;
}
if (string_index == 0 || (n_type & N_STAB)) {
continue;
}
switch (n_type & N_TYPE) {
case N_UNDF:
case N_INDR:
eligible = OSKextIsKernelComponent(aKext) ? true : false;
break;
case N_SECT:
eligible = OSKextIsKernelComponent(aKext) ? false : true;
break;
default:
eligible = false;
break;
}
if (eligible) {
SAFE_RELEASE_NULL(cfSymbolName);
symbol_name = (char *)(string_list + string_index);
cfSymbolName = CFStringCreateWithCString(kCFAllocatorDefault,
symbol_name, kCFStringEncodingASCII);
if (!cfSymbolName) {
OSKextLogMemError();
result = false;
goto finish;
}
libsArray = (CFMutableArrayRef)CFDictionaryGetValue(
multdefSymbols, cfSymbolName);
if (libsArray) {
result = true;
CFArrayAppendValue(libsArray, aKext);
} else {
libKext = (OSKextRef)CFDictionaryGetValue(
onedefSymbols, cfSymbolName);
if (libKext) {
result = true;
libsArray = CFArrayCreateMutable(kCFAllocatorDefault,
0, &kCFTypeArrayCallBacks);
if (!libsArray) {
OSKextLogMemError();
goto finish;
}
CFArrayAppendValue(libsArray, libKext);
CFArrayAppendValue(libsArray, aKext);
CFDictionarySetValue(multdefSymbols, cfSymbolName, libsArray);
SAFE_RELEASE_NULL(libsArray);
if (!notedMultdef) {
if (kCFNotFound == CFArrayGetFirstIndexOfValue(
multdefLibs, RANGE_ALL(multdefLibs), aKext)) {
CFArrayAppendValue(multdefLibs, aKext);
}
notedMultdef = true;
}
CFDictionaryRemoveValue(onedefSymbols, cfSymbolName);
} else {
if (CFDictionaryGetValue(undefSymbols, cfSymbolName)) {
result = true;
CFDictionarySetValue(onedefSymbols, cfSymbolName, aKext);
CFDictionaryRemoveValue(undefSymbols, cfSymbolName);
}
}
}
}
}
finish:
if (executable) {
(void)posix_madvise((void *)CFDataGetBytePtr(executable),
CFDataGetLength(executable),
POSIX_MADV_DONTNEED);
}
SAFE_RELEASE(cfSymbolName);
SAFE_RELEASE(executable);
return result;
}
CFArrayRef OSKextFindLinkDependencies(
OSKextRef aKext,
Boolean nonKPIFlag,
Boolean allowUnsupportedFlag,
CFDictionaryRef * undefinedSymbolsOut,
CFDictionaryRef * onedefSymbolsOut,
CFDictionaryRef * multiplyDefinedSymbolsOut,
CFArrayRef * multipleDefinitionLibraries)
{
CFArrayRef result = NULL;
CFArrayRef allKexts = NULL; CFMutableArrayRef libKexts = NULL; CFMutableDictionaryRef undefSymbols = NULL; CFMutableDictionaryRef onedefSymbols = NULL; CFMutableDictionaryRef multdefSymbols = NULL; CFMutableArrayRef multdefLibs = NULL; char kextPath[PATH_MAX];
char dependencyPath[PATH_MAX];
CFIndex kextCount, kextIndex;
allKexts = OSKextGetAllKexts();
if (!allKexts) {
goto finish;
}
__OSKextGetFileSystemPath(aKext, NULL,
false, kextPath);
OSKextLog(aKext,
kOSKextLogStepLevel |
kOSKextLogDependenciesFlag | kOSKextLogLinkFlag,
"Searching for link dependencies of %s.",
kextPath);
libKexts = CFArrayCreateMutable(CFGetAllocator(aKext),
0, &kCFTypeArrayCallBacks);
undefSymbols = CFDictionaryCreateMutable(CFGetAllocator(aKext),
0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
onedefSymbols = CFDictionaryCreateMutable(CFGetAllocator(aKext),
0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
multdefSymbols = CFDictionaryCreateMutable(CFGetAllocator(aKext),
0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
multdefLibs = CFArrayCreateMutable(CFGetAllocator(aKext),
0, &kCFTypeArrayCallBacks);
if (!libKexts || !undefSymbols || !onedefSymbols ||
!multdefSymbols || !multdefLibs) {
OSKextLogMemError();
goto finish;
}
if (!__OSKextReadSymbolReferences(aKext, undefSymbols)) {
goto finish;
}
kextCount = CFArrayGetCount(allKexts);
for (kextIndex = 0; kextIndex < kextCount; kextIndex++) {
OSKextRef thisKext = (OSKextRef)CFArrayGetValueAtIndex(
allKexts, kextIndex);
if (thisKext != aKext &&
__OSKextIsSearchableForSymbols(thisKext, nonKPIFlag,
allowUnsupportedFlag)) {
if (__OSKextFindSymbols(thisKext, undefSymbols, onedefSymbols,
multdefSymbols, multdefLibs)) {
__OSKextGetFileSystemPath(thisKext, NULL,
false, dependencyPath);
OSKextLog(aKext,
kOSKextLogDetailLevel | kOSKextLogDependenciesFlag | kOSKextLogLinkFlag,
"%s found link dependency %s.",
kextPath, dependencyPath);
if (kCFNotFound == CFArrayGetFirstIndexOfValue(libKexts,
RANGE_ALL(libKexts), thisKext)) {
CFArrayAppendValue(libKexts, thisKext);
}
}
}
}
CFArraySortValues(libKexts, RANGE_ALL(libKexts),
&__OSKextCompareIdentifiers, NULL);
CFArraySortValues(multdefLibs, RANGE_ALL(multdefLibs),
&__OSKextCompareIdentifiers, NULL);
result = CFRetain(libKexts);
finish:
if (result) {
CFIndex count;
count = CFDictionaryGetCount(undefSymbols);
if (count) {
OSKextLog(aKext, kOSKextLogDetailLevel |
kOSKextLogDependenciesFlag | kOSKextLogLinkFlag,
"%s has %d remaining undefined symbol%s",
kextPath, (int)count, count > 1 ? "s" : "");
}
count = CFDictionaryGetCount(multdefSymbols);
if (count) {
OSKextLog(aKext, kOSKextLogDetailLevel |
kOSKextLogDependenciesFlag | kOSKextLogLinkFlag,
"%s has multiply defined %ld symbol%s",
kextPath, count, count > 1 ? "s" : "");
}
if (undefinedSymbolsOut) {
*undefinedSymbolsOut = CFRetain(undefSymbols);
}
if (onedefSymbolsOut) {
*onedefSymbolsOut = CFRetain(onedefSymbols);
}
if (multiplyDefinedSymbolsOut) {
*multiplyDefinedSymbolsOut = CFRetain(multdefSymbols);
}
if (multipleDefinitionLibraries) {
*multipleDefinitionLibraries = CFRetain(multdefLibs);
}
}
SAFE_RELEASE(libKexts);
SAFE_RELEASE(undefSymbols);
SAFE_RELEASE(onedefSymbols);
SAFE_RELEASE(multdefSymbols);
SAFE_RELEASE(multdefLibs);
return result;
}
typedef struct {
CFMutableArrayRef array;
uint32_t minDepth;
uint32_t depth;
Boolean error;
} __OSKextAddDependenciesContext;
static void __OSKextAddDependenciesApplierFunction(
const void * vKext,
void * vContext)
{
char kextPath[PATH_MAX];
OSKextRef aKext = (OSKextRef)vKext;
__OSKextAddDependenciesContext * context =
(__OSKextAddDependenciesContext *)vContext;
if (OSKextIsKernelComponent(aKext)) {
goto finish;
}
if (!aKext->loadInfo || !aKext->loadInfo->dependencies) {
__OSKextGetFileSystemPath(aKext, NULL, true,
kextPath);
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogDependenciesFlag,
"%s - missing load info or dependencies array in applier function.",
kextPath);
context->error = true;
goto finish;
}
context->depth++;
CFArrayApplyFunction(aKext->loadInfo->dependencies,
RANGE_ALL(aKext->loadInfo->dependencies),
&__OSKextAddDependenciesApplierFunction, context);
context->depth--;
finish:
if (!context->error) {
if (context->depth >= context->minDepth) {
if (CFArrayGetFirstIndexOfValue(context->array,
RANGE_ALL(context->array),
aKext) == -1) {
CFArrayAppendValue(context->array, aKext);
}
}
}
return;
}
CFMutableArrayRef __OSKextCopyDependenciesList(
OSKextRef aKext,
Boolean needAllFlag,
uint32_t minDepth)
{
CFMutableArrayRef result = NULL;
Boolean resolved = false;
__OSKextAddDependenciesContext context;
resolved = OSKextResolveDependencies(aKext);
if (needAllFlag && !resolved) {
goto finish;
}
result = CFArrayCreateMutable(CFGetAllocator(aKext), 0,
&kCFTypeArrayCallBacks);
if (!result) {
OSKextLogMemError();
goto finish;
}
context.array = result;
context.minDepth = minDepth;
context.depth = 0;
context.error = false;
__OSKextAddDependenciesApplierFunction(aKext, &context);
if (context.error) {
SAFE_RELEASE_NULL(result);
}
finish:
return result;
}
CFMutableArrayRef OSKextCopyLoadList(
OSKextRef aKext,
Boolean needAllFlag)
{
return __OSKextCopyDependenciesList(aKext, needAllFlag,
0);
}
CFMutableArrayRef OSKextCopyAllDependencies(
OSKextRef aKext,
Boolean needAllFlag)
{
return __OSKextCopyDependenciesList(aKext, needAllFlag,
1);
}
CFMutableArrayRef OSKextCopyIndirectDependencies(
OSKextRef aKext,
Boolean needAllFlag)
{
return __OSKextCopyDependenciesList(aKext, needAllFlag,
2);
}
Boolean OSKextDependsOnKext(OSKextRef aKext,
OSKextRef libraryKext,
Boolean directFlag)
{
Boolean result = false;
CFIndex count, i;
OSKextResolveDependencies(aKext);
if (!aKext->loadInfo || !aKext->loadInfo->dependencies) {
goto finish;
}
count = CFArrayGetCount(aKext->loadInfo->dependencies);
for (i = 0; i < count; i++) {
OSKextRef dependency = (OSKextRef)CFArrayGetValueAtIndex(
aKext->loadInfo->dependencies, i);
if ((dependency == libraryKext) ||
(!directFlag && OSKextDependsOnKext(dependency, libraryKext,
directFlag))) {
result = true;
goto finish;
}
}
finish:
return result;
}
CFMutableArrayRef OSKextCopyDependents(OSKextRef aKext,
Boolean directFlag)
{
CFMutableArrayRef result = NULL;
CFArrayRef allKexts = NULL; CFIndex count, i;
allKexts = OSKextGetAllKexts();
if (!allKexts) {
goto finish;
}
OSKextResolveDependencies(NULL);
result = CFArrayCreateMutable(CFGetAllocator(aKext), 0,
&kCFTypeArrayCallBacks);
if (!result) {
OSKextLogMemError();
goto finish;
}
count = CFArrayGetCount(allKexts);
for (i = 0; i < count; i++) {
OSKextRef thisKext = (OSKextRef)CFArrayGetValueAtIndex(allKexts, i);
if (OSKextDependsOnKext(thisKext, aKext, directFlag)) {
CFArrayAppendValue(result, thisKext);
}
}
finish:
return result;
}
Boolean OSKextIsCompatibleWithVersion(
OSKextRef aKext,
OSKextVersion aVersion)
{
Boolean result = false;
if (aKext->compatibleVersion > 0 &&
aKext->compatibleVersion <= aVersion &&
aKext->version >= aVersion) {
result = true;
}
return result;
}
typedef struct __OSKextPrintDependenciesContext {
UInt32 depth;
Boolean bundleIDFlag;
Boolean linkFlag;
} __OSKextPrintDependenciesContext;
#define _DEPENDENCY_DEPTH_INCREMENT (4)
void __OSKextLogDependencyGraphApplierFunction(
const void * vKext,
void * vContext)
{
OSKextRef aKext = (OSKextRef)vKext;
__OSKextPrintDependenciesContext dContext =
*(__OSKextPrintDependenciesContext *)vContext;
char * pad = NULL; CFStringRef kextLabel = NULL; char * kextName = NULL; char kextVersion[kOSKextVersionMaxLength];
char * note = ""; CFArrayRef linkDependencies = NULL; CFArrayRef dependencies = NULL;
if (!OSKextResolveDependencies(aKext)) {
goto finish;
}
if (dContext.depth) {
pad = (char *)malloc((1 + dContext.depth) * sizeof(char));
if (!pad) {
OSKextLogMemError();
goto finish;
}
memset(pad, ' ', dContext.depth);
pad[dContext.depth] = '\0';
}
if (dContext.bundleIDFlag) {
kextLabel = OSKextGetIdentifier(aKext);
CFRetain(kextLabel);
} else {
kextLabel = CFURLCopyLastPathComponent(aKext->bundleURL);
}
kextName = createUTF8CStringForCFString(kextLabel);
if (!kextName) {
goto finish;
}
OSKextVersionGetString(aKext->version, kextVersion, kOSKextVersionMaxLength);
if (!aKext->loadInfo || !aKext->loadInfo->dependencies) {
if (__OSKextHasAllDependencies(aKext)) {
note = "."; } else {
note = " (dependencies not resolved).";
}
} else {
if (__OSKextHasAllDependencies(aKext)) {
note = " ->";
} else {
note = " (dependencies not fully resolved) ->";
}
}
OSKextLog( NULL,
kOSKextLogExplicitLevel | kOSKextLogDependenciesFlag,
"%s%s (%s)%s", pad ? pad : "", kextName, kextVersion, note);
dContext.depth += _DEPENDENCY_DEPTH_INCREMENT;
if (dContext.linkFlag) {
linkDependencies = OSKextCopyLinkDependencies(aKext,
false);
dependencies = linkDependencies;
} else if (aKext->loadInfo && aKext->loadInfo->dependencies) {
dependencies = aKext->loadInfo->dependencies;
}
if (dependencies) {
CFArrayApplyFunction(dependencies, RANGE_ALL(dependencies),
__OSKextLogDependencyGraphApplierFunction, &dContext);
}
finish:
SAFE_FREE(pad);
SAFE_FREE(kextName);
SAFE_RELEASE(kextLabel);
SAFE_RELEASE(linkDependencies);
return;
}
void OSKextLogDependencyGraph(OSKextRef aKext,
Boolean bundleIDFlag,
Boolean linkFlag)
{
__OSKextPrintDependenciesContext context;
context.depth = 0;
context.bundleIDFlag = bundleIDFlag;
context.linkFlag = linkFlag;
OSKextResolveDependencies(aKext);
__OSKextLogDependencyGraphApplierFunction(aKext, &context);
}
#pragma mark Core Kernel IPC
CFMutableDictionaryRef __OSKextCreateKextRequest(
CFStringRef predicateIn,
CFTypeRef bundleIdentifierIn,
CFMutableDictionaryRef * argumentsOut)
{
CFMutableDictionaryRef result = NULL;
CFMutableDictionaryRef arguments = NULL; Boolean error = false;
result = CFDictionaryCreateMutable(kCFAllocatorDefault,
0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (!result) {
OSKextLogMemError();
goto finish;
}
CFDictionarySetValue(result, CFSTR(kKextRequestPredicateKey), predicateIn);
if (bundleIdentifierIn || argumentsOut) {
arguments = CFDictionaryCreateMutable(kCFAllocatorDefault,
0, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!arguments) {
OSKextLogMemError();
error = true;
goto finish;
}
CFDictionarySetValue(result, CFSTR(kKextRequestArgumentsKey),
arguments);
if (argumentsOut) {
*argumentsOut = arguments;
}
if (bundleIdentifierIn) {
CFDictionarySetValue(arguments,
CFSTR(kKextRequestArgumentBundleIdentifierKey),
bundleIdentifierIn);
}
}
finish:
if (error) {
SAFE_RELEASE_NULL(result);
}
SAFE_RELEASE(arguments);
return result;
}
OSReturn __OSKextSendKextRequest(
OSKextRef aKext,
CFDictionaryRef kextRequest,
CFTypeRef * cfResponseOut,
char ** rawResponseOut,
uint32_t * rawResponseLengthOut)
{
OSReturn result = kOSReturnError;
kern_return_t mig_result = KERN_FAILURE;
OSReturn op_result = kOSReturnError;
CFDataRef requestData = NULL; Boolean deallocResponse = true;
char * responseBuffer = NULL; uint32_t responseLength = 0;
char * logInfoBuffer = NULL; uint32_t logInfoLength = 0;
host_priv_t hostPriv = HOST_PRIV_NULL; CFStringRef errorString = NULL; char * errorCString = NULL;
hostPriv = mach_host_self();
requestData = IOCFSerialize(kextRequest, kNilOptions);
if (!requestData) {
result = kOSKextReturnSerialization;
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Failed to serialize kext request.");
goto finish;
}
mig_result = kext_request(
hostPriv,
__sOSKextLogOutputFunction ? __sKernelLogFilter : kOSKextLogSilentFilter,
(vm_offset_t)CFDataGetBytePtr(requestData),
CFDataGetLength(requestData),
(vm_offset_t *)&responseBuffer,
&responseLength,
(vm_offset_t *)&logInfoBuffer,
&logInfoLength,
&op_result);
result = __OSKextProcessKextRequestResults(aKext,
mig_result, op_result,
(char *)logInfoBuffer, logInfoLength);
if (result != kOSReturnSuccess) {
goto finish;
}
if (responseBuffer && responseLength) {
if (cfResponseOut) {
*cfResponseOut = IOCFUnserialize(responseBuffer, kCFAllocatorDefault,
0, &errorString);
if (!*cfResponseOut) {
result = kOSKextReturnSerialization;
if (errorString) {
errorCString = createUTF8CStringForCFString(errorString);
}
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Can't unserialize kext request response: %s",
errorCString ? errorCString : "unknown error");
SAFE_FREE_NULL(errorCString);
SAFE_RELEASE_NULL(errorString);
goto finish;
}
} else if (rawResponseOut && rawResponseLengthOut) {
*rawResponseOut = responseBuffer;
*rawResponseLengthOut = responseLength;
deallocResponse = false;
}
}
result = kOSReturnSuccess;
finish:
SAFE_RELEASE(requestData);
SAFE_RELEASE(errorString);
SAFE_FREE(errorCString);
if (deallocResponse && responseBuffer && responseLength) {
vm_deallocate(mach_task_self(), (vm_address_t)responseBuffer,
responseLength);
}
if (logInfoBuffer) {
vm_deallocate(mach_task_self(), (vm_address_t)logInfoBuffer,
logInfoLength);
}
if (hostPriv != HOST_PRIV_NULL) {
mach_port_deallocate(mach_task_self(), hostPriv);
}
return result;
}
OSReturn __OSKextSimpleKextRequest(
OSKextRef aKext,
CFStringRef predicate,
CFTypeRef * cfResponseOut)
{
kern_return_t result = kOSKextReturnInternalError;
CFDictionaryRef kextRequest = NULL;
kextRequest = __OSKextCreateKextRequest(predicate,
aKext ? OSKextGetIdentifier(aKext) : NULL, NULL);
if (!kextRequest) {
goto finish;
}
result = __OSKextSendKextRequest(aKext, kextRequest,
cfResponseOut,
NULL, NULL);
finish:
SAFE_RELEASE(kextRequest);
return result;
}
OSReturn __OSKextProcessKextRequestResults(
OSKextRef aKext,
kern_return_t mig_result,
kern_return_t op_result,
char * logInfoBuffer,
uint32_t logInfoLength)
{
OSReturn result = kOSReturnError;
CFTypeRef kernelLogMessages = NULL; CFStringRef errorString = NULL; char * errorCString = NULL;
if (mig_result != KERN_SUCCESS) {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Error communicating with kernel - %s.",
safe_mach_error_string(mig_result));
result = mig_result;
goto finish;
}
if (logInfoBuffer && logInfoLength) {
kernelLogMessages = IOCFUnserialize((char *)logInfoBuffer,
kCFAllocatorDefault,
0, &errorString);
if (kernelLogMessages) {
__OSKextLogKernelMessages(aKext, kernelLogMessages);
} else {
errorCString = createUTF8CStringForCFString(errorString);
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Failed to parse kernel log messages: %s.",
errorCString ? errorCString : "(unknown)");
}
}
if (op_result != KERN_SUCCESS) {
OSKextLog(aKext, kOSKextLogDebugLevel | kOSKextLogIPCFlag,
"Kernel error handling kext request - %s.",
safe_mach_error_string(op_result));
result = op_result;
goto finish;
}
result = kOSReturnSuccess;
finish:
SAFE_RELEASE(kernelLogMessages);
SAFE_RELEASE(errorString);
SAFE_FREE(errorCString);
return result;
}
#pragma mark Linking and Loading; Other Kernel Operations
OSReturn __OSKextLoadWithArgsDict(
OSKextRef aKext,
CFDictionaryRef loadArgsDict)
{
OSReturn result = kOSReturnError;
CFArrayRef loadList = NULL; CFMutableArrayRef kextIdentifiers = NULL; kern_return_t mig_result = KERN_FAILURE;
OSReturn op_result = kOSReturnError;
host_priv_t hostPriv = HOST_PRIV_NULL; CFDataRef mkext = NULL; const UInt8 * requestBuffer = NULL;
CFIndex requestLength = 0;
vm_address_t responseBuffer = 0; uint32_t responseLength = 0;
vm_address_t logInfoBuffer = 0; uint32_t logInfoLength = 0;
CFStringRef errorString = NULL; char kextPath[PATH_MAX];
CFIndex count = 0, i = 0;
hostPriv = mach_host_self();
if (hostPriv == HOST_PRIV_NULL) {
result = kOSKextReturnNotPrivileged;
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"Process must be running as root to load kexts.");
goto finish;
}
__OSKextGetFileSystemPath(aKext, NULL,
false, kextPath);
if (!__OSKextIsValid(aKext)) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"Can't load %s - validation problems.", kextPath);
result = kOSKextReturnValidation;
goto finish;
}
if (!OSKextSupportsArchitecture(aKext, OSKextGetArchitecture())) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"Can't load %s - no code for running kernel's architecture.",
kextPath);
result = kOSKextReturnArchNotFound;
goto finish;
}
if ((OSKextGetActualSafeBoot() || OSKextGetSimulatedSafeBoot()) &&
!OSKextIsLoadableInSafeBoot(aKext)) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"Can't load %s - ineligible during safe boot.", kextPath);
result = kOSKextReturnBootLevel;
goto finish;
}
if (!OSKextIsAuthentic(aKext)) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"Can't load %s - authentication problems.", kextPath);
result = kOSKextReturnAuthentication;
goto finish;
}
OSKextFlushLoadInfo( NULL, true);
loadList = OSKextCopyLoadList(aKext, true);
if (!loadList) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"Can't load %s - failed to resolve dependencies.", kextPath);
result = kOSKextReturnDependencies;
goto finish;
}
if (!OSKextAuthenticateDependencies(aKext)) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"Can't load %s - dependency authentication problems.", kextPath);
result = kOSKextReturnAuthentication;
goto finish;
}
if (!OSKextDependenciesAreLoadableInSafeBoot(aKext)) {
CFDictionaryRef bootLevelDiagnostics = NULL; CFArrayRef ineligibleDependencies = NULL; CFStringRef ineligibleDependenciesString = NULL;
bootLevelDiagnostics = __OSKextGetDiagnostics(aKext,
kOSKextDiagnosticsFlagBootLevel);
if (bootLevelDiagnostics) {
ineligibleDependencies = CFDictionaryGetValue(bootLevelDiagnostics,
kOSKextDependencyIneligibleInSafeBoot);
}
if (ineligibleDependencies && CFArrayGetCount(ineligibleDependencies)) {
ineligibleDependenciesString = createCFStringForPlist_new(
ineligibleDependencies, kPListStyleDiagnostics);
}
if (ineligibleDependenciesString) {
OSKextLogCFString(aKext, kOSKextLogErrorLevel | kOSKextLogLoadFlag,
CFSTR("Can't load %s - dependencies ineligible during safe boot:\n%@"),
kextPath, ineligibleDependenciesString);
} else {
OSKextLogCFString(aKext, kOSKextLogErrorLevel | kOSKextLogLoadFlag,
CFSTR("Can't load %s - dependencies ineligible during safe boot."),
kextPath);
}
SAFE_RELEASE_NULL(ineligibleDependenciesString);
result = kOSKextReturnBootLevel;
goto finish;
}
count = CFArrayGetCount(loadList);
kextIdentifiers = CFArrayCreateMutable(CFGetAllocator(aKext),
count, &kCFTypeArrayCallBacks);
if (!kextIdentifiers) {
OSKextLogMemError();
goto finish;
}
for (i = 0; i < count; i++) {
OSKextRef thisKext = (OSKextRef)CFArrayGetValueAtIndex(loadList, i);
CFArrayAppendValue(kextIdentifiers, OSKextGetIdentifier(thisKext));
}
SAFE_RELEASE_NULL(loadList);
result = OSKextReadLoadedKextInfo(kextIdentifiers,
true);
if (result != kOSReturnSuccess) {
goto finish;
}
if (!OSKextIsLoaded(aKext)) {
Boolean otherVersionLoaded = false;
Boolean otherUUIDLoaded = false;
const char * difference = NULL;
otherVersionLoaded = OSKextOtherVersionIsLoaded(aKext, &otherUUIDLoaded);
if (otherUUIDLoaded) {
difference = "UUID";
} else if (otherVersionLoaded) {
difference = "version";
}
if (difference) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"Can't load %s - a different %s is already loaded.",
kextPath, difference);
result = kOSKextReturnLoadedVersionDiffers;
goto finish;
}
}
loadList = OSKextCopyLoadList(aKext, true);
if (!loadList) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"Can't load %s - failed to resolve dependencies based on loaded kexts.",
kextPath);
result = kOSKextReturnDependencies;
goto finish;
}
if (!OSKextAuthenticateDependencies(aKext)) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"Can't load %s - dependency authentication problems.", kextPath);
result = kOSKextReturnAuthentication;
goto finish;
}
mkext = __OSKextCreateMkext(CFGetAllocator(aKext), loadList,
NULL,
0,
false,
true,
loadArgsDict);
if (!mkext) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"Can't create kernel load request for %s.", kextPath);
goto finish;
}
requestBuffer = CFDataGetBytePtr(mkext);
requestLength = CFDataGetLength(mkext);
OSKextLog(aKext, kOSKextLogProgressLevel | kOSKextLogLoadFlag,
"Loading %s.", kextPath);
mig_result = kext_request(
hostPriv,
__sOSKextLogOutputFunction ? __sKernelLogFilter : kOSKextLogSilentFilter,
(vm_offset_t)requestBuffer,
(mach_msg_type_number_t)requestLength,
&responseBuffer,
&responseLength,
(vm_offset_t *)&logInfoBuffer,
&logInfoLength,
&op_result);
result = __OSKextProcessKextRequestResults(aKext,
mig_result, op_result,
(char *)logInfoBuffer, logInfoLength);
if (result != kOSReturnSuccess) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"Failed to load %s - %s.",
kextPath, safe_mach_error_string(result));
goto finish;
}
finish:
SAFE_RELEASE(kextIdentifiers);
SAFE_RELEASE(loadList);
SAFE_RELEASE(mkext);
SAFE_RELEASE(errorString);
if (responseBuffer) {
vm_deallocate(mach_task_self(), responseBuffer, responseLength);
}
if (logInfoBuffer) {
vm_deallocate(mach_task_self(), logInfoBuffer, logInfoLength);
}
if (result == kOSReturnSuccess) {
OSKextLog(aKext, kOSKextLogProgressLevel | kOSKextLogLoadFlag,
"Successfully loaded %s.", kextPath);
} else {
OSKextRemoveKextPersonalitiesFromKernel(aKext);
}
return result;
}
OSReturn OSKextLoad(OSKextRef aKext)
{
return OSKextLoadWithOptions(aKext,
kOSKextExcludeNone,
kOSKextExcludeAll,
NULL,
false);
}
OSReturn OSKextLoadWithOptions(
OSKextRef aKext,
OSKextExcludeLevel startExclusion,
OSKextExcludeLevel addPersonalitiesExclusion,
CFArrayRef personalityNames,
Boolean delayAutounloadFlag)
{
OSReturn result = kOSReturnError;
CFMutableDictionaryRef loadArgs = NULL; CFNumberRef startExclusionNum = NULL; CFNumberRef addPersonalitiesExclusionNum = NULL;
loadArgs = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (!loadArgs) {
OSKextLogMemError();
goto finish;
}
CFDictionarySetValue(loadArgs,
CFSTR(kKextRequestArgumentBundleIdentifierKey),
OSKextGetIdentifier(aKext));
startExclusionNum = CFNumberCreate(CFGetAllocator(aKext),
kCFNumberSInt8Type,
&startExclusion);
if (!startExclusionNum) {
OSKextLogMemError();
goto finish;
}
CFDictionarySetValue(loadArgs, CFSTR(kKextRequestArgumentStartExcludeKey),
startExclusionNum);
addPersonalitiesExclusionNum = CFNumberCreate(CFGetAllocator(aKext),
kCFNumberSInt8Type,
&addPersonalitiesExclusion);
if (!addPersonalitiesExclusionNum) {
OSKextLogMemError();
goto finish;
}
CFDictionarySetValue(loadArgs, CFSTR(kKextRequestArgumentStartMatchingExcludeKey),
addPersonalitiesExclusionNum);
if (personalityNames) {
CFDictionarySetValue(loadArgs,
CFSTR(kKextRequestArgumentPersonalityNamesKey),
personalityNames);
}
if (delayAutounloadFlag) {
CFDictionarySetValue(loadArgs, CFSTR(kKextRequestArgumentDelayAutounloadKey),
kCFBooleanTrue);
}
result = __OSKextLoadWithArgsDict(aKext, loadArgs);
finish:
SAFE_RELEASE(loadArgs);
SAFE_RELEASE(startExclusionNum);
SAFE_RELEASE(addPersonalitiesExclusionNum);
return result;
}
#ifndef IOKIT_EMBEDDED
Boolean __OSKextInitKXLDDependency(
KXLDDependency * dependency,
OSKextRef aKext,
CFDataRef kernelImage,
Boolean isDirect)
{
Boolean result = FALSE;
char kextPath[PATH_MAX];
if (!aKext->loadInfo->linkedExecutable) {
__OSKextGetFileSystemPath(aKext,
NULL,
false, kextPath);
OSKextLog(aKext, kOSKextLogErrorLevel,
"Can't use %s - not linked.", kextPath);
goto finish;
}
if (OSKextIsInterface(aKext)) {
CFDataRef interfaceTarget = NULL;
CFStringRef interfaceTargetName = NULL;
if (OSKextIsKernelComponent(aKext)) {
interfaceTarget = kernelImage;
interfaceTargetName = __kOSKextKernelIdentifier;
} else {
OSKextRef interfaceTargetKext = (OSKextRef)
CFArrayGetValueAtIndex(aKext->loadInfo->dependencies, 0);
if (!interfaceTargetKext->loadInfo->linkedExecutable) {
__OSKextGetFileSystemPath(interfaceTargetKext,
NULL,
false, kextPath);
OSKextLog(aKext, kOSKextLogErrorLevel,
"Can't use %s - not linked.", kextPath);
goto finish;
}
interfaceTarget = interfaceTargetKext->loadInfo->linkedExecutable;
interfaceTargetName = interfaceTargetKext->bundleID;
}
dependency->kext = (u_char *) CFDataGetBytePtr(interfaceTarget);
dependency->kext_size = CFDataGetLength(interfaceTarget);
dependency->kext_name =
createUTF8CStringForCFString(interfaceTargetName);
dependency->interface = (u_char *)
CFDataGetBytePtr(aKext->loadInfo->linkedExecutable);
dependency->interface_size =
CFDataGetLength(aKext->loadInfo->linkedExecutable);
dependency->interface_name = createUTF8CStringForCFString(
OSKextGetIdentifier(aKext));
} else {
dependency->kext = (u_char *)
CFDataGetBytePtr(aKext->loadInfo->linkedExecutable);
dependency->kext_size =
CFDataGetLength(aKext->loadInfo->linkedExecutable);
dependency->kext_name = createUTF8CStringForCFString(
OSKextGetIdentifier(aKext));
dependency->interface = NULL;
dependency->interface_size = 0;
dependency->interface_name = NULL;
}
dependency->is_direct_dependency = isDirect;
result = TRUE;
finish:
return result;
}
CFDataRef __OSKextCopyStrippedExecutable(OSKextRef aKext)
{
CFDataRef result = NULL;
CFMutableDataRef strippedExecutable = NULL;
u_char *file;
u_long fileSize;
u_long amountRemoved = 0;
if (!aKext->loadInfo->linkedExecutable) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s %d - no linkedExecutable",
__func__, __LINE__);
goto finish;
}
#if SPLIT_KEXTS
#endif
fileSize = CFDataGetLength(aKext->loadInfo->linkedExecutable);
strippedExecutable = CFDataCreateMutableCopy(kCFAllocatorDefault,
fileSize,
aKext->loadInfo->linkedExecutable);
if (!strippedExecutable) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s %d - no strippedExecutable",
__func__, __LINE__);
goto finish;
}
file = (u_char *) CFDataGetMutableBytePtr(strippedExecutable);
if (!file) {
goto finish;
}
if (__OSKextIsArchitectureLP64()) {
struct segment_command_64 * linkedit =
macho_get_segment_by_name_64((struct mach_header_64 *)file, SEG_LINKEDIT);
if (!linkedit) {
goto finish;
}
if ((roundPageCrossSafe(aKext->loadInfo->linkInfo.vmaddr_TEXT) + roundPageCrossSafe(aKext->loadInfo->linkInfo.linkedKextSize)) !=
roundPageCrossSafe(linkedit->vmaddr) + roundPageCrossSafe(linkedit->vmsize))
{
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s %d - __LINKEDIT__ segment is not last segment (%p + %llu != %p + %llu)",
__func__, __LINE__,
(void *)roundPageCrossSafe((vm_offset_t)(aKext->loadInfo->linkInfo.vmaddr_TEXT)),
(uint64_t)roundPageCrossSafe((vm_offset_t)(aKext->loadInfo->linkInfo.linkedKextSize)),
(void *)roundPageCrossSafe((vm_offset_t)(linkedit->vmaddr)),
(uint64_t)roundPageCrossSafe((vm_offset_t)(linkedit->vmsize)));
goto finish;
}
} else {
struct segment_command * linkedit =
macho_get_segment_by_name((struct mach_header *)file, SEG_LINKEDIT);
if (!linkedit) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s %d - no __LINKEDIT__ segment",
__func__, __LINE__);
goto finish;
}
if ((roundPageCrossSafe(aKext->loadInfo->linkInfo.vmaddr_TEXT) + roundPageCrossSafe(aKext->loadInfo->linkInfo.linkedKextSize)) !=
roundPageCrossSafe(linkedit->vmaddr) + roundPageCrossSafe(linkedit->vmsize))
{
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s %d - __LINKEDIT__ segment is not last segment (%p + %llu != %p + %llu)",
__func__, __LINE__,
(void *)roundPageCrossSafe((vm_offset_t)(aKext->loadInfo->linkInfo.vmaddr_TEXT)),
(uint64_t)roundPageCrossSafe((vm_offset_t)(aKext->loadInfo->linkInfo.linkedKextSize)),
(void *)roundPageCrossSafe((vm_offset_t)(linkedit->vmaddr)),
(uint64_t)roundPageCrossSafe((vm_offset_t)(linkedit->vmsize)));
goto finish;
}
}
if (!macho_trim_linkedit(file, &amountRemoved)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s %d - macho_trim_linkedit failed",
__func__, __LINE__);
goto finish;
}
fileSize -= amountRemoved;
CFDataSetLength(strippedExecutable, fileSize);
result = CFRetain(strippedExecutable);
finish:
SAFE_RELEASE_NULL(strippedExecutable);
return result;
}
static Boolean __OSKextPerformSplitLink(
OSKextRef aKext,
plkInfo * plkInfo,
KXLDContext * kxldContext)
{
Boolean result = false;
char * bundleIDCString = NULL; CFArrayRef dependencies = NULL; CFMutableArrayRef indirectDependencies = NULL; CFDataRef kextExecutable = NULL;
kern_return_t kxldResult = KERN_FAILURE;
KXLDDependency * kxldDependencies = NULL; CFIndex numKxldDependencies = 0;
kxld_addr_t kmodInfoKern = 0;
CFIndex numDirectDependencies = 0;
CFIndex numIndirectDependencies = 0;
__OSKextKXLDCallbackContext linkAddressContext;
CFDataRef relocData = NULL; char kextPath[PATH_MAX];
CFIndex i;
boolean_t isSplitKext = false;
if (!OSKextDeclaresExecutable(aKext)) {
result = true;
goto finish;
}
__OSKextGetFileSystemPath(aKext, NULL,
false, kextPath);
kextExecutable = OSKextCopyExecutableForArchitecture(aKext,
OSKextGetArchitecture());
if (!kextExecutable) {
OSKextLog(aKext, kOSKextLogErrorLevel,
"Can't link %s - architecture %s not found", kextPath,
OSKextGetArchitecture()->name);
goto finish;
}
kcgen_verboseLog("Linking split kext: %s", kextPath);
if (__OSKextSetLinkInfo(aKext, plkInfo, kextExecutable) == false) {
OSKextLog(aKext, kOSKextLogErrorLevel,
"Can't get link info for '%s'", kextPath);
goto finish;
}
isSplitKext = __OSKextIsSplitKext(aKext);
if (OSKextIsInterface(aKext)) {
result = true;
goto finish;
}
dependencies = OSKextCopyLinkDependencies(aKext, true);
if (!dependencies) {
goto finish;
}
bundleIDCString = createUTF8CStringForCFString(OSKextGetIdentifier(aKext));
numDirectDependencies = CFArrayGetCount(dependencies);
if (!numDirectDependencies) {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogLinkFlag,
"Internal error: attempting to link a kext without its dependencies.");
goto finish;
}
if (__OSKextGetBleedthroughFlag(aKext)) {
numIndirectDependencies = 0;
} else {
indirectDependencies = OSKextCopyIndirectDependencies(aKext,
TRUE);
if (!indirectDependencies) {
goto finish;
}
for (i = 0; i < CFArrayGetCount(indirectDependencies); ++i) {
OSKextRef indirectDependency =
(OSKextRef) CFArrayGetValueAtIndex(indirectDependencies, i);
if (!OSKextDeclaresExecutable(indirectDependency) ||
CFArrayContainsValue(dependencies, RANGE_ALL(dependencies),
indirectDependency))
{
CFArrayRemoveValueAtIndex(indirectDependencies, i);
i--;
continue;
}
}
numIndirectDependencies = CFArrayGetCount(indirectDependencies);
}
numKxldDependencies = numDirectDependencies + numIndirectDependencies;
kxldDependencies = (KXLDDependency *)
calloc(numKxldDependencies, sizeof(*kxldDependencies));
if (!kxldDependencies) {
OSKextLogMemError();
goto finish;
}
for (i = 0; i < numDirectDependencies; i++) {
OSKextRef dependency =
(OSKextRef) CFArrayGetValueAtIndex(dependencies, i);
if (!__OSKextInitKXLDDependency(&kxldDependencies[i],
dependency,
plkInfo->kernelImage,
TRUE)) {
goto finish;
}
}
for (i = 0; i < numIndirectDependencies; i++) {
OSKextRef dependency =
(OSKextRef) CFArrayGetValueAtIndex(indirectDependencies, i);
if (!__OSKextInitKXLDDependency(&kxldDependencies[i + numDirectDependencies],
dependency,
plkInfo->kernelImage,
FALSE)) {
goto finish;
}
}
(void)posix_madvise((void *)CFDataGetBytePtr(kextExecutable),
CFDataGetLength(kextExecutable),
POSIX_MADV_WILLNEED);
linkAddressContext.kernelLoadAddress = NULL;
linkAddressContext.kext = aKext;
kxldResult = kxld_link_split_file(kxldContext,
&aKext->loadInfo->linkInfo,
bundleIDCString,
(void *)&linkAddressContext,
kxldDependencies,
numKxldDependencies,
&kmodInfoKern);
#if SPLIT_KEXTS_DEBUG
{
u_char *kext_exe = (u_char *)CFDataGetBytePtr(kextExecutable);
unsigned int kext_exe_sz = (unsigned int)CFDataGetLength(kextExecutable);
u_char *linked_kext = (u_char *)aKext->loadInfo->linkInfo.linkedKext;
unsigned int linked_kext_sz = (unsigned int)aKext->loadInfo->linkInfo.linkedKextSize;
kcgen_verboseLog("Linked %s from %p-%p (%d) into %p-%p (%d)", kextPath,
kext_exe, kext_exe + kext_exe_sz, kext_exe_sz,
linked_kext, linked_kext + linked_kext_sz, linked_kext_sz);
}
#endif
for (i = 0; i < numKxldDependencies; i++) {
SAFE_FREE(kxldDependencies[i].kext_name);
SAFE_FREE(kxldDependencies[i].interface_name);
}
if (kxldResult != KERN_SUCCESS) {
OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"%s - Link error %d for \"%s\"",
__func__, kxldResult, kextPath);
goto finish;
}
if (aKext->loadInfo->linkInfo.kaslr_offsets) {
if (!__OSKextConvertExeOfstsToLinkedOfsts(aKext)) {
OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"%s - KASLR offset conversion error for \"%s\"",
__func__, kextPath);
goto finish;
}
}
if (aKext->loadInfo->linkInfo.linkedKext &&
aKext->loadInfo->linkInfo.linkedKextSize) {
relocData = CFDataCreateWithBytesNoCopy(CFGetAllocator(aKext),
aKext->loadInfo->linkInfo.linkedKext,
aKext->loadInfo->linkInfo.linkedKextSize,
kCFAllocatorNull);
if (!relocData) {
OSKextLogMemError();
goto finish;
}
aKext->loadInfo->linkedExecutable = CFRetain(relocData);
aKext->loadInfo->kmodInfoAddress = kmodInfoKern;
}
result = true;
finish:
if (kextExecutable) {
(void)posix_madvise((void *)CFDataGetBytePtr(kextExecutable),
CFDataGetLength(kextExecutable),
POSIX_MADV_DONTNEED);
}
SAFE_RELEASE(kextExecutable);
SAFE_RELEASE(dependencies);
SAFE_RELEASE(indirectDependencies);
SAFE_RELEASE(relocData);
SAFE_FREE(kxldDependencies);
SAFE_FREE(bundleIDCString);
return result;
}
static Boolean __OSKextPerformLink(
OSKextRef aKext,
CFDataRef kernelImage,
uint64_t kernelLoadAddress,
Boolean stripSymbolsFlag,
KXLDContext * kxldContext)
{
Boolean result = false;
char * bundleIDCString = NULL; CFArrayRef dependencies = NULL; CFMutableArrayRef indirectDependencies = NULL; CFDataRef kextExecutable = NULL;
kern_return_t kxldResult = KERN_FAILURE;
KXLDDependency * kxldDependencies = NULL; CFIndex numKxldDependencies = 0;
kxld_addr_t kmodInfoKern = 0;
CFIndex numDirectDependencies = 0;
CFIndex numIndirectDependencies = 0;
__OSKextKXLDCallbackContext linkAddressContext;
u_char * relocBytes = NULL; u_char ** relocBytesPtr = NULL;
CFDataRef relocData = NULL; char kextPath[PATH_MAX];
CFIndex i;
if (!OSKextDeclaresExecutable(aKext)) {
result = true;
goto finish;
}
__OSKextGetFileSystemPath(aKext, NULL,
false, kextPath);
kextExecutable = OSKextCopyExecutableForArchitecture(aKext,
OSKextGetArchitecture());
if (!kextExecutable) {
OSKextLog(aKext, kOSKextLogErrorLevel,
"Can't link %s - architecture %s not found", kextPath,
OSKextGetArchitecture()->name);
goto finish;
}
if (OSKextIsInterface(aKext)) {
aKext->loadInfo->linkedExecutable = CFRetain(kextExecutable);
aKext->loadInfo->prelinkedExecutable = CFRetain(kextExecutable);
aKext->loadInfo->linkInfo.linkedKextSize = CFDataGetLength(kextExecutable);
result = true;
goto finish;
}
dependencies = OSKextCopyLinkDependencies(aKext, true);
if (!dependencies) {
goto finish;
}
OSKextLog(aKext, kOSKextLogProgressLevel | kOSKextLogLinkFlag,
"Linking %s.", kextPath);
bundleIDCString = createUTF8CStringForCFString(
OSKextGetIdentifier(aKext));
numDirectDependencies = CFArrayGetCount(dependencies);
if (!numDirectDependencies) {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogLinkFlag,
"Internal error: attempting to link a kext without its dependencies.");
goto finish;
}
if (__OSKextGetBleedthroughFlag(aKext)) {
numIndirectDependencies = 0;
} else {
indirectDependencies = OSKextCopyIndirectDependencies(aKext,
TRUE);
if (!indirectDependencies) {
goto finish;
}
for (i = 0; i < CFArrayGetCount(indirectDependencies); ++i) {
OSKextRef indirectDependency =
(OSKextRef) CFArrayGetValueAtIndex(indirectDependencies, i);
if (!OSKextDeclaresExecutable(indirectDependency) ||
CFArrayContainsValue(dependencies, RANGE_ALL(dependencies),
indirectDependency))
{
CFArrayRemoveValueAtIndex(indirectDependencies, i);
i--;
continue;
}
}
numIndirectDependencies = CFArrayGetCount(indirectDependencies);
}
numKxldDependencies = numDirectDependencies + numIndirectDependencies;
kxldDependencies = (KXLDDependency *) calloc(numKxldDependencies, sizeof(*kxldDependencies));
if (!kxldDependencies) {
OSKextLogMemError();
goto finish;
}
for (i = 0; i < numDirectDependencies; i++) {
OSKextRef dependency =
(OSKextRef) CFArrayGetValueAtIndex(dependencies, i);
if (!__OSKextInitKXLDDependency(&kxldDependencies[i],
dependency, kernelImage, TRUE)) {
goto finish;
}
}
for (i = 0; i < numIndirectDependencies; i++) {
OSKextRef dependency =
(OSKextRef) CFArrayGetValueAtIndex(indirectDependencies, i);
if (!__OSKextInitKXLDDependency(
&kxldDependencies[i + numDirectDependencies],
dependency, kernelImage, FALSE)) {
goto finish;
}
}
relocBytesPtr = &relocBytes;
(void)posix_madvise((void *)CFDataGetBytePtr(kextExecutable),
CFDataGetLength(kextExecutable),
POSIX_MADV_WILLNEED);
linkAddressContext.kernelLoadAddress = kernelLoadAddress;
linkAddressContext.kext = aKext;
kxldResult = kxld_link_file(kxldContext,
(void *)CFDataGetBytePtr(kextExecutable),
CFDataGetLength(kextExecutable),
bundleIDCString,
(void *)&linkAddressContext,
kxldDependencies, numKxldDependencies,
relocBytesPtr,
&kmodInfoKern);
for (i = 0; i < numKxldDependencies; i++) {
SAFE_FREE(kxldDependencies[i].kext_name);
SAFE_FREE(kxldDependencies[i].interface_name);
}
if (kxldResult != KERN_SUCCESS) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogLinkFlag,
"Link failed (error code %d).", kxldResult);
goto finish;
}
if (relocBytes && aKext->loadInfo->linkInfo.linkedKextSize) {
relocData = CFDataCreateWithBytesNoCopy(CFGetAllocator(aKext),
relocBytes,
aKext->loadInfo->linkInfo.linkedKextSize,
kCFAllocatorDefault);
if (!relocData) {
OSKextLogMemError();
goto finish;
}
aKext->loadInfo->linkedExecutable = CFRetain(relocData);
aKext->loadInfo->kmodInfoAddress = kmodInfoKern;
if (stripSymbolsFlag) {
#define KMOD_INFO_DEBUG 0
#if KMOD_INFO_DEBUG
u_int64_t originalLoadSize = aKext->loadInfo->linkInfo.linkedKextSize;
#endif // KMOD_INFO_DEBUG
aKext->loadInfo->prelinkedExecutable = __OSKextCopyStrippedExecutable(aKext);
if (!aKext->loadInfo->prelinkedExecutable) {
char tempStr[1024];
CFStringGetCString(aKext->bundleID, tempStr, sizeof(tempStr),
kCFStringEncodingUTF8);
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogLinkFlag,
"Can't strip executable for %s <%s>", tempStr, __func__);
aKext->loadInfo->prelinkedExecutable = CFRetain(relocData);
goto finish;
}
aKext->loadInfo->linkInfo.linkedKextSize = CFDataGetLength(aKext->loadInfo->prelinkedExecutable);
{
u_char *file = (u_char *) CFDataGetMutableBytePtr((CFMutableDataRef) aKext->loadInfo->prelinkedExecutable);
Boolean is_32bit_kext = ((struct mach_header *) file)->magic == MH_MAGIC;
u_int64_t kmodInfo_offset = kmodInfoKern - aKext->loadInfo->linkInfo.vmaddr_TEXT;
vm_size_t *size_field = (vm_size_t *) (is_32bit_kext ?
(file + kmodInfo_offset + offsetof(kmod_info_32_v1_t, size)) :
(file + kmodInfo_offset + offsetof(kmod_info_64_v1_t, size)));
#if KMOD_INFO_DEBUG
vm_size_t original_kmodInfo_size = *size_field;
OSKextLog(aKext, kOSKextLogDebugLevel | kOSKextLogLinkFlag | kOSKextLogLoadFlag,
"--original load size: 0x%.8llx, original kmod_info.size: 0x%.8x",
originalLoadSize, (unsigned int) original_kmodInfo_size);
#endif // KMOD_INFO_DEBUG
*size_field = aKext->loadInfo->linkInfo.linkedKextSize;
#if KMOD_INFO_DEBUG
OSKextLog(aKext, kOSKextLogDebugLevel | kOSKextLogLinkFlag | kOSKextLogLoadFlag,
"-- new load size: 0x%.8zx, new kmod_info.size: 0x%.8x",
aKext->loadInfo->linkInfo.linkedKextSize, (unsigned int) *size_field);
#endif // KMOD_INFO_DEBUG
}
}
if (!aKext->loadInfo->prelinkedExecutable) {
aKext->loadInfo->prelinkedExecutable = CFRetain(relocData);
}
}
result = true;
finish:
if (kextExecutable) {
(void)posix_madvise((void *)CFDataGetBytePtr(kextExecutable),
CFDataGetLength(kextExecutable),
POSIX_MADV_DONTNEED);
}
SAFE_RELEASE(kextExecutable);
SAFE_RELEASE(dependencies);
SAFE_RELEASE(indirectDependencies);
SAFE_RELEASE(relocData);
SAFE_FREE(kxldDependencies);
SAFE_FREE(bundleIDCString);
return result;
}
Boolean __OSKextExtractDebugSymbols(
OSKextRef aKext,
CFMutableDictionaryRef symbols)
{
Boolean result = false;
CFStringRef relocName = NULL;
if (!OSKextDeclaresExecutable(aKext) || OSKextIsInterface(aKext)) {
result = true;
goto finish;
}
if (aKext->loadInfo->linkedExecutable) {
relocName = CFStringCreateWithFormat(CFGetAllocator(aKext), NULL,
CFSTR("%@.%s"),
OSKextGetIdentifier(aKext),
__kOSKextSymbolFileSuffix);
if (!relocName) {
OSKextLogMemError();
goto finish;
}
CFDictionarySetValue(symbols, relocName, aKext->loadInfo->linkedExecutable);
}
result = true;
finish:
SAFE_RELEASE(relocName);
return result;
}
Boolean __OSKextGenerateDebugSymbols(
OSKextRef aKext,
CFDataRef kernelImage,
uint64_t kernelLoadAddress,
KXLDContext * kxldContext,
CFMutableDictionaryRef symbols)
{
Boolean result = false;
CFArrayRef dependencies = NULL; CFIndex count, i;
if (__OSKextIsSplitKext(aKext)) {
goto finish;
}
if (!__OSKextCreateLoadInfo(aKext)) {
OSKextLogMemError();
goto finish;
}
if (aKext->loadInfo->linkedExecutable) {
result = true;
goto finish;
}
dependencies = OSKextCopyLinkDependencies(aKext, true);
if (!dependencies) {
result = false;
goto finish;
}
count = CFArrayGetCount(dependencies);
for (i = 0; i < count; i++) {
OSKextRef dependency = (OSKextRef)CFArrayGetValueAtIndex(
dependencies, i);
result = __OSKextGenerateDebugSymbols(dependency, kernelImage,
kernelLoadAddress, kxldContext, symbols);
if (!result) {
goto finish;
}
}
result = __OSKextPerformLink(aKext, kernelImage, kernelLoadAddress,
false , kxldContext);
if (!result) {
goto finish;
}
result = __OSKextExtractDebugSymbols(aKext, symbols);
if (!result) {
goto finish;
}
result = true;
finish:
SAFE_RELEASE(dependencies);
return result;
}
kxld_addr_t __OSKextLinkAddressCallback(
u_long size,
KXLDAllocateFlags * flags __unused,
void * user_data)
{
kxld_addr_t result = 0;
kxld_addr_t kextAddress = 0;
static kxld_addr_t loadAddressOffset = 0;
__OSKextKXLDCallbackContext * context =
(__OSKextKXLDCallbackContext *)user_data;
context->kext->loadInfo->linkInfo.linkedKextSize = size;
kextAddress = (kxld_addr_t)OSKextGetLoadAddress(context->kext);
if (kextAddress) {
result = kextAddress;
} else {
result = context->kernelLoadAddress + loadAddressOffset;
loadAddressOffset += size;
}
return result;
}
void __OSKextLoggingCallback(
KXLDLogSubsystem subsystem,
KXLDLogLevel level,
const char * format,
va_list argList,
void * user_data)
{
__OSKextKXLDCallbackContext * context =
(__OSKextKXLDCallbackContext *)user_data;
OSKextRef aKext = (context) ? context->kext : NULL;
OSKextLogSpec logSpec = 0;
switch (subsystem) {
case kKxldLogLinking:
logSpec |= kOSKextLogLinkFlag;
break;
case kKxldLogPatching:
logSpec |= kOSKextLogPatchFlag;
break;
}
switch (level) {
case kKxldLogExplicit:
logSpec |= kOSKextLogExplicitLevel;
break;
case kKxldLogErr:
logSpec |= kOSKextLogErrorLevel;
break;
case kKxldLogWarn:
logSpec |= kOSKextLogWarningLevel;
break;
case kKxldLogBasic:
logSpec |= kOSKextLogProgressLevel;
break;
case kKxldLogDetail:
logSpec |= kOSKextLogDetailLevel;
break;
case kKxldLogDebug:
logSpec |= kOSKextLogDebugLevel;
break;
}
OSKextVLog(aKext, logSpec, format, argList);
}
static uint64_t __OSKextSetupCrossLinkByArch(cpu_type_t cputype)
{
uint64_t kxldPageSize = 0;
#if defined(CPU_TYPE_ARM64)
const cpu_type_t arm64_cputype = CPU_TYPE_ARM64;
#else
const cpu_type_t arm64_cputype = CPU_TYPE_ARM | CPU_ARCH_ABI64;
#endif
if (cputype == arm64_cputype) {
const vm_size_t crossLinkPageSize = 0x4000;
if (!setCrossLinkPageSize(crossLinkPageSize)) {
OSKextLog( NULL, kOSKextLogErrorLevel | kOSKextLogLinkFlag,
"Can't setup cross linking page size = 0x%lx.",
(long) crossLinkPageSize);
}
}
if (isCrossLinking()) {
kxldPageSize = getEffectivePageSize();
} else {
kxldPageSize = 0;
}
return kxldPageSize;
}
CFDictionaryRef OSKextGenerateDebugSymbols(
OSKextRef aKext,
CFDataRef kernelImage)
{
CFMutableDictionaryRef result = NULL;
CFDataRef kernelImageCopy = NULL;
KXLDContext * kxldContext = NULL;
KXLDFlags kxldFlags = 0;
kern_return_t kxldResult = 0;
uint64_t kernelLoadAddress = 0;
macho_seek_result machoResult;
const UInt8 * kernelStart;
const UInt8 * kernelEnd;
fat_iterator fatIterator = NULL; struct mach_header_64 * machHeader = NULL; uint64_t kxldPageSize = 0;
if (!kernelImage) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogLinkFlag,
"Can't generate debug symbols; no kernel file provided ");
goto finish;
}
if (!OSKextResolveDependencies(aKext)) {
goto finish;
}
result = CFDictionaryCreateMutable(CFGetAllocator(aKext), 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (!result) {
OSKextLogMemError();
goto finish;
}
kernelStart = CFDataGetBytePtr(kernelImage);
kernelEnd = kernelStart + CFDataGetLength(kernelImage) - 1;
fatIterator = fat_iterator_for_data(kernelStart, kernelEnd,
1 );
if (!fatIterator) {
OSKextLog( NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Can't read kernel file.");
goto finish;
}
machHeader = (struct mach_header_64 *)
fat_iterator_find_arch(fatIterator,
OSKextGetArchitecture()->cputype,
OSKextGetArchitecture()->cpusubtype,
NULL);
if (!machHeader) {
OSKextLog( NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Can't find architecture %s in kernel file.",
OSKextGetArchitecture()->name);
goto finish;
}
machoResult = macho_find_dysymtab(machHeader, kernelEnd, NULL);
if (machoResult == macho_seek_result_found) {
kxldFlags |= kKXLDFlagIncludeRelocs;
}
else {
OSKextLog(NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"kernel does NOT support KASLR");
}
kxldPageSize = __OSKextSetupCrossLinkByArch(OSKextGetArchitecture()->cputype);
kxldResult = kxld_create_context(&kxldContext, __OSKextLinkAddressCallback,
__OSKextLoggingCallback, kxldFlags, OSKextGetArchitecture()->cputype,
OSKextGetArchitecture()->cpusubtype, kxldPageSize);
if (kxldResult != KERN_SUCCESS) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogLinkFlag,
"Can't create link context.");
goto finish;
}
kernelLoadAddress = __OSKextGetFakeLoadAddress(kernelImage);
if (!kernelLoadAddress) {
goto finish;
}
if (!__OSKextGenerateDebugSymbols(aKext, kernelImage,
kernelLoadAddress, kxldContext, result))
{
SAFE_RELEASE_NULL(result);
goto finish;
}
finish:
SAFE_RELEASE(kernelImageCopy);
if (kxldContext) kxld_destroy_context(kxldContext);
if (fatIterator) fat_iterator_close(fatIterator);
return result;
}
Boolean OSKextNeedsLoadAddressForDebugSymbols(OSKextRef aKext)
{
Boolean result = false;
if (!OSKextIsKernelComponent(aKext) &&
!OSKextIsInterface(aKext) &&
!OSKextGetLoadAddress(aKext) &&
OSKextDeclaresExecutable(aKext)) {
result = true;
}
return result;
}
#endif
OSReturn __OSKextUnload(
OSKextRef aKext,
CFStringRef kextIdentifier,
Boolean terminateServiceAndRemovePersonalities)
{
OSReturn result = kOSReturnError;
char * kextIDCString = NULL; CFDictionaryRef kextRequest = NULL; CFMutableDictionaryRef requestArgs = NULL; char * termMessage =
" (with termnation of IOServices)";
char kextPath[PATH_MAX];
if (aKext) {
kextIdentifier = OSKextGetIdentifier(aKext);
__OSKextGetFileSystemPath(aKext, NULL,
false, kextPath);
} else {
kextIDCString = createUTF8CStringForCFString(kextIdentifier);
}
OSKextLog(aKext,
kOSKextLogProgressLevel |
kOSKextLogIPCFlag | kOSKextLogLoadFlag,
"Requesting unload of %s%s.",
aKext ? kextPath : kextIDCString,
terminateServiceAndRemovePersonalities ? termMessage : "");
kextRequest = __OSKextCreateKextRequest(
CFSTR(kKextRequestPredicateUnload),
kextIdentifier, &requestArgs);
if (!kextRequest || !requestArgs) {
goto finish;
}
if (terminateServiceAndRemovePersonalities) {
CFDictionarySetValue(requestArgs,
CFSTR(kKextRequestArgumentTerminateIOServicesKey),
kCFBooleanTrue);
}
result = __OSKextSendKextRequest(aKext, kextRequest,
NULL,
NULL, NULL);
if (result != kOSReturnSuccess) {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Failed to unload %s - %s.",
aKext ? kextPath : kextIDCString,
safe_mach_error_string(result));
goto finish;
} else {
OSKextLog(aKext,
kOSKextLogProgressLevel |
kOSKextLogIPCFlag | kOSKextLogLoadFlag,
"Successfully unloaded %s.",
aKext ? kextPath : kextIDCString);
}
finish:
SAFE_FREE(kextIDCString);
SAFE_RELEASE(kextRequest);
return result;
}
OSReturn OSKextUnload(
OSKextRef aKext,
Boolean terminateServiceAndRemovePersonalities)
{
return __OSKextUnload(aKext, NULL,
terminateServiceAndRemovePersonalities);
}
OSReturn OSKextUnloadKextWithIdentifier(
CFStringRef kextIdentifier,
Boolean terminateServiceAndRemovePersonalities)
{
return __OSKextUnload( NULL, kextIdentifier,
terminateServiceAndRemovePersonalities);
}
Boolean OSKextIsStarted(OSKextRef aKext)
{
Boolean result = false;
if (!aKext->loadInfo) {
goto finish;
}
if (aKext->loadInfo->kernelLoadInfo) {
__OSKextCheckLoaded(aKext);
}
if (aKext->loadInfo->flags.isStarted) {
result = true;
}
finish:
return result;
}
kern_return_t OSKextStart(OSKextRef aKext)
{
OSReturn result = kOSReturnError;
char kextPath[PATH_MAX];
__OSKextGetFileSystemPath(aKext, NULL,
false, kextPath);
OSKextLog(aKext,
kOSKextLogProgressLevel |
kOSKextLogIPCFlag | kOSKextLogLoadFlag,
"Requesting start of %s.", kextPath);
result = __OSKextSimpleKextRequest(aKext,
CFSTR(kKextRequestPredicateStart), NULL);
if (result == kOSReturnSuccess) {
OSKextLog(aKext, kOSKextLogProgressLevel |
kOSKextLogIPCFlag | kOSKextLogLoadFlag,
"Started %s.", kextPath);
} else {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"Failed to start %s - %s.", kextPath,
safe_mach_error_string(result));
}
return result;
}
kern_return_t OSKextStop(OSKextRef aKext)
{
OSReturn result = kOSReturnError;
char kextPath[PATH_MAX];
__OSKextGetFileSystemPath(aKext, NULL,
false, kextPath);
OSKextLog(aKext,
kOSKextLogProgressLevel |
kOSKextLogIPCFlag | kOSKextLogLoadFlag,
"Requesting stop of %s.", kextPath);
result = __OSKextSimpleKextRequest(aKext, CFSTR(kKextRequestPredicateStop),
NULL);
if (result == kOSReturnSuccess) {
OSKextLog(aKext,
kOSKextLogProgressLevel |
kOSKextLogIPCFlag | kOSKextLogLoadFlag,
"Successfully stopped %s.", kextPath);
} else {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"Failed to stop %s - %s.", kextPath,
safe_mach_error_string(result));
}
return result;
}
OSReturn
OSKextSendPersonalitiesToKernel(CFArrayRef personalities,
Boolean resetFlag)
{
OSReturn result = kOSReturnError;
CFDataRef serializedPersonalities = NULL; void * dataPtr;
CFIndex dataLength = 0;
uint32_t sendDataFlag = resetFlag ? kIOCatalogResetDrivers : kIOCatalogAddDrivers;
if (!personalities) {
result = kOSKextReturnInvalidArgument;
goto finish;
}
if (!CFArrayGetCount(personalities)) {
result = kOSReturnSuccess;
goto finish;
}
OSKextLog( NULL,
kOSKextLogStepLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Sending %d personalit%s to the kernel.",
(int)CFArrayGetCount(personalities),
(CFArrayGetCount(personalities) != 1) ? "ies" : "y");
serializedPersonalities = IOCFSerialize(personalities, kNilOptions);
if (!serializedPersonalities) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"Can't serialize personalities.");
result = kOSKextReturnSerialization;
goto finish;
}
dataPtr = (void *)CFDataGetBytePtr(serializedPersonalities);
dataLength = CFDataGetLength(serializedPersonalities);
result = IOCatalogueSendData(kIOMasterPortDefault,
sendDataFlag, dataPtr, dataLength);
if (result != KERN_SUCCESS) {
OSKextLog( NULL,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Failed to send personalities to the kernel.");
goto finish;
}
finish:
SAFE_RELEASE(serializedPersonalities);
return result;
}
OSReturn OSKextSendKextPersonalitiesToKernel(
OSKextRef aKext,
CFArrayRef personalityNames)
{
OSReturn result = kOSReturnSuccess;
CFArrayRef personalities = NULL; CFMutableArrayRef mutablePersonalities = NULL; char kextPath[PATH_MAX];
char * nameCString = NULL; CFIndex count, i;
__OSKextGetFileSystemPath(aKext, NULL,
false, kextPath);
count = 0;
if (personalityNames) {
count = CFArrayGetCount(personalityNames);
}
if (!count) {
personalities = OSKextCopyPersonalitiesArray(aKext);
if (personalities && CFArrayGetCount(personalities)) {
OSKextLog(aKext, kOSKextLogStepLevel | kOSKextLogLoadFlag,
"Sending all personalties for %s to the kernel.",
kextPath);
} else {
OSKextLog(aKext, kOSKextLogStepLevel | kOSKextLogLoadFlag,
"%s has no personalities to send to kernel.",
kextPath);
}
} else {
CFDictionaryRef allPersonalities = OSKextGetValueForInfoDictionaryKey(
aKext, CFSTR(kIOKitPersonalitiesKey));
if (!allPersonalities && !CFDictionaryGetCount(allPersonalities)) {
OSKextLog(aKext, kOSKextLogStepLevel | kOSKextLogLoadFlag,
"%s has no personalities to send to kernel.",
kextPath);
goto finish;
}
mutablePersonalities = CFArrayCreateMutable(CFGetAllocator(aKext),
0, &kCFTypeArrayCallBacks);
if (!mutablePersonalities) {
OSKextLogMemError();
goto finish;
}
personalities = mutablePersonalities;
OSKextLog(aKext,
kOSKextLogDetailLevel |
kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Sending named personalities of %s to the kernel:",
kextPath);
for (i = 0; i < count; i++) {
CFStringRef name = CFArrayGetValueAtIndex(personalityNames, i);
CFDictionaryRef personality = CFDictionaryGetValue(allPersonalities,
name);
SAFE_FREE_NULL(nameCString);
nameCString = createUTF8CStringForCFString(name);
if (!nameCString) {
OSKextLogMemError();
goto finish;
}
if (!personality) {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Personality %s not found in %s.",
nameCString, kextPath);
result = kOSKextReturnInvalidArgument;
goto finish;
}
OSKextLog(aKext,
kOSKextLogDetailLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
" %s", nameCString);
CFArrayAppendValue(mutablePersonalities, personality);
}
}
if (personalities && CFArrayGetCount(personalities)) {
OSReturn sendResult = OSKextSendPersonalitiesToKernel(personalities,
FALSE);
if (sendResult != kOSReturnSuccess) {
result = sendResult;
}
}
finish:
SAFE_FREE(nameCString);
SAFE_RELEASE(personalities);
return result;
}
OSReturn OSKextSendPersonalitiesOfKextsToKernel(CFArrayRef kextArray,
Boolean resetFlag)
{
OSReturn result = kOSReturnSuccess;
CFArrayRef personalities = NULL; CFIndex count;
count = CFArrayGetCount(kextArray);
if (!count) {
goto finish;
}
personalities = OSKextCopyPersonalitiesOfKexts(kextArray);
if (!personalities || !CFArrayGetCount(personalities)) {
goto finish;
}
result = OSKextSendPersonalitiesToKernel(personalities, resetFlag);
finish:
SAFE_RELEASE(personalities);
return result;
}
OSReturn __OSKextRemovePersonalities(
OSKextRef aKext,
CFStringRef aBundleID)
{
OSReturn result = kOSReturnError;
kern_return_t iocatalogueResult = KERN_SUCCESS;
CFDictionaryRef personality = NULL; CFDataRef data = NULL; void * dataPointer = NULL; CFIndex dataLength = 0;
char kextPath[PATH_MAX];
personality = CFDictionaryCreate(CFGetAllocator(aKext),
(const void **)&kCFBundleIdentifierKey,
(const void **)&aBundleID, 1,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (!personality) {
OSKextLogMemError();
goto finish;
}
__OSKextGetFileSystemPath(aKext, NULL,
FALSE, kextPath);
data = IOCFSerialize(personality, kNilOptions);
if (!data) {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Can't serialize personalities for %s.",
kextPath);
goto finish;
}
dataPointer = (void *)CFDataGetBytePtr(data);
dataLength = CFDataGetLength(data);
iocatalogueResult = IOCatalogueSendData(kIOMasterPortDefault,
kIOCatalogRemoveDrivers,
dataPointer, dataLength);
if (iocatalogueResult != KERN_SUCCESS) {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Failed to remove personalities of %s from IOCatalogue - %s.",
kextPath,
safe_mach_error_string(iocatalogueResult));
goto finish;
}
result = kOSReturnSuccess;
finish:
SAFE_RELEASE(data);
SAFE_RELEASE(personality);
return result;
}
OSReturn OSKextRemoveKextPersonalitiesFromKernel(OSKextRef aKext)
{
return __OSKextRemovePersonalities(aKext, OSKextGetIdentifier(aKext));
}
OSReturn OSKextRemovePersonalitiesForIdentifierFromKernel(
CFStringRef aBundleID)
{
return __OSKextRemovePersonalities( NULL, aBundleID);
}
void __OSKextProcessLoadInfo(
const void * vKey __unused,
const void * vValue,
void * vContext __unused)
{
CFDictionaryRef loadInfoDict = (CFDictionaryRef)vValue;
CFStringRef bundleID = NULL; char * bundleIDCString = NULL; CFArrayRef matchingKexts = NULL; OSKextRef aKext = NULL; Boolean foundOne = FALSE;
OSKextVersion loadedVersion = -1;
CFStringRef scratchString = NULL;
char kextPath[PATH_MAX];
char kextVersBuffer[kOSKextVersionMaxLength];
char loadedVersBuffer[kOSKextVersionMaxLength] = __kStringUnknown;
CFIndex count, i;
bundleID = CFDictionaryGetValue(loadInfoDict, kCFBundleIdentifierKey);
if (!bundleID) {
goto finish;
}
matchingKexts = OSKextCopyKextsWithIdentifier(bundleID);
if (!matchingKexts) {
goto finish;
}
bundleIDCString = createUTF8CStringForCFString(bundleID);
scratchString = CFDictionaryGetValue(loadInfoDict, kCFBundleVersionKey);
if (scratchString) {
loadedVersion = OSKextParseVersionCFString(scratchString);
OSKextVersionGetString(loadedVersion,
loadedVersBuffer, sizeof(loadedVersBuffer));
} else {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Kernel load info for %s lacks a CFBundleVersion.",
bundleIDCString);
goto finish;
}
count = CFArrayGetCount(matchingKexts);
for (i = 0; i < count; i++) {
aKext = (OSKextRef)CFArrayGetValueAtIndex(matchingKexts, i);
if (!__OSKextCreateLoadInfo(aKext)) {
OSKextLogMemError();
goto finish;
}
__OSKextGetFileSystemPath(aKext, NULL,
false, kextPath);
OSKextVersionGetString(OSKextGetVersion(aKext),
kextVersBuffer, sizeof(kextVersBuffer));
if (loadedVersion != aKext->version) {
aKext->loadInfo->flags.otherCFBundleVersionIsLoaded = 1;
continue;
}
SAFE_RELEASE_NULL(aKext->loadInfo->kernelLoadInfo);
aKext->loadInfo->kernelLoadInfo = CFRetain(loadInfoDict);
foundOne = TRUE;
}
finish:
if (!foundOne && !CFEqual(bundleID, CFSTR(kOSKextKernelIdentifier))) {
OSKextLog(aKext,
kOSKextLogDetailLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"For loaded kext %s, v%s: no opened kext matches.",
bundleIDCString, loadedVersBuffer);
}
SAFE_FREE(bundleIDCString);
SAFE_RELEASE(matchingKexts);
return;
}
static void __OSKextCheckLoaded(OSKextRef aKext)
{
CFDataRef kextUUID = NULL; CFDataRef loadedUUID = NULL; uuid_string_t kextUUIDCString = "";
uuid_string_t loadedUUIDCString = "";
CFNumberRef scratchNumber = NULL; CFBooleanRef scratchBool = NULL; char kextPath[PATH_MAX];
char kextVersBuffer[kOSKextVersionMaxLength];
if (!aKext->loadInfo || !aKext->loadInfo->kernelLoadInfo) {
goto finish;
}
__OSKextGetFileSystemPath(aKext, NULL,
false, kextPath);
loadedUUID = CFDictionaryGetValue(aKext->loadInfo->kernelLoadInfo,
CFSTR(kOSBundleUUIDKey));
kextUUID = OSKextCopyUUIDForArchitecture(aKext,
OSKextGetRunningKernelArchitecture());
if (loadedUUID) {
uuid_unparse(CFDataGetBytePtr(loadedUUID), loadedUUIDCString);
}
if (kextUUID) {
uuid_unparse(CFDataGetBytePtr(kextUUID), kextUUIDCString);
}
OSKextVersionGetString(OSKextGetVersion(aKext), kextVersBuffer,
sizeof(kextVersBuffer));
if ( (loadedUUID && kextUUID && CFEqual(loadedUUID, kextUUID)) ||
(!loadedUUID && !kextUUID) ) {
aKext->loadInfo->flags.isLoaded = 1;
OSKextLog(aKext,
kOSKextLogDebugLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"%s (version %s%s%s) is loaded.",
kextPath, kextVersBuffer,
kextUUIDCString[0] ? ", UUID " : ", no UUID",
kextUUIDCString);
} else {
aKext->loadInfo->flags.otherUUIDIsLoaded = 1;
OSKextLog(aKext,
kOSKextLogDebugLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"%s (version %s%s%s): same version, different UUID (%s) is loaded.",
kextPath, kextVersBuffer,
kextUUIDCString[0] ? ", UUID " : ", no UUID",
kextUUIDCString,
loadedUUIDCString[0] ? loadedUUIDCString : "none");
goto finish;
}
scratchBool = CFDictionaryGetValue(aKext->loadInfo->kernelLoadInfo,
CFSTR(kOSBundleStartedKey));
if (scratchBool && CFBooleanGetValue(scratchBool)) {
aKext->loadInfo->flags.isStarted = 1;
OSKextLog(aKext,
kOSKextLogDebugLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"%s (version %s): is%s started.",
kextPath, kextVersBuffer,
aKext->loadInfo->flags.isStarted ? "" : " not");
}
scratchNumber = CFDictionaryGetValue(aKext->loadInfo->kernelLoadInfo,
CFSTR(kOSBundleLoadTagKey));
if (scratchNumber) {
uint32_t loadTag;
if (CFNumberGetValue(scratchNumber, kCFNumberSInt32Type, &loadTag)) {
aKext->loadInfo->loadTag = loadTag;
} else {
}
}
scratchNumber = CFDictionaryGetValue(aKext->loadInfo->kernelLoadInfo,
CFSTR(kOSBundleLoadAddressKey));
if (scratchNumber) {
uint64_t loadAddress;
if (CFNumberGetValue(scratchNumber, kCFNumberSInt64Type,
&loadAddress)) {
__OSKextSetLoadAddress(aKext, loadAddress);
} else {
}
}
scratchNumber = CFDictionaryGetValue(aKext->loadInfo->kernelLoadInfo,
CFSTR(kOSBundleLoadSizeKey));
if (scratchNumber) {
uint32_t loadSize;
if (CFNumberGetValue(scratchNumber, kCFNumberSInt32Type,
&loadSize)) {
aKext->loadInfo->linkInfo.linkedKextSize = loadSize;
} else {
}
}
finish:
if (aKext->loadInfo) {
SAFE_RELEASE_NULL(aKext->loadInfo->kernelLoadInfo);
}
SAFE_RELEASE(kextUUID);
return;
}
OSReturn OSKextReadLoadedKextInfo(
CFArrayRef kextIdentifiers,
Boolean flushDependenciesFlag)
{
OSReturn result = kOSReturnError;
CFArrayRef kexts = NULL; CFDictionaryRef allLoadInfo = NULL; const NXArchInfo * currentArch = NULL; const NXArchInfo * kernelArch = NULL; CFIndex count, i;
pthread_once(&__sOSKextInitialized, __OSKextInitialize);
currentArch = OSKextGetArchitecture();
kernelArch = OSKextGetRunningKernelArchitecture();
if (currentArch != kernelArch) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Can't read loaded kext info - current architecture %s != kernel's architecture %s.",
currentArch->name, kernelArch->name);
result = kOSKextReturnArchNotFound;
goto finish;
}
if (!kextIdentifiers) {
OSKextFlushLoadInfo( NULL, flushDependenciesFlag);
} else {
kexts = OSKextCopyKextsWithIdentifiers(kextIdentifiers);
if (!kexts) {
goto finish;
}
count = CFArrayGetCount(kexts);
for (i = 0; i < count; i++) {
OSKextRef thisKext = (OSKextRef) CFArrayGetValueAtIndex(kexts, i);
OSKextFlushLoadInfo(thisKext, flushDependenciesFlag);
}
}
if (kextIdentifiers && CFArrayGetCount(kextIdentifiers)) {
CFIndex numIdentifiers = CFArrayGetCount(kextIdentifiers);
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Reading load info for %u kext%s.",
(uint32_t)numIdentifiers,
numIdentifiers == 1 ? "" : "s");
} else {
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Reading load info for all kexts.");
}
allLoadInfo = OSKextCopyLoadedKextInfo(kextIdentifiers, __sOSKextInfoEssentialKeys);
if (!allLoadInfo) {
goto finish;
}
CFDictionaryApplyFunction(allLoadInfo, __OSKextProcessLoadInfo,
NULL);
result = kOSReturnSuccess;
finish:
SAFE_RELEASE(kexts);
SAFE_RELEASE(allLoadInfo);
return result;
}
Boolean OSKextIsLoaded(OSKextRef aKext)
{
Boolean result = false;
if (!aKext->loadInfo) {
goto finish;
}
if (aKext->loadInfo->kernelLoadInfo) {
__OSKextCheckLoaded(aKext);
}
if (aKext->loadInfo->flags.isLoaded) {
result = true;
}
finish:
return result;
}
uint64_t OSKextGetLoadAddress(OSKextRef aKext)
{
uint64_t result = 0x0;
if (!aKext->loadInfo) {
goto finish;
}
if (aKext->loadInfo->kernelLoadInfo) {
__OSKextCheckLoaded(aKext);
}
result = aKext->loadInfo->linkInfo.vmaddr_TEXT;
finish:
return result;
}
Boolean __OSKextSetLoadAddress(OSKextRef aKext, uint64_t address)
{
Boolean result = false;
char kextPath[PATH_MAX];
if (!__OSKextCreateLoadInfo(aKext)) {
goto finish;
}
__OSKextGetFileSystemPath(aKext, NULL,
true,
kextPath);
if (!__OSKextIsArchitectureLP64() && address > UINT32_MAX) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogLoadFlag,
"Attempt to set 64-bit load address - %s.",
kextPath);
goto finish;
}
if (__OSKextIsArchitectureLP64()) {
OSKextLog(aKext, kOSKextLogDebugLevel | kOSKextLogLinkFlag | kOSKextLogLoadFlag,
"setting load address of %s to 0x%0llx",
kextPath, (unsigned long long)address);
} else {
OSKextLog(aKext, kOSKextLogDebugLevel | kOSKextLogLinkFlag | kOSKextLogLoadFlag,
"setting load address of %s to 0x%0x",
kextPath, (int)address);
}
aKext->loadInfo->linkInfo.vmaddr_TEXT = address;
result = true;
finish:
return result;
}
Boolean OSKextSetLoadAddress(OSKextRef aKext, uint64_t address)
{
Boolean result = false;
if (!__OSKextCreateLoadInfo(aKext)) {
goto finish;
}
if (aKext->loadInfo->kernelLoadInfo) {
__OSKextCheckLoaded(aKext);
}
result = __OSKextSetLoadAddress(aKext, address);
finish:
return result;
}
Boolean OSKextOtherVersionIsLoaded(OSKextRef aKext, Boolean * uuidFlag)
{
Boolean result = false;
if (!aKext->loadInfo) {
goto finish;
}
if (aKext->loadInfo->kernelLoadInfo) {
__OSKextCheckLoaded(aKext);
}
if (aKext->loadInfo->flags.otherCFBundleVersionIsLoaded ||
aKext->loadInfo->flags.otherUUIDIsLoaded) {
result = true;
}
if (uuidFlag) {
*uuidFlag = aKext->loadInfo->flags.otherUUIDIsLoaded ? true : false;
}
finish:
return result;
}
uint32_t OSKextGetLoadTag(OSKextRef aKext)
{
uint32_t result = 0;
if (!aKext->loadInfo) {
goto finish;
}
if (aKext->loadInfo->kernelLoadInfo) {
__OSKextCheckLoaded(aKext);
}
result = aKext->loadInfo->loadTag;
finish:
return result;
}
static void __OSKextFlushLoadInfoApplierFunction(
const void * vKey __unused,
const void * vValue,
void * vContext)
{
OSKextRef aKext = (OSKextRef)vValue;
Boolean flushDependenciesFlag = *(Boolean *)vContext;
OSKextFlushLoadInfo(aKext, flushDependenciesFlag);
return;
}
void OSKextFlushLoadInfo(
OSKextRef aKext,
Boolean flushDependenciesFlag)
{
static Boolean flushingAll = false;
char kextPath[PATH_MAX];
pthread_once(&__sOSKextInitialized, __OSKextInitialize);
if (aKext) {
if (OSKextGetURL(aKext)) {
__OSKextGetFileSystemPath(aKext, NULL,
false, kextPath);
}
if (aKext->loadInfo) {
if (!flushingAll) {
OSKextLog(aKext,
kOSKextLogStepLevel | kOSKextLogKextBookkeepingFlag,
"Flushing load info for %s (%s dependencies)",
kextPath,
flushDependenciesFlag ? "with" : "keeping");
}
SAFE_RELEASE_NULL(aKext->loadInfo->kernelLoadInfo);
SAFE_RELEASE_NULL(aKext->loadInfo->executable);
SAFE_RELEASE_NULL(aKext->loadInfo->linkedExecutable);
SAFE_RELEASE_NULL(aKext->loadInfo->prelinkedExecutable);
if (flushDependenciesFlag) {
OSKextFlushDependencies(aKext);
}
SAFE_FREE_NULL(aKext->loadInfo);
if (!aKext->flags.sip_protected) {
aKext->flags.valid = 0;
aKext->flags.invalid = 0;
aKext->flags.validated = 0;
aKext->flags.authentic = 0;
aKext->flags.inauthentic = 0;
aKext->flags.authenticated = 0;
aKext->flags.isSigned = 0;
}
}
} else if (__sOSKextsByURL) {
flushingAll = true;
OSKextLog( NULL,
kOSKextLogStepLevel | kOSKextLogKextBookkeepingFlag,
"Flushing load info for all kexts (%s dependencies)",
flushDependenciesFlag ? "with" : "keeping");
CFDictionaryApplyFunction(__sOSKextsByURL,
__OSKextFlushLoadInfoApplierFunction,
&flushDependenciesFlag);
flushingAll = false;
}
return;
}
CFArrayRef _OSKextCopyKernelRequests(void)
{
CFArrayRef result = NULL;
OSReturn op_result = kOSReturnError;
CFMutableDictionaryRef requestDict = NULL;
OSKextLog( NULL,
kOSKextLogDebugLevel | kOSKextLogIPCFlag,
"Reading requests from kernel.");
requestDict = __OSKextCreateKextRequest(
CFSTR(kKextRequestPredicateGetKernelRequests),
NULL, NULL);
op_result = __OSKextSendKextRequest( NULL, requestDict,
(CFTypeRef *)&result,
NULL, NULL);
if (op_result != kOSReturnSuccess) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Failed to read requests from kernel - %s.",
safe_mach_error_string(op_result));
SAFE_RELEASE_NULL(result);
goto finish;
}
if (!result || CFArrayGetTypeID() != CFGetTypeID(result)) {
SAFE_RELEASE_NULL(result);
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Requests from kernel missing or of wrong type.");
goto finish;
}
finish:
SAFE_RELEASE(requestDict);
return result;
}
OSReturn _OSKextSendResource(
CFDictionaryRef request,
OSReturn requestResult,
CFDataRef resource)
{
OSReturn result = kOSReturnError;
CFDictionaryRef requestArgs = NULL; CFMutableDictionaryRef response = NULL; CFMutableDictionaryRef responseArgs = NULL; CFNumberRef requestResultNum = NULL;
requestArgs = CFDictionaryGetValue(request,
CFSTR(kKextRequestArgumentsKey));
if (!requestArgs) {
result = kOSKextReturnInvalidArgument;
goto finish;
}
response = CFDictionaryCreateMutableCopy(kCFAllocatorDefault,
0, request);
if (!response) {
OSKextLogMemError();
goto finish;
}
responseArgs = CFDictionaryCreateMutableCopy(kCFAllocatorDefault,
0, requestArgs);
if (!responseArgs) {
OSKextLogMemError();
goto finish;
}
CFDictionarySetValue(response, CFSTR(kKextRequestPredicateKey),
CFSTR(kKextRequestPredicateSendResource));
CFDictionarySetValue(response, CFSTR(kKextRequestArgumentsKey),
responseArgs);
if (resource) {
CFDictionarySetValue(responseArgs, CFSTR(kKextRequestArgumentValueKey),
resource);
}
requestResultNum = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type,
(SInt32 *)&requestResult);
if (requestResultNum) {
CFDictionarySetValue(responseArgs, CFSTR(kKextRequestArgumentResultKey),
requestResultNum);
}
result = __OSKextSendKextRequest( NULL, response,
NULL,
NULL, NULL);
finish:
SAFE_RELEASE(requestResultNum);
SAFE_RELEASE(responseArgs);
SAFE_RELEASE(response);
return result;
}
CFArrayRef OSKextCreateLoadedKextInfo(
CFArrayRef kextIdentifiers)
{
CFArrayRef result = NULL;
CFDictionaryRef allLoadInfoDict = NULL; CFDictionaryRef * loadInfoItems = NULL; CFIndex count;
allLoadInfoDict = OSKextCopyLoadedKextInfo(kextIdentifiers, NULL );
if (!allLoadInfoDict || CFDictionaryGetTypeID() != CFGetTypeID(allLoadInfoDict)) {
goto finish;
}
count = CFDictionaryGetCount(allLoadInfoDict);
loadInfoItems = malloc(count * sizeof(CFDictionaryRef));;
if (!loadInfoItems) {
OSKextLogMemError();
goto finish;
}
CFDictionaryGetKeysAndValues(allLoadInfoDict, NULL,
(const void **)loadInfoItems);
result = CFArrayCreate(kCFAllocatorDefault,
(const void **)loadInfoItems, count, &kCFTypeArrayCallBacks);
finish:
SAFE_RELEASE(allLoadInfoDict);
return result;
}
CFDictionaryRef OSKextCopyLoadedKextInfoByUUID(
CFArrayRef kextIdentifiers,
CFArrayRef infoKeys)
{
CFDictionaryRef result = NULL;
OSReturn op_result = kOSReturnError;
CFMutableDictionaryRef requestDict = NULL; CFMutableDictionaryRef requestArgs = NULL; CFStringRef infoString = NULL; char * infoCString = NULL;
OSKextLog( NULL,
kOSKextLogStepLevel | kOSKextLogIPCFlag,
"Reading loaded kext info from kernel.");
requestDict = __OSKextCreateKextRequest(CFSTR(kKextRequestPredicateGetLoadedByUUID),
kextIdentifiers, &requestArgs);
if (!requestDict) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Failed to create kext request.");
goto finish;
}
if (infoKeys && CFArrayGetCount(infoKeys)) {
CFDictionarySetValue(requestArgs,
CFSTR(kKextRequestArgumentInfoKeysKey),
infoKeys);
}
op_result = __OSKextSendKextRequest( NULL, requestDict,
(CFTypeRef *)&result,
NULL, NULL);
if (op_result != kOSReturnSuccess) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Failed to read loaded kext info from kernel - %s.",
safe_mach_error_string(op_result));
SAFE_RELEASE_NULL(result);
goto finish;
}
if (!result) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Kernel request call returned no data.");
goto finish;
}
if (CFDictionaryGetTypeID() != CFGetTypeID(result)) {
SAFE_RELEASE_NULL(result);
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Loaded kext info from kernel is wrong type.");
goto finish;
}
if (__OSKextShouldLog( NULL,
kOSKextLogDebugLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag)) {
infoString = createCFStringForPlist_new(result,
kPListStyleClassic);
infoCString = createUTF8CStringForCFString(infoString);
OSKextLog( NULL,
kOSKextLogDebugLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Loaded kext info:\n%s", infoCString);
}
finish:
SAFE_RELEASE(requestDict);
SAFE_RELEASE(infoString);
SAFE_FREE(infoCString);
return result;
}
#ifndef kKextRequestPredicateGetUUIDByAddress
#define kKextRequestPredicateGetUUIDByAddress "Get Kext UUID by Address"
#endif
#ifndef kKextRequestArgumentLookupAddressKey
#define kKextRequestArgumentLookupAddressKey "Kext Request Lookup Address"
#endif
CF_EXPORT CFDataRef
OSKextCopyUUIDForAddress(
uint64_t address)
{
CFDataRef result = NULL;
OSReturn op_result = kOSReturnError;
CFMutableDictionaryRef requestDict = NULL; CFMutableDictionaryRef requestArgs = NULL; CFNumberRef lookupNum = NULL;
OSKextLog( NULL,
kOSKextLogStepLevel | kOSKextLogIPCFlag,
"Reading kext UUID for Address\n.");
requestDict = __OSKextCreateKextRequest(CFSTR(kKextRequestPredicateGetUUIDByAddress),
NULL, &requestArgs);
if (!requestDict) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Failed to create kext request.");
goto finish;
}
lookupNum = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &address);
if (!lookupNum) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Failed to create lookup address object.");
goto finish;
}
CFDictionarySetValue(requestArgs,
CFSTR(kKextRequestArgumentLookupAddressKey),
lookupNum);
op_result = __OSKextSendKextRequest( NULL, requestDict,
(CFTypeRef *) &result,
NULL, NULL);
if (op_result != kOSReturnSuccess) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Failed to lookup uuid with address from kernel - %s.",
safe_mach_error_string(op_result));
SAFE_RELEASE_NULL(result);
goto finish;
}
if (!result) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Kernel request call returned no data.");
goto finish;
}
finish:
SAFE_RELEASE(requestDict);
SAFE_RELEASE(lookupNum);
return result;
}
CFDictionaryRef OSKextCopyLoadedKextInfo(
CFArrayRef kextIdentifiers,
CFArrayRef infoKeys)
{
CFDictionaryRef result = NULL;
OSReturn op_result = kOSReturnError;
CFMutableDictionaryRef requestDict = NULL; CFMutableDictionaryRef requestArgs = NULL; CFStringRef infoString = NULL; char * infoCString = NULL;
OSKextLog( NULL,
kOSKextLogStepLevel | kOSKextLogIPCFlag,
"Reading loaded kext info from kernel.");
requestDict = __OSKextCreateKextRequest(CFSTR(kKextRequestPredicateGetLoaded),
kextIdentifiers, &requestArgs);
if (infoKeys && CFArrayGetCount(infoKeys)) {
CFDictionarySetValue(requestArgs,
CFSTR(kKextRequestArgumentInfoKeysKey),
infoKeys);
}
op_result = __OSKextSendKextRequest( NULL, requestDict,
(CFTypeRef *)&result,
NULL, NULL);
if (op_result != kOSReturnSuccess) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Failed to read loaded kext info from kernel - %s.",
safe_mach_error_string(op_result));
SAFE_RELEASE_NULL(result);
goto finish;
}
if (!result) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Kernel request call returned no data.");
goto finish;
}
if (CFDictionaryGetTypeID() != CFGetTypeID(result)) {
SAFE_RELEASE_NULL(result);
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Loaded kext info from kernel is wrong type.");
goto finish;
}
if (__OSKextShouldLog( NULL,
kOSKextLogDebugLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag)) {
infoString = createCFStringForPlist_new(result,
kPListStyleClassic);
infoCString = createUTF8CStringForCFString(infoString);
OSKextLog( NULL,
kOSKextLogDebugLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Loaded kext info:\n%s", infoCString);
}
finish:
SAFE_RELEASE(requestDict);
SAFE_RELEASE(infoString);
SAFE_FREE(infoCString);
return result;
}
Boolean __OSKextCreateLoadInfo(OSKextRef aKext)
{
Boolean result = false;
if (!aKext->loadInfo) {
aKext->loadInfo = (__OSKextLoadInfo *)
malloc(sizeof(*(aKext->loadInfo)));
if (!aKext->loadInfo) {
OSKextLogMemError();
goto finish;
}
memset(aKext->loadInfo, 0, sizeof(*(aKext->loadInfo)));
}
result = true;
finish:
return result;
}
Boolean __OSKextCreateMkextInfo(OSKextRef aKext)
{
Boolean result = false;
if (!aKext->mkextInfo) {
aKext->mkextInfo = (__OSKextMkextInfo *)
malloc(sizeof(*(aKext->mkextInfo)));
if (!aKext->mkextInfo) {
OSKextLogMemError();
goto finish;
}
memset(aKext->mkextInfo, 0, sizeof(*(aKext->mkextInfo)));
}
result = true;
finish:
return result;
}
#pragma mark Sanity Checking and Diagnostics
typedef struct {
OSKextRef kext;
CFDictionaryRef libraries;
CFMutableArrayRef propPath;
Boolean valid;
Boolean hasKernelStyleDependency;
Boolean hasKPIStyleDependency;
} __OSKextValidateOSBundleLibraryContext;
static void __OSKextValidateOSBundleLibraryApplierFunction(
const void * vKey,
const void * vValue,
void * vContext)
{
CFStringRef libID = (CFStringRef)vKey;
CFStringRef libVersion = (CFStringRef)vValue;
__OSKextValidateOSBundleLibraryContext * context =
(__OSKextValidateOSBundleLibraryContext *)vContext;
OSKextVersion version = -1;
CFArrayAppendValue(context->propPath, libID);
if (!__OSKextCheckProperty(context->kext,
context->libraries,
libID,
context->propPath,
CFStringGetTypeID(),
NULL,
false,
true,
true,
NULL,
NULL)) {
context->valid = false;
goto finish;
} else {
version = OSKextParseVersionCFString(libVersion);
if (version == -1) {
__OSKextAddDiagnostic(context->kext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticPropertyIsIllegalValueKey, context->propPath,
NULL);
context->valid = false;
}
}
if (CFStringHasPrefix(libID, __kOSKextKernelLibBundleID)) {
context->hasKernelStyleDependency = true;
if (version < __sOSNewKmodInfoKernelVersion) {
context->kext->flags.warnForMismatchedKmodInfo = 1;
}
} else if (CFStringHasPrefix(libID, __kOSKextKPIPrefix)) {
context->hasKPIStyleDependency = true;
if (version < __sOSNewKmodInfoKernelVersion) {
context->kext->flags.warnForMismatchedKmodInfo = 1;
}
}
finish:
CFArrayRemoveValueAtIndex(context->propPath,
CFArrayGetCount(context->propPath) - 1);
return;
}
typedef struct {
OSKextRef kext;
CFDictionaryRef personalities;
CFMutableArrayRef propPath;
Boolean valid;
Boolean justCheckingIOKitDebug;
} __OSKextValidateIOKitPersonalityContext;
static void __OSKextValidateIOKitPersonalityApplierFunction(
const void * vKey,
const void * vValue,
void * vContext)
{
CFStringRef personalityName = (CFStringRef)vKey;
CFDictionaryRef personality = (CFDictionaryRef)vValue;
__OSKextValidateIOKitPersonalityContext * context =
(__OSKextValidateIOKitPersonalityContext *)vContext;
Boolean checkResult = false;
CFStringRef propKey = NULL; Boolean valueIsNonnil;
CFStringRef ioclassProp = NULL; CFStringRef stringValue = NULL; Boolean checkIOMatchCategory = false;
OSKextRef personalityKext = NULL; CFStringRef diagnosticString = NULL;
CFArrayAppendValue(context->propPath, personalityName);
if (!__OSKextCheckProperty(context->kext,
context->personalities,
personalityName,
context->propPath,
CFDictionaryGetTypeID(),
NULL,
false,
true,
false,
NULL,
NULL)) {
context->valid = false;
goto finish;
}
propKey = CFSTR(kIOKitDebugKey);
CFArrayAppendValue(context->propPath, propKey);
checkResult = __OSKextCheckProperty(context->kext,
personality,
propKey,
context->propPath,
CFNumberGetTypeID(),
NULL,
false,
true,
false,
NULL,
&valueIsNonnil);
context->valid = context->valid && checkResult;
if (checkResult && valueIsNonnil) {
context->kext->flags.plistHasIOKitDebugFlags = 1;
}
CFArrayRemoveValueAtIndex(context->propPath,
CFArrayGetCount(context->propPath) - 1);
if (context->justCheckingIOKitDebug) {
goto finish;
}
propKey = kCFBundleIdentifierKey;
CFArrayAppendValue(context->propPath, propKey);
checkResult = __OSKextCheckProperty(context->kext,
personality,
propKey,
context->propPath,
CFStringGetTypeID(),
NULL,
false,
true,
true, (CFTypeRef *)&stringValue,
&valueIsNonnil);
context->valid = context->valid && checkResult;
if (!stringValue) {
__OSKextAddDiagnostic(context->kext, kOSKextDiagnosticsFlagWarnings,
kOSKextDiagnosticPersonalityHasNoBundleIdentifierKey,
personalityName, NULL);
} else if (!CFEqual(stringValue, OSKextGetIdentifier(context->kext))) {
diagnosticString = CFStringCreateWithFormat(kCFAllocatorDefault,
NULL, CFSTR("%@ -> %@ (kext is %@)"),
personalityName, stringValue, context->kext);
__OSKextAddDiagnostic(context->kext, kOSKextDiagnosticsFlagWarnings,
kOSKextDiagnosticPersonalityHasDifferentBundleIdentifierKey,
personalityName, NULL);
SAFE_RELEASE_NULL(diagnosticString);
}
if (stringValue) {
personalityKext = OSKextGetKextWithIdentifier(stringValue);
if (!personalityKext) {
diagnosticString = CFStringCreateWithFormat(kCFAllocatorDefault,
NULL, CFSTR("'%@' -> '%@'"),
personalityName, stringValue);
__OSKextAddDiagnostic(context->kext, kOSKextDiagnosticsFlagWarnings,
kOSKextDiagnosticPersonalityNamesUnknownKextKey,
diagnosticString, NULL);
SAFE_RELEASE_NULL(diagnosticString);
} else {
if (!OSKextDeclaresExecutable(personalityKext)) {
diagnosticString = CFStringCreateWithFormat(kCFAllocatorDefault,
NULL, CFSTR("'%@' -> '%@'"),
personalityName, stringValue);
__OSKextAddDiagnostic(context->kext, kOSKextDiagnosticsFlagWarnings,
kOSKextDiagnosticPersonalityNamesKextWithNoExecutableKey,
diagnosticString, NULL);
SAFE_RELEASE_NULL(diagnosticString);
}
}
}
CFArrayRemoveValueAtIndex(context->propPath,
CFArrayGetCount(context->propPath) - 1);
propKey = CFSTR(kIOClassKey);
CFArrayAppendValue(context->propPath, propKey);
checkResult = __OSKextCheckProperty(context->kext,
personality,
propKey,
context->propPath,
CFStringGetTypeID(),
NULL,
true,
true,
true,
(CFTypeRef *)&ioclassProp, NULL);
context->valid = context->valid && checkResult;
CFArrayRemoveValueAtIndex(context->propPath,
CFArrayGetCount(context->propPath) - 1);
propKey = CFSTR(kIOProviderClassKey);
CFArrayAppendValue(context->propPath, propKey);
checkResult = __OSKextCheckProperty(context->kext,
personality,
propKey,
context->propPath,
CFStringGetTypeID(),
NULL,
true,
true,
true,
(CFTypeRef *)&stringValue,
NULL);
context->valid = context->valid && checkResult;
if (checkResult && CFEqual(stringValue, CFSTR(kIOResourcesClass))) {
checkIOMatchCategory = true;
}
CFArrayRemoveValueAtIndex(context->propPath,
CFArrayGetCount(context->propPath) - 1);
if (checkIOMatchCategory) {
propKey = CFSTR(kIOMatchCategoryKey);
CFArrayAppendValue(context->propPath, propKey);
checkResult = __OSKextCheckProperty(context->kext,
personality,
propKey,
context->propPath,
CFStringGetTypeID(),
NULL,
false,
true,
false, (CFTypeRef *)&stringValue,
NULL);
context->valid = context->valid && checkResult;
if (checkResult && stringValue) {
if (ioclassProp && !CFEqual(ioclassProp, stringValue)) {
__OSKextAddDiagnostic(context->kext,
kOSKextDiagnosticsFlagWarnings,
kOSKextDiagnosticNonuniqueIOResourcesMatchKey,
personalityName, NULL);
}
}
CFArrayRemoveValueAtIndex(context->propPath,
CFArrayGetCount(context->propPath) - 1);
}
propKey = CFSTR(kIOProbeScoreKey);
CFArrayAppendValue(context->propPath, propKey);
checkResult = __OSKextCheckProperty(context->kext,
personality,
propKey,
context->propPath,
CFNumberGetTypeID(),
NULL,
false,
false,
false,
NULL,
NULL);
CFArrayRemoveValueAtIndex(context->propPath,
CFArrayGetCount(context->propPath) - 1);
finish:
CFArrayRemoveValueAtIndex(context->propPath,
CFArrayGetCount(context->propPath) - 1);
SAFE_RELEASE(diagnosticString);
return;
}
static void __OSKextValidateIOKitPersonalityTargetApplierFunction(
const void * vKey,
const void * vValue,
void * vContext)
{
CFStringRef personalityName = (CFStringRef)vKey;
CFDictionaryRef personality = (CFDictionaryRef)vValue;
__OSKextValidateIOKitPersonalityContext * context =
(__OSKextValidateIOKitPersonalityContext *)vContext;
Boolean checkResult = false;
CFStringRef propKey = NULL; Boolean valueIsNonnil;
CFStringRef stringValue = NULL; OSKextRef personalityKext = NULL; CFStringRef diagnosticString = NULL;
CFArrayAppendValue(context->propPath, personalityName);
if (!__OSKextCheckProperty(context->kext,
context->personalities,
personalityName,
context->propPath,
CFDictionaryGetTypeID(),
NULL,
false,
true,
false,
NULL,
NULL)) {
context->valid = false;
goto finish;
}
propKey = kCFBundleIdentifierKey;
CFArrayAppendValue(context->propPath, propKey);
checkResult = __OSKextCheckProperty(context->kext,
personality,
propKey,
context->propPath,
CFStringGetTypeID(),
NULL,
false,
true,
true, (CFTypeRef *)&stringValue,
&valueIsNonnil);
context->valid = context->valid && checkResult;
if (stringValue) {
personalityKext = OSKextGetKextWithIdentifier(stringValue);
if (personalityKext &&
personalityKext != context->kext &&
!OSKextIsLoadable(personalityKext)) {
diagnosticString = CFStringCreateWithFormat(kCFAllocatorDefault,
NULL, CFSTR("'%@' -> '%@'"),
stringValue, OSKextGetURL(personalityKext));
__OSKextAddDiagnostic(context->kext, kOSKextDiagnosticsFlagWarnings,
kOSKextDiagnosticPersonalityNamesNonloadableKextKey,
diagnosticString, NULL);
SAFE_RELEASE_NULL(diagnosticString);
}
}
CFArrayRemoveValueAtIndex(context->propPath,
CFArrayGetCount(context->propPath) - 1);
finish:
CFArrayRemoveValueAtIndex(context->propPath,
CFArrayGetCount(context->propPath) - 1);
SAFE_RELEASE(diagnosticString);
return;
}
Boolean __OSKextValidate(OSKextRef aKext, CFMutableArrayRef propPath)
{
Boolean result = true; CFStringRef propKey = NULL; CFMutableArrayRef allocPropPath = NULL; Boolean checkResult;
CFDictionaryRef dictValue = NULL; Boolean valueIsNonnil;
char kextPathCString[PATH_MAX];
__OSKextGetFileSystemPath(aKext, NULL,
false, kextPathCString);
OSKextLog(aKext, kOSKextLogStepLevel | kOSKextLogValidationFlag,
"Validating %s.", kextPathCString);
aKext->flags.invalid = 0;
aKext->flags.valid = 0;
aKext->flags.validated = 0;
if (!propPath) {
allocPropPath = propPath = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks);
if (!propPath) {
OSKextLogMemError();
goto finish;
}
}
if (!__OSKextProcessInfoDictionary(aKext, NULL)) {
result = false;
}
if (!aKext->infoDictionary) {
goto finish;
}
propKey = CFSTR(kOSBundleAllowUserLoadKey);
CFArrayAppendValue(propPath, propKey);
checkResult = __OSKextCheckProperty(aKext,
aKext->infoDictionary,
propKey,
propPath,
CFBooleanGetTypeID(),
NULL,
FALSE,
true,
FALSE,
NULL,
NULL);
result = result && checkResult;
CFArrayRemoveValueAtIndex(propPath, CFArrayGetCount(propPath) - 1);
propKey = CFSTR(kOSBundleLibrariesKey);
CFArrayAppendValue(propPath, propKey);
checkResult = __OSKextCheckProperty(aKext,
aKext->infoDictionary,
propKey,
propPath,
CFDictionaryGetTypeID(),
NULL,
(OSKextDeclaresExecutable(aKext) &&
!OSKextIsKernelComponent(aKext)),
true,
(OSKextDeclaresExecutable(aKext) &&
!OSKextIsKernelComponent(aKext)),
(CFTypeRef *)&dictValue,
NULL);
result = result && checkResult;
if (!dictValue || !CFDictionaryGetCount(dictValue)) {
if (OSKextDeclaresExecutable(aKext) &&
!OSKextIsKernelComponent(aKext)) {
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticMissingPropertyKey,
propPath, NULL);
}
} else if (OSKextIsKernelComponent(aKext)) {
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticBadSystemPropertyKey, CFSTR(kOSBundleLibrariesKey),
NULL);
result = false;
} else if (!OSKextDeclaresExecutable(aKext) && !OSKextIsLibrary(aKext)) {
__OSKextSetDiagnostic(aKext, kOSKextDiagnosticsFlagWarnings,
kOSKextDiagnosticCodelessWithLibrariesKey);
}
if (checkResult && dictValue) {
__OSKextValidateOSBundleLibraryContext validateLibrariesContext;
validateLibrariesContext.kext = aKext;
validateLibrariesContext.libraries = dictValue;
validateLibrariesContext.propPath = propPath;
validateLibrariesContext.valid = true; validateLibrariesContext.hasKernelStyleDependency = false; validateLibrariesContext.hasKPIStyleDependency = false; CFDictionaryApplyFunction(dictValue,
__OSKextValidateOSBundleLibraryApplierFunction,
&validateLibrariesContext);
result = result && validateLibrariesContext.valid;
if (validateLibrariesContext.hasKernelStyleDependency &&
validateLibrariesContext.hasKPIStyleDependency) {
__OSKextSetDiagnostic(aKext, kOSKextDiagnosticsFlagWarnings,
kOSKextDiagnosticDeclaresBothKernelAndKPIDependenciesKey);
}
}
dictValue = NULL;
CFArrayRemoveValueAtIndex(propPath, CFArrayGetCount(propPath) - 1);
propKey = CFSTR(kIOKitPersonalitiesKey);
CFArrayAppendValue(propPath, propKey);
checkResult = __OSKextCheckProperty(aKext,
aKext->infoDictionary,
propKey,
propPath,
CFDictionaryGetTypeID(),
NULL,
false, true,
false, (CFTypeRef *)&dictValue,
&valueIsNonnil);
result = result && checkResult;
if (checkResult && dictValue) {
__OSKextValidateIOKitPersonalityContext validatePersonalitiesContext;
validatePersonalitiesContext.kext = aKext;
validatePersonalitiesContext.personalities = dictValue;
validatePersonalitiesContext.propPath = propPath;
validatePersonalitiesContext.valid = true; validatePersonalitiesContext.justCheckingIOKitDebug = false;
CFDictionaryApplyFunction(dictValue,
__OSKextValidateIOKitPersonalityApplierFunction,
&validatePersonalitiesContext);
result = result && validatePersonalitiesContext.valid;
}
CFArrayRemoveValueAtIndex(propPath, CFArrayGetCount(propPath) - 1);
result = __OSKextValidateExecutable(aKext) && result;
finish:
SAFE_RELEASE(allocPropPath);
if (result) {
aKext->flags.validated = true;
aKext->flags.valid = true;
} else {
aKext->flags.invalid = 1;
}
return result;
}
Boolean OSKextValidate(OSKextRef aKext)
{
Boolean result = TRUE;
CFMutableArrayRef propPath = NULL; CFStringRef propKey = NULL; Boolean checkResult;
CFDictionaryRef dictValue = NULL;
propPath = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks);
if (!propPath) {
OSKextLogMemError();
goto finish;
}
result = __OSKextValidate(aKext, propPath);
propKey = CFSTR(kIOKitPersonalitiesKey);
CFArrayAppendValue(propPath, propKey);
checkResult = __OSKextCheckProperty(aKext,
aKext->infoDictionary,
propKey,
propPath,
CFDictionaryGetTypeID(),
NULL,
false, true,
false, (CFTypeRef *)&dictValue,
NULL);
result = result && checkResult;
if (checkResult && dictValue) {
__OSKextValidateIOKitPersonalityContext validatePersonalitiesContext;
validatePersonalitiesContext.kext = aKext;
validatePersonalitiesContext.personalities = dictValue;
validatePersonalitiesContext.propPath = propPath;
validatePersonalitiesContext.valid = true; validatePersonalitiesContext.justCheckingIOKitDebug = false;
CFDictionaryApplyFunction(dictValue,
__OSKextValidateIOKitPersonalityTargetApplierFunction,
&validatePersonalitiesContext);
result = result && validatePersonalitiesContext.valid;
}
CFArrayRemoveValueAtIndex(propPath, CFArrayGetCount(propPath) - 1);
finish:
SAFE_RELEASE(propPath);
return result;
}
Boolean __OSKextValidateExecutable(OSKextRef aKext)
{
Boolean result = false;
CFDataRef executable = NULL;
const struct mach_header * mach_header = NULL; const void * file_end = NULL;
macho_seek_result seek_result;
uint8_t nlist_type;
const void * symbol_address = NULL;
const kmod_info_32_v1_t * kmod_info_32 = NULL; const kmod_info_64_v1_t * kmod_info_64 = NULL; CFStringRef bundleIdentifier = NULL; const u_char * kmodNameCString = NULL; const u_char * kmodVersionCString = NULL; CFStringRef kmodName = NULL; OSKextVersion version = -1;
if (!OSKextDeclaresExecutable(aKext) ||
OSKextIsInterface(aKext)) {
result = true;
goto finish;
}
if (!__OSKextReadExecutable(aKext)) {
goto finish;
}
executable = OSKextCopyExecutableForArchitecture(aKext, OSKextGetArchitecture());
if (!executable) {
result = true;
goto finish;
}
mach_header = (const struct mach_header *)CFDataGetBytePtr(executable);
file_end = (((const char *)mach_header) + CFDataGetLength(executable));
seek_result = macho_find_symbol(
mach_header, file_end, __kOSKextKmodInfoSymbol, &nlist_type,
&symbol_address);
if ((macho_seek_result_found != seek_result) ||
((nlist_type & N_TYPE) != N_SECT) ||
(symbol_address == NULL)) {
__OSKextSetDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticExecutableBadKey);
goto finish;
}
if (!aKext->flags.warnForMismatchedKmodInfo) {
result = true;
goto finish;
}
if (__OSKextIsArchitectureLP64()) {
kmod_info_64 = (kmod_info_64_v1_t *)symbol_address;
kmodNameCString = kmod_info_64->name;
kmodVersionCString = kmod_info_64->version;
} else {
kmod_info_32 = (kmod_info_32_v1_t *)symbol_address;
kmodNameCString = kmod_info_32->name;
kmodVersionCString = kmod_info_32->version;
}
bundleIdentifier = OSKextGetIdentifier(aKext);
kmodName = CFStringCreateWithCString(kCFAllocatorDefault,
(const char *)kmodNameCString, kCFStringEncodingUTF8);
if (!kmodName) {
OSKextLogMemError();
goto finish;
}
if (!CFEqual(bundleIdentifier, kmodName)) {
__OSKextSetDiagnostic(aKext, kOSKextDiagnosticsFlagWarnings,
kOSKextDiagnosticBundleIdentifierMismatchKey);
}
version = OSKextParseVersionString((const char *)kmodVersionCString);
if (version < 0 || aKext->version != version) {
__OSKextSetDiagnostic(aKext, kOSKextDiagnosticsFlagWarnings,
kOSKextDiagnosticBundleVersionMismatchKey);
}
result = true;
finish:
if (executable) {
(void)posix_madvise((void *)CFDataGetBytePtr(executable),
CFDataGetLength(executable),
POSIX_MADV_DONTNEED);
}
SAFE_RELEASE(executable);
SAFE_RELEASE(kmodName);
return result;
}
Boolean __OSKextIsValid(OSKextRef aKext)
{
if (aKext->flags.invalid) {
return false;
}
if (!aKext->flags.validated) {
__OSKextValidate(aKext, NULL);
}
return aKext->flags.valid ? true : false;
}
Boolean OSKextIsValid(OSKextRef aKext)
{
if (aKext->flags.invalid) {
return false;
}
if (!aKext->flags.validated) {
OSKextValidate(aKext);
}
return aKext->flags.valid ? true : false;
}
static Boolean __OSKextBasicFilesystemAuthenticationRecursive(
OSKextRef aKext,
CFURLRef anURL,
CFURLRef pluginsURL)
{
Boolean result = true; CFStringRef filename = NULL; CFURLRef absURL = NULL; char kextPath[PATH_MAX];
char urlPath[PATH_MAX];
struct stat stat_buf;
struct stat lstat_buf;
CFArrayRef urlContents = NULL; SInt32 error;
CFIndex count, i;
__OSKextGetFileSystemPath(aKext, NULL,
FALSE, kextPath);
filename = CFURLCopyLastPathComponent(anURL);
if (filename && CFEqual(filename, __kDSStoreFilename)) {
goto finish;
}
absURL = CFURLCopyAbsoluteURL(anURL);
if (!absURL) {
result = false;
OSKextLogMemError();
goto finish;
}
if (!__OSKextGetFileSystemPath( NULL, anURL,
true, urlPath)) {
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticURLConversionKey, anURL, NULL);
result = false;
goto finish;
}
OSKextLog(aKext,
kOSKextLogStepLevel |
kOSKextLogAuthenticationFlag | kOSKextLogFileAccessFlag,
"Authenticating %s file/directory %s.",
kextPath, urlPath);
if (0 != stat(urlPath, &stat_buf) || 0 != lstat(urlPath, &lstat_buf)) {
if (errno == ENOENT) {
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagAuthentication,
kOSKextDiagnosticFileNotFoundKey, anURL, NULL);
result = false;
goto finish;
} else {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Can't stat %s - %s.",
urlPath, strerror(errno));
result = false;
goto finish;
}
}
if ( (stat_buf.st_uid != 0) || (stat_buf.st_gid != 0 ) ||
(stat_buf.st_mode & S_IWOTH) || (stat_buf.st_mode & S_IWGRP) ) {
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagAuthentication,
kOSKextDiagnosticOwnerPermissionKey, anURL, NULL);
result = false;
}
if ((lstat_buf.st_mode & S_IFMT) == S_IFLNK) {
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagWarnings,
kOSKextDiagnosticSymlinkKey, anURL, NULL);
}
if (!CFURLHasDirectoryPath(anURL)) {
goto finish;
}
if (pluginsURL && CFEqual(absURL, pluginsURL)) {
goto finish;
}
urlContents = CFURLCreatePropertyFromResource(CFGetAllocator(aKext),
anURL,
kCFURLFileDirectoryContents, &error);
if (!urlContents || error) {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag |
kOSKextLogAuthenticationFlag,
"Can't read file %s.", urlPath);
goto finish;
}
count = CFArrayGetCount(urlContents);
for (i = 0; i < count; i++) {
CFURLRef thisURL = (CFURLRef)CFArrayGetValueAtIndex(urlContents, i);
result = __OSKextBasicFilesystemAuthenticationRecursive(aKext, thisURL, pluginsURL) && result;
}
finish:
SAFE_RELEASE(filename);
SAFE_RELEASE(absURL);
SAFE_RELEASE(urlContents);
return result;
}
Boolean
_OSKextBasicFilesystemAuthentication(OSKextRef aKext, __unused void *context)
{
Boolean result = true; CFBundleRef kextBundle = NULL; CFURLRef pluginsURL = NULL; CFURLRef pluginsAbsURL = NULL;
if (OSKextIsFromMkext(aKext)) {
if (aKext->mkextInfo->mkextURL) {
result = __OSKextBasicFilesystemAuthenticationRecursive(aKext,
aKext->mkextInfo->mkextURL, NULL);
} else {
__OSKextSetDiagnostic(aKext, kOSKextDiagnosticsFlagAuthentication,
kOSKextDiagnosticNoFileKey);
result = false;
}
} else {
kextBundle = CFBundleCreate(kCFAllocatorDefault, aKext->bundleURL);
if (!kextBundle) {
result = false;
goto finish;
}
pluginsURL = CFBundleCopyBuiltInPlugInsURL(kextBundle);
if (pluginsURL) {
pluginsAbsURL = CFURLCopyAbsoluteURL(pluginsURL);
if (!pluginsAbsURL) {
OSKextLogMemError();
result = false;
goto finish;
}
}
result = __OSKextBasicFilesystemAuthenticationRecursive(aKext, aKext->bundleURL,
pluginsAbsURL);
}
finish:
SAFE_RELEASE(kextBundle);
SAFE_RELEASE(pluginsURL);
SAFE_RELEASE(pluginsAbsURL);
return result;
}
Boolean OSKextAuthenticate(OSKextRef aKext)
{
Boolean result = true;
char kextPath[PATH_MAX];
aKext->flags.inauthentic = 0;
aKext->flags.authentic = 0;
aKext->flags.authenticated = 0;
aKext->flags.sip_protected = 0;
if (__sOSKextAuthenticationFunction) {
if (!__OSKextGetFileSystemPath(aKext, NULL, true, kextPath)) {
OSKextLog(aKext,
kOSKextLogErrorLevel |
kOSKextLogGeneralFlag | kOSKextLogKextBookkeepingFlag,
"Could not get absolute path of kext!");
result = false;
goto finish;
}
if (!__sOSKextIsSIPDisabled && rootless_check_trusted(kextPath) == 0) {
aKext->flags.sip_protected = 1;
}
result = __sOSKextAuthenticationFunction(aKext, __sOSKextAuthenticationContext);
} else {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogValidationFlag,
"Trying to authenticate kext with no authentication function, failing.");
result = false;
}
finish:
aKext->flags.authenticated = 1;
if (result) {
aKext->flags.authentic = 1;
} else {
aKext->flags.inauthentic = 1;
}
return result;
}
Boolean OSKextIsAuthentic(OSKextRef aKext)
{
if (aKext->flags.inauthentic) {
return false;
}
if (!aKext->flags.authenticated) {
OSKextAuthenticate(aKext);
}
return aKext->flags.authentic ? true : false;
}
#ifndef IOKIT_EMBEDDED
Boolean OSKextIsSigned(OSKextRef aKext)
{
CFURLRef checkURL = NULL;
if (aKext->flags.isSigned) {
return(true);
}
checkURL = CFURLCreateCopyAppendingPathComponent(
kCFAllocatorDefault,
aKext->bundleURL,
CFSTR("Contents/_CodeSignature"),
true);
if (checkURL) {
if (CFURLResourceIsReachable(checkURL, NULL)) {
aKext->flags.isSigned = 1;
}
else {
__OSKextSetDiagnostic(aKext, kOSKextDiagnosticsFlagWarnings,
kOSKextDiagnosticNotSignedKey);
}
}
SAFE_RELEASE(checkURL);
return aKext->flags.isSigned ? true : false;
}
#endif
Boolean OSKextIsLoadable(OSKextRef aKext)
{
Boolean result = false;
if (__OSKextIsValid(aKext) &&
OSKextIsAuthentic(aKext) &&
OSKextResolveDependencies(aKext) &&
OSKextValidateDependencies(aKext) &&
OSKextAuthenticateDependencies(aKext)) {
result = true;
}
return result;
}
CFDictionaryRef
OSKextCopyDiagnostics(OSKextRef aKext,
OSKextDiagnosticsFlags typeFlags)
{
CFDictionaryRef result = NULL;
CFMutableDictionaryRef mResult = NULL;
if (aKext->diagnostics) {
CFDictionaryRef diagnosticsDict;
mResult = CFDictionaryCreateMutable(CFGetAllocator(aKext), 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (!mResult) {
OSKextLogMemError();
goto finish;
}
if ((typeFlags & kOSKextDiagnosticsFlagValidation)) {
diagnosticsDict = __OSKextCopyDiagnosticsDict(aKext,
kOSKextDiagnosticsFlagValidation);
if (diagnosticsDict && CFDictionaryGetCount(diagnosticsDict)) {
CFDictionarySetValue(mResult, kOSKextDiagnosticsValidationKey,
diagnosticsDict);
}
SAFE_RELEASE_NULL(diagnosticsDict);
}
if ((typeFlags & kOSKextDiagnosticsFlagAuthentication)) {
diagnosticsDict = __OSKextCopyDiagnosticsDict(aKext,
kOSKextDiagnosticsFlagAuthentication);
if (diagnosticsDict && CFDictionaryGetCount(diagnosticsDict)) {
CFDictionarySetValue(mResult, kOSKextDiagnosticsAuthenticationKey,
diagnosticsDict);
}
SAFE_RELEASE_NULL(diagnosticsDict);
}
if ((typeFlags & kOSKextDiagnosticsFlagDependencies)) {
diagnosticsDict = __OSKextCopyDiagnosticsDict(aKext,
kOSKextDiagnosticsFlagDependencies);
if (diagnosticsDict && CFDictionaryGetCount(diagnosticsDict)) {
CFDictionarySetValue(mResult, kOSKextDiagnosticsDependenciesKey,
diagnosticsDict);
}
SAFE_RELEASE_NULL(diagnosticsDict);
}
if ((typeFlags & kOSKextDiagnosticsFlagWarnings)) {
diagnosticsDict = __OSKextCopyDiagnosticsDict(aKext,
kOSKextDiagnosticsFlagWarnings);
if (diagnosticsDict && CFDictionaryGetCount(diagnosticsDict)) {
CFDictionarySetValue(mResult, kOSKextDiagnosticsWarningsKey,
diagnosticsDict);
}
SAFE_RELEASE_NULL(diagnosticsDict);
}
if ((typeFlags & kOSKextDiagnosticsFlagBootLevel)) {
diagnosticsDict = __OSKextCopyDiagnosticsDict(aKext,
kOSKextDiagnosticsFlagBootLevel);
if (diagnosticsDict && CFDictionaryGetCount(diagnosticsDict)) {
CFDictionarySetValue(mResult, kOSKextDiagnosticsBootLevelKey,
diagnosticsDict);
}
SAFE_RELEASE_NULL(diagnosticsDict);
}
result = (CFDictionaryRef)mResult;
} else {
result = CFDictionaryCreate(CFGetAllocator(aKext),
NULL, NULL, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
}
finish:
return result;
}
CFDictionaryRef __OSKextCopyDiagnosticsDict(
OSKextRef aKext,
OSKextDiagnosticsFlags type)
{
CFDictionaryRef result = NULL;
CFDictionaryRef dictToCopy = NULL;
if (!aKext->diagnostics) {
goto finish;
}
switch (type) {
case kOSKextDiagnosticsFlagValidation:
dictToCopy = aKext->diagnostics->validationFailures;
break;
case kOSKextDiagnosticsFlagAuthentication:
dictToCopy = aKext->diagnostics->authenticationFailures;
break;
case kOSKextDiagnosticsFlagDependencies:
dictToCopy = aKext->diagnostics->dependencyFailures;
break;
case kOSKextDiagnosticsFlagWarnings:
dictToCopy = aKext->diagnostics->warnings;
break;
case kOSKextDiagnosticsFlagBootLevel:
dictToCopy = aKext->diagnostics->bootLevel;
break;
default:
break;
}
if (dictToCopy) {
result = CFDictionaryCreateCopy(CFGetAllocator(aKext), dictToCopy);
}
finish:
if (!result) {
result = CFDictionaryCreate(CFGetAllocator(aKext),
NULL, NULL, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
}
return result;
}
void OSKextLogDiagnostics(OSKextRef aKext,
OSKextDiagnosticsFlags typeFlags)
{
CFDictionaryRef diagnosticsDict = NULL; CFStringRef plistString = NULL; char * cString = NULL;
diagnosticsDict = OSKextCopyDiagnostics(aKext, typeFlags);
if (!diagnosticsDict || !CFDictionaryGetCount(diagnosticsDict)) {
goto finish;
}
plistString = createCFStringForPlist_new(diagnosticsDict,
kPListStyleDiagnostics);
if (!plistString) {
goto finish;
}
cString = createUTF8CStringForCFString(plistString);
if (!cString) {
goto finish;
}
OSKextLog( NULL,
kOSKextLogExplicitLevel | kOSKextLogGeneralFlag,
"%s", cString);
finish:
SAFE_RELEASE(diagnosticsDict);
SAFE_RELEASE(plistString);
SAFE_FREE(cString);
return;
}
typedef struct {
OSKextDiagnosticsFlags typeFlags;
} __OSKextFlushDiagnosticsContext;
static void __OSKextFlushDiagnosticsApplierFunction(
const void * vKey __unused,
const void * vValue,
void * vContext)
{
OSKextRef aKext = (OSKextRef)vValue;
__OSKextFlushDiagnosticsContext * context =
(__OSKextFlushDiagnosticsContext *)vContext;
OSKextFlushDiagnostics(aKext, context->typeFlags);
return;
}
const UInt32 __kOSKextDiagnosticsFlagAllImplemented =
kOSKextDiagnosticsFlagValidation |
kOSKextDiagnosticsFlagAuthentication |
kOSKextDiagnosticsFlagDependencies |
kOSKextDiagnosticsFlagWarnings |
kOSKextDiagnosticsFlagBootLevel;
void OSKextFlushDiagnostics(OSKextRef aKext, OSKextDiagnosticsFlags typeFlags)
{
pthread_once(&__sOSKextInitialized, __OSKextInitialize);
if (aKext) {
if (aKext->diagnostics) {
if (typeFlags & kOSKextDiagnosticsFlagValidation) {
SAFE_RELEASE_NULL(aKext->diagnostics->validationFailures);
}
if (typeFlags & kOSKextDiagnosticsFlagAuthentication) {
SAFE_RELEASE_NULL(aKext->diagnostics->authenticationFailures);
}
if (typeFlags & kOSKextDiagnosticsFlagDependencies) {
SAFE_RELEASE_NULL(aKext->diagnostics->dependencyFailures);
}
if (typeFlags & kOSKextDiagnosticsFlagWarnings) {
SAFE_RELEASE_NULL(aKext->diagnostics->warnings);
}
if (typeFlags & kOSKextDiagnosticsFlagBootLevel) {
SAFE_RELEASE_NULL(aKext->diagnostics->bootLevel);
}
if ((typeFlags & __kOSKextDiagnosticsFlagAllImplemented) ==
__kOSKextDiagnosticsFlagAllImplemented) {
SAFE_FREE_NULL(aKext->diagnostics);
}
}
} else if (__sOSKextsByURL) {
__OSKextFlushDiagnosticsContext context;
context.typeFlags = typeFlags;
CFDictionaryApplyFunction(__sOSKextsByURL,
__OSKextFlushDiagnosticsApplierFunction, &context);
}
return;
}
CFMutableDictionaryRef __OSKextGetDiagnostics(OSKextRef aKext,
OSKextDiagnosticsFlags type)
{
CFMutableDictionaryRef result = NULL;
if (!aKext->diagnostics) {
aKext->diagnostics = (__OSKextDiagnostics *)malloc(
sizeof(*(aKext->diagnostics)));
if (!aKext->diagnostics) {
OSKextLogMemError();
goto finish;
}
memset(aKext->diagnostics, 0, sizeof(*(aKext->diagnostics)));
}
switch (type) {
case kOSKextDiagnosticsFlagValidation:
if (!aKext->diagnostics->validationFailures) {
aKext->diagnostics->validationFailures =
CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!aKext->diagnostics->validationFailures) {
OSKextLogMemError();
goto finish;
}
}
result = aKext->diagnostics->validationFailures;
break;
case kOSKextDiagnosticsFlagAuthentication:
if (!aKext->diagnostics->authenticationFailures) {
aKext->diagnostics->authenticationFailures =
CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!aKext->diagnostics->authenticationFailures) {
OSKextLogMemError();
goto finish;
}
}
result = aKext->diagnostics->authenticationFailures;
break;
case kOSKextDiagnosticsFlagDependencies:
if (!aKext->diagnostics->dependencyFailures) {
aKext->diagnostics->dependencyFailures =
CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!aKext->diagnostics->dependencyFailures) {
OSKextLogMemError();
goto finish;
}
}
result = aKext->diagnostics->dependencyFailures;
break;
case kOSKextDiagnosticsFlagWarnings:
if (!aKext->diagnostics->warnings) {
aKext->diagnostics->warnings =
CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!aKext->diagnostics->warnings) {
OSKextLogMemError();
goto finish;
}
}
result = aKext->diagnostics->warnings;
break;
case kOSKextDiagnosticsFlagBootLevel:
if (!aKext->diagnostics->bootLevel) {
aKext->diagnostics->bootLevel =
CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!aKext->diagnostics->bootLevel) {
OSKextLogMemError();
goto finish;
}
}
result = aKext->diagnostics->bootLevel;
break;
}
finish:
return result;
}
void __OSKextSetDiagnostic(
OSKextRef aKext,
OSKextDiagnosticsFlags type,
CFStringRef key)
{
CFMutableDictionaryRef diagnosticDict;
if (!(type & __sOSKextRecordsDiagnositcs)) {
goto finish;
}
diagnosticDict = __OSKextGetDiagnostics(aKext, type);
if (!diagnosticDict) {
goto finish;
}
CFDictionarySetValue(diagnosticDict, key, kCFBooleanTrue);
finish:
return;
}
void __OSKextAddDiagnostic(
OSKextRef aKext,
OSKextDiagnosticsFlags type,
CFStringRef key,
CFTypeRef value,
CFTypeRef note)
{
CFMutableDictionaryRef diagnosticDict;
CFMutableArrayRef valueArray;
CFMutableArrayRef createdArray = NULL; CFStringRef combinedValue = NULL; CFStringRef valueWithNote = NULL; CFStringRef pathValue = NULL; CFStringRef valueToSet = NULL;
if (!(type & __sOSKextRecordsDiagnositcs)) {
goto finish;
}
diagnosticDict = __OSKextGetDiagnostics(aKext, type);
if (!diagnosticDict) {
goto finish;
}
valueToSet = value;
if (CFGetTypeID(value) == CFURLGetTypeID()) {
pathValue = CFURLCopyFileSystemPath(value, kCFURLPOSIXPathStyle);
if (!pathValue) {
OSKextLogMemError();
goto finish;
}
valueToSet = pathValue;
}
else if (CFGetTypeID(value) == CFArrayGetTypeID()) {
combinedValue = CFStringCreateByCombiningStrings(kCFAllocatorDefault,
(CFArrayRef)value, CFSTR("."));
if (!combinedValue) {
OSKextLogMemError();
goto finish;
}
valueToSet = combinedValue;
}
if (note) {
valueWithNote = CFStringCreateWithFormat(kCFAllocatorDefault,
NULL, CFSTR("%@ - %@"), valueToSet, note);
if (!valueWithNote) {
OSKextLogMemError();
goto finish;
}
valueToSet = valueWithNote;
}
valueArray = (CFMutableArrayRef)CFDictionaryGetValue(diagnosticDict, key);
if (!valueArray) {
valueArray = createdArray = CFArrayCreateMutable(kCFAllocatorDefault,
0, &kCFTypeArrayCallBacks);
if (!valueArray) {
OSKextLogMemError();
goto finish;
}
CFDictionarySetValue(diagnosticDict, key, valueArray);
} else if (CFArrayGetTypeID() != CFGetTypeID(valueArray)) {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogKextBookkeepingFlag,
"Internal error in diagnositcs recording");
goto finish;
}
if (!CFArrayGetCountOfValue(valueArray, RANGE_ALL(valueArray), valueToSet)) {
CFArrayAppendValue(valueArray, valueToSet);
}
finish:
SAFE_RELEASE(createdArray);
SAFE_RELEASE(combinedValue);
SAFE_RELEASE(valueWithNote);
SAFE_RELEASE(pathValue);
return;
}
Boolean __OSKextCheckProperty(
OSKextRef aKext,
CFDictionaryRef aDict,
CFTypeRef propKey,
CFTypeRef diagnosticValue,
CFTypeID expectedType,
CFArrayRef legalValues,
Boolean required,
Boolean typeRequired,
Boolean nonnilRequired,
CFTypeRef * valueOut,
Boolean * valueIsNonnil)
{
Boolean result = false;
CFTypeRef value = NULL; Boolean isFloat = false;
CFStringRef diagnosticString = NULL; CFStringRef noteString = NULL; CFNumberRef zeroValue = NULL; Boolean valueIsNonnil_local = false;
CFIndex count, i;
if (valueIsNonnil) {
*valueIsNonnil = false;
}
if (valueOut) {
*valueOut = NULL;
}
if (aDict == aKext->infoDictionary) {
value = OSKextGetValueForInfoDictionaryKey(aKext, propKey);
} else {
value = CFDictionaryGetValue(aDict, propKey);
}
if (!value) {
if (!required) {
result = true;
} else {
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticMissingPropertyKey, diagnosticValue,
NULL);
}
goto finish;
} else if (valueOut) {
*valueOut = value;
}
isFloat = CFNumberGetTypeID() == CFGetTypeID(value) &&
CFNumberIsFloatType((CFNumberRef)value);
if (expectedType != CFGetTypeID(value) || isFloat) {
const char * expectedTag = NULL;
if (expectedType == CFStringGetTypeID()) {
expectedTag = "<string>";
} else if (expectedType == CFNumberGetTypeID() && isFloat) {
expectedTag = "<integer> (kexts may not use <real>)";
} else if (expectedType == CFNumberGetTypeID()) {
expectedTag = "<integer>";
} else if (expectedType == CFDataGetTypeID()) {
expectedTag = "<data>";
} else if (expectedType == CFBooleanGetTypeID()) {
expectedTag = "boolean, <true/> or <false/>";
} else if (expectedType == CFArrayGetTypeID()) {
expectedTag = "<array>";
} else if (expectedType == CFDictionaryGetTypeID()) {
expectedTag = "<dict>";
}
if (expectedType) {
noteString = CFStringCreateWithFormat(kCFAllocatorDefault,
NULL, CFSTR("should be %s"),
expectedTag);
}
__OSKextAddDiagnostic(aKext,
typeRequired ? kOSKextDiagnosticsFlagValidation : kOSKextDiagnosticsFlagWarnings,
typeRequired ? kOSKextDiagnosticPropertyIsIllegalTypeKey : kOSKextDiagnosticTypeWarningKey,
diagnosticString ? diagnosticString : diagnosticValue, noteString);
goto finish;
}
if (legalValues) {
Boolean valueIsLegal = false;
count = CFArrayGetCount(legalValues);
for (i = 0; i < count; i++) {
CFTypeRef thisValue = CFArrayGetValueAtIndex(legalValues, i);
if (CFEqual(thisValue, value)) {
valueIsLegal = true;
break;
}
}
if (!valueIsLegal) {
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticPropertyIsIllegalValueKey, diagnosticValue,
NULL);
}
}
if (expectedType == CFBooleanGetTypeID()) {
CFBooleanRef boolValue = (CFBooleanRef)value;
valueIsNonnil_local = CFBooleanGetValue(boolValue);
} else if (expectedType == CFStringGetTypeID()) {
CFStringRef stringValue = (CFStringRef)value;
valueIsNonnil_local = CFStringGetLength(stringValue) ? true : false;
} else if (expectedType == CFDataGetTypeID()) {
CFDataRef dataValue = (CFDataRef)value;
valueIsNonnil_local = CFDataGetLength(dataValue) ? true : false;
} else if (expectedType == CFArrayGetTypeID()) {
CFArrayRef arrayValue = (CFArrayRef)value;
valueIsNonnil_local = CFArrayGetCount(arrayValue) ? true : false;
} else if (expectedType == CFDictionaryGetTypeID()) {
CFDictionaryRef dictValue = (CFDictionaryRef)value;
valueIsNonnil_local = CFDictionaryGetCount(dictValue) ? true : false;
} else if (expectedType == CFNumberGetTypeID()) {
CFNumberRef numberValue = (CFNumberRef)value;
int zero = 0;
zeroValue = CFNumberCreate(kCFAllocatorDefault,
kCFNumberIntType, &zero);
if (!zeroValue) {
OSKextLogMemError();
}
if (kCFCompareEqualTo !=
CFNumberCompare(numberValue, zeroValue, NULL)) {
valueIsNonnil_local = true;
}
}
if (valueIsNonnil) {
*valueIsNonnil = valueIsNonnil_local;
}
if (nonnilRequired && !valueIsNonnil_local) {
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticPropertyIsIllegalValueKey, diagnosticValue,
NULL);
goto finish;
}
result = true;
finish:
SAFE_RELEASE(diagnosticString);
SAFE_RELEASE(noteString);
SAFE_RELEASE(zeroValue);
return result;
}
#pragma mark Misc Private Functions
Boolean __OSKextReadInfoDictionary(
OSKextRef aKext,
CFBundleRef kextBundle)
{
Boolean result = false;
CFBundleRef createdBundle = NULL; CFURLRef infoDictURL = NULL; struct stat statbuf;
CFDataRef infoDictData = NULL; char * infoDictXML = NULL; int fd = -1; ssize_t totalBytesRead;
CFStringRef errorString = NULL; char * errorCString = NULL; char kextPath[PATH_MAX];
char mkextPath[PATH_MAX] = __kStringUnknown;
char infoDictPath[PATH_MAX];
__OSKextGetFileSystemPath(aKext, NULL,
false, kextPath);
if (aKext->infoDictionary) {
result = true;
goto finish;
}
if (aKext->staticFlags.isFromMkext) {
__OSKextGetFileSystemPath( NULL,
aKext->mkextInfo->mkextURL,
false, mkextPath);
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"%s created from m%s is missing its info dictionary.",
kextPath, mkextPath);
result = false;
goto finish;
}
if (!kextBundle) {
OSKextLog(aKext, kOSKextLogDebugLevel | kOSKextLogFileAccessFlag,
"Opening CFBundle for %s.", kextPath);
kextBundle = createdBundle = CFBundleCreate(kCFAllocatorDefault,
aKext->bundleURL);
if (!createdBundle) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Can't open CFBundle for %s.", kextPath);
__OSKextSetDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticNotABundleKey);
goto finish;
}
}
infoDictURL = _CFBundleCopyInfoPlistURL(kextBundle);
if (!infoDictURL) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"%s has no Info.plist file.", kextPath);
__OSKextSetDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticNotABundleKey);
goto finish;
}
if (!__OSKextGetFileSystemPath( NULL, infoDictURL,
true, infoDictPath)) {
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticURLConversionKey, infoDictURL, NULL);
goto finish;
}
if (0 != stat(infoDictPath, &statbuf)) {
if (errno == ENOENT) {
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticFileNotFoundKey, infoDictURL, NULL);
} else {
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticStatFailureKey, infoDictURL, NULL);
}
goto finish;
}
fd = open(infoDictPath, O_RDONLY);
if (fd < 0) {
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticFileAccessKey, infoDictURL, NULL);
goto finish;
}
infoDictXML = (char *)malloc((1 + statbuf.st_size) * sizeof(char));
if (!infoDictXML) {
OSKextLogMemError();
goto finish;
}
for (totalBytesRead = 0; totalBytesRead < statbuf.st_size; ) {
ssize_t bytesRead = read(fd, infoDictXML + totalBytesRead,
statbuf.st_size - totalBytesRead);
if (bytesRead < 0) {
__OSKextSetDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticFileAccessKey);
goto finish;
}
totalBytesRead += bytesRead;
}
infoDictXML[totalBytesRead] = '\0';
aKext->infoDictionary = (CFMutableDictionaryRef)IOCFUnserialize(
(const char *)infoDictXML, kCFAllocatorDefault, 0, &errorString);
if (!aKext->infoDictionary ||
CFDictionaryGetTypeID() != CFGetTypeID(aKext->infoDictionary)) {
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticBadPropertyListXMLKey,
errorString, NULL);
if (errorString) {
errorCString = createUTF8CStringForCFString(errorString);
}
OSKextLog(aKext, kOSKextLogErrorLevel,
"Can't read info dictionary for %s: %s.",
kextPath, errorCString ? errorCString : "(unknown error)");
goto finish;
}
result = true;
finish:
if (createdBundle) {
OSKextLog(aKext, kOSKextLogDebugLevel | kOSKextLogFileAccessFlag,
"Releasing CFBundle for %s.",
kextPath);
}
SAFE_RELEASE(createdBundle);
SAFE_RELEASE(infoDictURL);
SAFE_RELEASE(infoDictData);
SAFE_FREE(infoDictXML);
SAFE_RELEASE(errorString);
SAFE_FREE(errorCString);
if (fd >= 0) {
close(fd);
}
if (!result) {
aKext->flags.invalid = 1;
aKext->flags.valid = 0;
}
return result;
}
Boolean __OSKextProcessInfoDictionary(
OSKextRef aKext,
CFBundleRef kextBundle)
{
Boolean result = false;
Boolean valueIsNonnil;
Boolean checkResult;
CFMutableArrayRef propPath = NULL; CFStringRef propKey = NULL; CFBooleanRef boolValue = NULL; CFStringRef stringValue = NULL; CFDictionaryRef dictValue = NULL; CFTypeRef debugLevel = NULL; Boolean isInterfaceSetFalse = false;
OSKextVersion bundleVersion = -1;
OSKextVersion compatibleVersion = -1;
__OSKextRemoveKextFromIdentifierDict(aKext, __sOSKextsByIdentifier);
if (!__OSKextReadInfoDictionary(aKext, kextBundle)) {
goto finish;
}
result = true;
propKey = _kCFBundlePackageTypeKey;
checkResult = __OSKextCheckProperty(aKext,
aKext->infoDictionary,
propKey,
propKey,
CFStringGetTypeID(),
__sOSKextPackageTypeValues,
true,
true,
true,
NULL,
NULL);
result = result && checkResult;
if (!checkResult) {
goto finish;
}
propKey = kCFBundleIdentifierKey;
checkResult = __OSKextCheckProperty(aKext,
aKext->infoDictionary,
propKey,
propKey,
CFStringGetTypeID(),
NULL,
true,
true,
true,
(CFTypeRef *)&stringValue,
NULL);
result = result && checkResult;
if (checkResult && stringValue) {
SAFE_RELEASE_NULL(aKext->bundleID);
if (stringValue) {
aKext->bundleID = CFRetain(stringValue);
}
if (CFStringGetLength(stringValue) > KMOD_MAX_NAME - 1) {
__OSKextSetDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticIdentifierOrVersionTooLongKey);
result = false;
}
} else {
aKext->bundleID = CFSTR(__kOSKextUnknownIdentifier);
}
propKey = kCFBundleVersionKey;
checkResult = __OSKextCheckProperty(aKext,
aKext->infoDictionary,
propKey,
propKey,
CFStringGetTypeID(),
NULL,
true,
true,
false, (CFTypeRef *)&stringValue,
NULL);
result = result && checkResult;
if (checkResult && stringValue) {
if (CFStringGetLength(stringValue) > KMOD_MAX_NAME - 1) {
__OSKextSetDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticIdentifierOrVersionTooLongKey);
result = false;
} else {
bundleVersion = OSKextParseVersionCFString(stringValue);
if (bundleVersion == -1) {
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticPropertyIsIllegalValueKey, kCFBundleVersionKey,
NULL);
result = false;
}
aKext->version = bundleVersion;
}
}
propKey = CFSTR(kOSBundleCompatibleVersionKey);
checkResult = __OSKextCheckProperty(aKext,
aKext->infoDictionary,
propKey,
propKey,
CFStringGetTypeID(),
NULL,
false,
true,
false, (CFTypeRef *)&stringValue,
NULL);
result = result && checkResult;
if (checkResult && stringValue) {
compatibleVersion = OSKextParseVersionCFString(stringValue);
if (compatibleVersion == -1) {
result = false;
}
aKext->compatibleVersion = compatibleVersion;
if (compatibleVersion > bundleVersion) {
__OSKextSetDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticCompatibleVersionLaterThanVersionKey);
result = false;
}
}
propKey = CFSTR(kOSBundleIsInterfaceKey);
checkResult = __OSKextCheckProperty(aKext,
aKext->infoDictionary,
propKey,
propKey,
CFBooleanGetTypeID(),
NULL,
false,
true,
false,
(CFTypeRef *)&boolValue,
&valueIsNonnil);
result = result && checkResult;
if (valueIsNonnil) {
aKext->flags.isInterface = 1;
}
if (boolValue) {
isInterfaceSetFalse = !CFBooleanGetValue(boolValue);
}
propKey = CFSTR(kOSKernelResourceKey);
checkResult = __OSKextCheckProperty(aKext,
aKext->infoDictionary,
propKey,
propKey,
CFBooleanGetTypeID(),
NULL,
false,
true,
false,
NULL,
&valueIsNonnil);
result = result && checkResult;
if (valueIsNonnil) {
aKext->flags.isKernelComponent = 1;
}
if (aKext->flags.isKernelComponent) {
if (isInterfaceSetFalse) {
__OSKextSetDiagnostic(aKext, kOSKextDiagnosticsFlagWarnings,
kOSKextDiagnosticKernelComponentNotInterfaceKey);
}
aKext->flags.isInterface = 1;
}
propKey = kCFBundleExecutableKey;
checkResult = __OSKextCheckProperty(aKext,
aKext->infoDictionary,
propKey,
propKey,
CFStringGetTypeID(),
NULL,
false,
true,
true, NULL,
&valueIsNonnil);
result = result && checkResult;
if (valueIsNonnil) {
aKext->flags.declaresExecutable = 1;
}
#if SHARED_EXECUTABLE
propKey = CFSTR(kOSBundleSharedExecutableIdentifierKey);
checkResult = __OSKextCheckProperty(aKext,
aKext->infoDictionary,
propKey,
propKey,
CFStringGetTypeID(),
NULL,
false,
true,
false, NULL,
&valueIsNonnil);
result = result && checkResult;
if (aKext->flags.declaresExecutable) {
if (valueIsNonnil) {
__OSKextSetDiagnostic(aKext, kOSKextDiagnosticsFlagValidation,
kOSKextDiagnosticSharedExecutableAndExecutableKey);
}
} else if (valueIsNonnil) {
aKext->flags.declaresExecutable = 1;
}
#endif
propKey = CFSTR(kOSBundleEnableKextLoggingKey);
checkResult = __OSKextCheckProperty(aKext,
aKext->infoDictionary,
propKey,
propKey,
CFBooleanGetTypeID(),
NULL,
false,
true,
false,
(CFTypeRef *)&boolValue,
&valueIsNonnil);
result = result && checkResult;
if (valueIsNonnil) {
aKext->flags.loggingEnabled = CFBooleanGetValue(boolValue) ? 1 : 0;
aKext->flags.plistHasEnableLoggingSet = CFBooleanGetValue(boolValue) ? 1 : 0;
}
propKey = CFSTR(kOSBundleDebugLevelKey);
debugLevel = OSKextGetValueForInfoDictionaryKey(aKext, propKey);
if (debugLevel) {
__OSKextAddDiagnostic(aKext, kOSKextDiagnosticsFlagWarnings,
kOSKextDiagnosticDeprecatedPropertyKey, CFSTR(kOSBundleDebugLevelKey),
NULL);
}
propKey = CFSTR(kOSBundleRequiredKey);
checkResult = __OSKextCheckProperty(aKext,
aKext->infoDictionary,
propKey,
propKey,
CFStringGetTypeID(),
__sOSKextOSBundleRequiredValues,
false,
true,
false, NULL,
&valueIsNonnil);
result = result && checkResult;
if (valueIsNonnil) {
aKext->flags.isLoadableInSafeBoot = 1;
} else {
if (OSKextGetActualSafeBoot() || OSKextGetSimulatedSafeBoot()) {
__OSKextSetDiagnostic(aKext, kOSKextDiagnosticsFlagBootLevel,
kOSKextDiagnosticIneligibleInSafeBoot);
}
}
propKey = CFSTR(kIOKitPersonalitiesKey);
checkResult = __OSKextCheckProperty(aKext,
aKext->infoDictionary,
propKey,
propKey,
CFDictionaryGetTypeID(),
NULL,
false,
true,
false, (CFTypeRef *)&dictValue,
&valueIsNonnil);
result = result && checkResult;
if (dictValue) {
__OSKextValidateIOKitPersonalityContext validatePersonalitiesContext;
propPath = CFArrayCreateMutable(CFGetAllocator(aKext), 0,
&kCFTypeArrayCallBacks);
if (!propPath) {
OSKextLogMemError();
result = false;
goto finish;
}
CFArrayAppendValue(propPath, propKey);
validatePersonalitiesContext.kext = aKext;
validatePersonalitiesContext.personalities = dictValue;
validatePersonalitiesContext.propPath = propPath;
validatePersonalitiesContext.valid = true; validatePersonalitiesContext.justCheckingIOKitDebug = true;
CFDictionaryApplyFunction(dictValue,
__OSKextValidateIOKitPersonalityApplierFunction,
&validatePersonalitiesContext);
result = result && validatePersonalitiesContext.valid;
CFArrayRemoveValueAtIndex(propPath, CFArrayGetCount(propPath) - 1);
}
finish:
if (!result) {
aKext->flags.invalid = 1;
aKext->flags.valid = 0;
}
(void)__OSKextRecordKextInIdentifierDict(aKext, __sOSKextsByIdentifier);
SAFE_RELEASE(propPath);
return result;
}
#pragma mark Mkext and Prelinked Kernel Files
Boolean OSKextIsFromMkext(OSKextRef aKext)
{
return aKext->staticFlags.isFromMkext ? true : false;
}
#define REQUIRED_MATCH(flags, string, type) \
(((flags) & kOSKextOSBundleRequired ## type ## Flag) && \
string && CFEqual(string, CFSTR(kOSBundleRequired ## type)))
Boolean OSKextMatchesRequiredFlags(OSKextRef aKext,
OSKextRequiredFlags requiredFlags)
{
Boolean result = false;
CFStringRef requiredString = NULL;
requiredString = OSKextGetValueForInfoDictionaryKey(aKext,
CFSTR(kOSBundleRequiredKey));
if (REQUIRED_MATCH(requiredFlags, requiredString, Root) ||
REQUIRED_MATCH(requiredFlags, requiredString, LocalRoot) ||
REQUIRED_MATCH(requiredFlags, requiredString, NetworkRoot) ||
REQUIRED_MATCH(requiredFlags, requiredString, Console) ||
REQUIRED_MATCH(requiredFlags, requiredString, SafeBoot)) {
result = true;
} else if (!requiredFlags) {
result = true;
}
return result;
}
CFArrayRef OSKextFilterRequiredKexts(
CFArrayRef kextArray,
OSKextRequiredFlags requiredFlags)
{
CFMutableArrayRef result = NULL;
CFIndex count, i;
result = CFArrayCreateMutable(CFGetAllocator(kextArray), 0,
&kCFTypeArrayCallBacks);
if (!result) {
OSKextLogMemError();
goto finish;
}
if (!kextArray) {
kextArray = OSKextGetAllKexts();
}
count = CFArrayGetCount(kextArray);
for (i = 0; i < count; i++) {
OSKextRef thisKext = (OSKextRef)CFArrayGetValueAtIndex(kextArray, i);
if (OSKextMatchesRequiredFlags(thisKext, requiredFlags)) {
CFArrayAppendValue(result, thisKext);
}
}
finish:
return result;
}
#define GZIP_WINDOW_OFFSET (16)
Boolean __OSKextAddCompressedFileToMkext(
OSKextRef aKext,
CFMutableDataRef mkextData,
CFDataRef fileData,
Boolean plistFlag,
Boolean * compressed)
{
Boolean result = false;
const UInt8 * fileBuffer = CFDataGetBytePtr(fileData);
UInt8 * mkextBuffer;
mkext2_header * mkextHeader;
CFIndex mkextStartLength = CFDataGetLength(mkextData);
uint32_t fullSize = CFDataGetLength(fileData);
unsigned long compressedSize;
uint32_t compressedSize32;
mkext2_file_entry * entryPtr = NULL;
int zlib_result;
uint32_t entrySize;
UInt8 * compressDest;
z_stream zstream;
Boolean zstream_inited = false;
*compressed = false;
compressedSize = fullSize + ((fullSize+1000)/1000) + 12;
if (plistFlag) {
entrySize = 0;
} else {
entrySize = sizeof(mkext2_file_entry);
}
CFDataSetLength(mkextData,
mkextStartLength + entrySize + compressedSize);
mkextBuffer = CFDataGetMutableBytePtr(mkextData);
if (plistFlag) {
compressDest = mkextBuffer + mkextStartLength;
} else {
entryPtr = (mkext2_file_entry *)(mkextBuffer + mkextStartLength);
entryPtr->full_size = OSSwapHostToBigInt32(fullSize);
compressDest = entryPtr->data;
}
zstream.next_in = (UInt8 *)fileBuffer;
zstream.next_out = compressDest;
zstream.avail_in = fullSize;
zstream.avail_out = compressedSize;
zstream.zalloc = Z_NULL;
zstream.zfree = Z_NULL;
zstream.opaque = Z_NULL;
zlib_result = deflateInit2(&zstream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
15, 8 , Z_DEFAULT_STRATEGY);
if (Z_OK != zlib_result) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"zlib deflateInit failed.");
goto finish;
} else {
zstream_inited = true;
}
zlib_result = deflate(&zstream, Z_FINISH);
if (zlib_result == Z_STREAM_END) {
compressedSize = zstream.total_out;
} else if (zlib_result == Z_OK) {
compressedSize = zstream.total_out;
} else {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"zlib deflate failed.");
goto finish;
}
zlib_result = Z_OK;
compressedSize32 = (uint32_t)compressedSize;
if (Z_OK == zlib_result) {
result = true;
if (compressedSize32 >= fullSize) {
*compressed = false;
} else {
*compressed = true;
if (plistFlag) {
mkextHeader = (mkext2_header *)mkextBuffer;
mkextHeader->plist_offset =
OSSwapHostToBigInt32(mkextStartLength);
mkextHeader->plist_full_size =
OSSwapHostToBigInt32(CFDataGetLength(fileData));
mkextHeader->plist_compressed_size =
OSSwapHostToBigInt32(compressedSize32);
OSKextLog(aKext, kOSKextLogDetailLevel | kOSKextLogArchiveFlag,
"Compressed info dict from %u to %u bytes (%.2f%%).",
fullSize, compressedSize32,
(100.0 * (float)compressedSize32/(float)fullSize));
} else {
entryPtr->compressed_size = OSSwapHostToBigInt32(compressedSize32);
OSKextLog(aKext, kOSKextLogDetailLevel | kOSKextLogArchiveFlag,
"Compressed executable from %u to %u bytes (%.2f%%).",
fullSize, compressedSize32,
(100.0 * (float)compressedSize32/(float)fullSize));
}
CFDataSetLength(mkextData,
mkextStartLength + entrySize + compressedSize32);
}
}
finish:
if (zstream_inited) deflateEnd(&zstream);
if (result != true) {
CFDataSetLength(mkextData, mkextStartLength);
}
return result;
}
Boolean __OSKextAddToMkext(
OSKextRef aKext,
CFMutableDataRef mkextData,
CFMutableArrayRef mkextInfoDictArray,
char * volumePath,
Boolean compressFlag)
{
Boolean result = false;
CFMutableDictionaryRef infoDictionary = NULL; CFStringRef bundlePath = NULL; CFStringRef executableRelPath = NULL; char kextPath[PATH_MAX];
char * kextVolPath = kextPath;
CFDataRef executable = NULL; CFIndex mkextDataStartLength = CFDataGetLength(mkextData);
uint32_t mkextEntryOffset;
CFNumberRef mkextEntryOffsetNum = NULL; mkext2_file_entry entryScratch;
void * mkextEntryFile = NULL; Boolean compressed = false;
__OSKextGetFileSystemPath(aKext, NULL,
true, kextPath);
OSKextLog(aKext, kOSKextLogStepLevel | kOSKextLogArchiveFlag,
"Adding %s to mkext.", kextPath);
infoDictionary = OSKextCopyInfoDictionary(aKext);
if (!infoDictionary) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"Can't get info dictionary for %s.", kextPath);
goto finish;
}
if (aKext->flags.loggingEnabled) {
CFDictionarySetValue(infoDictionary, CFSTR(kOSBundleEnableKextLoggingKey),
kCFBooleanTrue);
}
executable = OSKextCopyExecutableForArchitecture(aKext, OSKextGetArchitecture());
if (!executable && OSKextDeclaresExecutable(aKext)) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"Can't get executable for %s (architecture %s).", kextPath,
OSKextGetArchitecture()->name);
goto finish;
}
if (executable) {
uint32_t entryFileSize;
(void)posix_madvise((void *)CFDataGetBytePtr(executable),
CFDataGetLength(executable),
POSIX_MADV_SEQUENTIAL);
mkextEntryOffset = mkextDataStartLength;
mkextEntryOffsetNum = CFNumberCreate(CFGetAllocator(aKext),
kCFNumberSInt32Type, &mkextEntryOffset);
if (!mkextEntryOffsetNum) {
OSKextLogMemError();
goto finish;
}
CFDictionarySetValue(infoDictionary, CFSTR(kMKEXTExecutableKey),
mkextEntryOffsetNum);
entryFileSize = CFDataGetLength(executable);
entryScratch.full_size = OSSwapHostToBigInt32(entryFileSize);
if (compressFlag && (!__OSKextAddCompressedFileToMkext(aKext,
mkextData, executable, false, &compressed))) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"%s failed to compress executable.", kextPath);
goto finish;
}
if (!compressed) {
mkextEntryFile = (void *)CFDataGetBytePtr(executable);
entryScratch.compressed_size = OSSwapHostToBigInt32(0);
CFDataAppendBytes(mkextData, (const UInt8 *)&entryScratch,
sizeof(entryScratch));
CFDataAppendBytes(mkextData, (const UInt8 *)mkextEntryFile,
entryFileSize);
}
OSKextLog(aKext, kOSKextLogDetailLevel | kOSKextLogArchiveFlag,
"%s added %u-byte %scompressed executable to mkext.",
kextPath, entryFileSize, compressFlag ? "" : "non");
}
if (!__OSKextGetFileSystemPath(aKext, NULL,
true, kextPath)) {
OSKextLogStringError(aKext);
goto finish;
}
kextVolPath = __absPathOnVolume(kextPath, volumePath);
bundlePath = CFStringCreateWithBytes(CFGetAllocator(aKext),
(UInt8 *)kextVolPath, strlen(kextVolPath),
kCFStringEncodingUTF8, false);
if (!bundlePath) {
OSKextLogMemError();
goto finish;
}
CFDictionarySetValue(infoDictionary, CFSTR(kMKEXTBundlePathKey),
bundlePath);
executableRelPath = __OSKextCopyExecutableRelativePath(aKext);
if (executableRelPath) {
CFDictionarySetValue(infoDictionary, CFSTR(kMKEXTExecutableRelativePathKey),
executableRelPath);
}
CFArrayAppendValue(mkextInfoDictArray, infoDictionary);
result = true;
finish:
if (!result) {
CFDataSetLength(mkextData, mkextDataStartLength);
}
if (executable) {
(void)posix_madvise((void *)CFDataGetBytePtr(executable),
CFDataGetLength(executable),
POSIX_MADV_DONTNEED);
}
SAFE_RELEASE(infoDictionary);
SAFE_RELEASE(bundlePath);
SAFE_RELEASE(executableRelPath);
SAFE_RELEASE(executable);
SAFE_RELEASE(mkextEntryOffsetNum);
if (compressed) {
SAFE_FREE(mkextEntryFile);
}
return result;
}
__private_extern__ u_int32_t
mkext_adler32(u_int8_t *buffer, int32_t length)
{
int32_t cnt;
u_int32_t result, lowHalf, highHalf;
lowHalf = 1;
highHalf = 0;
for (cnt = 0; cnt < length; cnt++) {
if ((cnt % 5000) == 0) {
lowHalf %= 65521L;
highHalf %= 65521L;
}
lowHalf += buffer[cnt];
highHalf += lowHalf;
}
lowHalf %= 65521L;
highHalf %= 65521L;
result = (highHalf << 16) | lowHalf;
return result;
}
CFDataRef __OSKextCreateMkext(
CFAllocatorRef allocator,
CFArrayRef kextArray,
CFURLRef volumeRootURL,
OSKextRequiredFlags requiredFlags,
Boolean compressFlag,
Boolean skipLoadedFlag,
CFDictionaryRef loadArgsDict)
{
CFMutableDataRef result = NULL;
CFMutableDataRef mkextData = NULL; mkext2_header mkextHeaderScratch;
mkext2_header * mkextHeader;
void * mkextEnd;
CFMutableDictionaryRef mkextPlist = NULL; CFMutableArrayRef mkextInfoDictArray = NULL; CFDataRef mkextPlistData = NULL; Boolean compressed = false; uint32_t adlerChecksum;
char kextPath[PATH_MAX];
char volumePath[PATH_MAX] = "";
CFIndex count, i, numKexts;
if (!kextArray) {
kextArray = OSKextGetAllKexts();
}
count = CFArrayGetCount(kextArray);
if (!count) {
goto finish;
}
mkextData = CFDataCreateMutable(allocator, 0);
if (!mkextData) {
OSKextLogMemError();
goto finish;
}
mkextPlist = CFDictionaryCreateMutable(allocator, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (!mkextPlist) {
OSKextLogMemError();
goto finish;
}
mkextInfoDictArray = CFArrayCreateMutable(allocator, 0,
&kCFTypeArrayCallBacks);
if (!mkextInfoDictArray) {
OSKextLogMemError();
goto finish;
}
if (volumeRootURL) {
if (!CFURLGetFileSystemRepresentation(volumeRootURL,
TRUE, (UInt8 *)volumePath, sizeof(volumePath))) {
OSKextLogStringError( NULL);
goto finish;
}
}
CFDictionarySetValue(mkextPlist, CFSTR(kMKEXTInfoDictionariesKey),
mkextInfoDictArray);
if (loadArgsDict) {
CFDictionarySetValue(mkextPlist, CFSTR(kKextRequestPredicateKey),
CFSTR(kKextRequestPredicateLoad));
CFDictionarySetValue(mkextPlist, CFSTR(kKextRequestArgumentsKey),
loadArgsDict);
}
CFDataAppendBytes(mkextData, (const UInt8 *)&mkextHeaderScratch,
sizeof(mkextHeaderScratch));
for (i = 0, numKexts = 0; i < count; i++) {
OSKextRef thisKext = (OSKextRef)CFArrayGetValueAtIndex(kextArray, i);
__OSKextGetFileSystemPath(thisKext, NULL,
false, kextPath);
if (!__OSKextIsValid(thisKext)) {
OSKextLog(thisKext,
kOSKextLogStepLevel | kOSKextLogArchiveFlag,
"%s is not valid; omitting from mkext.",
kextPath);
continue;
}
if (skipLoadedFlag && OSKextIsLoaded(thisKext)) {
OSKextLog(thisKext, kOSKextLogDebugLevel | kOSKextLogArchiveFlag,
"Omitting loaded kext %s from mkext for kernel load.",
kextPath);
continue;
}
if (OSKextMatchesRequiredFlags(thisKext, requiredFlags)) {
if (OSKextSupportsArchitecture(thisKext, NULL)) {
if (!__OSKextAddToMkext(thisKext, mkextData,
mkextInfoDictArray, volumePath, compressFlag)) {
goto finish;
}
numKexts++;
} else {
OSKextLog(thisKext, kOSKextLogStepLevel | kOSKextLogArchiveFlag,
"%s does not contain code for architecture %s.",
kextPath, OSKextGetArchitecture()->name);
}
}
}
mkextPlistData = IOCFSerialize(mkextPlist, kNilOptions);
if (!mkextPlistData) {
goto finish;
}
compressed = false;
if (compressFlag && !__OSKextAddCompressedFileToMkext( NULL,
mkextData, mkextPlistData, true, &compressed)) {
goto finish;
}
if (!compressed) {
mkextHeader = (mkext2_header *)CFDataGetMutableBytePtr(mkextData);
mkextHeader->plist_offset =
OSSwapHostToBigInt32(CFDataGetLength(mkextData));
mkextHeader->plist_compressed_size =
OSSwapHostToBigInt32(0);
mkextHeader->plist_full_size =
OSSwapHostToBigInt32(CFDataGetLength(mkextPlistData));
CFDataAppendBytes(mkextData, CFDataGetBytePtr(mkextPlistData),
CFDataGetLength(mkextPlistData));
}
mkextHeader = (mkext2_header *)CFDataGetMutableBytePtr(mkextData);
mkextHeader->magic = OSSwapHostToBigInt32(MKEXT_MAGIC);
mkextHeader->signature = OSSwapHostToBigInt32(MKEXT_SIGN);
mkextHeader->length = OSSwapHostToBigInt32(CFDataGetLength(mkextData));
mkextHeader->version = OSSwapHostToBigInt32(MKEXT_VERS_2);
mkextHeader->numkexts = OSSwapHostToBigInt32(numKexts);
mkextHeader->cputype = OSSwapHostToBigInt32(OSKextGetArchitecture()->cputype);
mkextHeader->cpusubtype = OSSwapHostToBigInt32(OSKextGetArchitecture()->cpusubtype);
mkextEnd = ((void *)mkextHeader + CFDataGetLength(mkextData));
adlerChecksum = mkext_adler32((uint8_t *)&(mkextHeader->version),
mkextEnd - (void *)&(mkextHeader->version));
mkextHeader->adler32 = OSSwapHostToBigInt32(adlerChecksum);
result = mkextData;
CFRetain(result);
OSKextLog( NULL, kOSKextLogProgressLevel | kOSKextLogArchiveFlag,
"Created mkext for architecture %s containing %u kexts.",
__sOSKextArchInfo->name, (int)numKexts);
finish:
SAFE_RELEASE(mkextInfoDictArray);
SAFE_RELEASE(mkextPlist);
SAFE_RELEASE(mkextData);
SAFE_RELEASE(mkextPlistData);
return result;
}
CFDataRef OSKextCreateMkext(
CFAllocatorRef allocator,
CFArrayRef kextArray,
CFURLRef volumeRootURL,
OSKextRequiredFlags requiredFlags,
Boolean compressFlag)
{
return __OSKextCreateMkext(allocator,
kextArray, volumeRootURL, requiredFlags,
compressFlag, false, NULL);
}
CFDataRef __OSKextUncompressMkext2FileData(
CFAllocatorRef allocator,
const UInt8 * buffer,
uint32_t compressedSize,
uint32_t fullSize)
{
CFDataRef result = NULL;
CFDataRef createdData = NULL; uint32_t uncompressedSize;
uint8_t * uncompressedData = NULL; int zlib_result;
z_stream zstream;
Boolean zstream_inited = false;
if (!compressedSize) {
createdData = CFDataCreate(allocator, buffer, fullSize);
if (createdData) {
UInt8 * dataBuffer = (UInt8 *)CFDataGetBytePtr(createdData);
if (!dataBuffer) {
goto finish;
}
result = createdData;
}
}
uncompressedData = (void *)malloc(fullSize);
if (!uncompressedData) {
OSKextLogMemError();
goto finish;
}
zstream.next_in = (UInt8 *)buffer;
zstream.next_out = uncompressedData;
zstream.avail_in = compressedSize;
zstream.avail_out = fullSize;
zstream.zalloc = Z_NULL;
zstream.zfree = Z_NULL;
zstream.opaque = Z_NULL;
zlib_result = inflateInit(&zstream);
if (Z_OK != zlib_result) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"zlib inflateInit failed.");
goto finish;
} else {
zstream_inited = true;
}
zlib_result = inflate(&zstream, Z_FINISH);
if (zlib_result == Z_STREAM_END) {
uncompressedSize = zstream.total_out;
} else if (zlib_result == Z_OK) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"zlib inflate discrepancy, uncompressed size != original size.");
goto finish;
} else {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"zlib inflate failed: %s.",
zstream.msg ? zstream.msg : "unknown");
goto finish;
}
if (uncompressedSize != fullSize) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"zlib inflate discrepancy, uncompressed size != original size.");
goto finish;
}
result = CFDataCreateWithBytesNoCopy(allocator,
uncompressedData, fullSize, kCFAllocatorMalloc);
if (!result) {
OSKextLogMemError();
}
finish:
if (zstream_inited) inflateEnd(&zstream);
if (!result) {
SAFE_FREE(uncompressedData);
SAFE_RELEASE(createdData);
}
return result;
}
CFArrayRef OSKextCreateKextsFromMkextFile(CFAllocatorRef allocator,
CFURLRef anURL)
{
CFArrayRef result = NULL;
CFDataRef mkextData = NULL;
pthread_once(&__sOSKextInitialized, __OSKextInitialize);
if (!CFURLCreateDataAndPropertiesFromResource(allocator, anURL,
&mkextData, NULL, NULL,
NULL)) {
OSKextLogMemError();
goto finish;
}
result = __OSKextCreateKextsFromMkext(allocator, mkextData, anURL);
if (!result) {
goto finish;
}
finish:
SAFE_RELEASE(mkextData);
return result;
}
CFArrayRef OSKextCreateKextsFromMkextData(CFAllocatorRef allocator,
CFDataRef mkextData)
{
pthread_once(&__sOSKextInitialized, __OSKextInitialize);
return __OSKextCreateKextsFromMkext(allocator, mkextData, NULL);
}
CFArrayRef __OSKextCreateKextsFromMkext(
CFAllocatorRef allocator,
CFDataRef mkextData,
CFURLRef mkextURL)
{
CFMutableArrayRef result = NULL;
CFMutableArrayRef kexts = NULL; uint32_t magic;
fat_iterator fatIterator = NULL; mkext2_header * mkextHeader;
void * mkextEnd;
CFDictionaryRef mkextPlist = NULL; CFArrayRef mkextInfoDictArray = NULL; uint32_t adlerChecksum;
uint32_t mkextPlistOffset;
uint32_t mkextPlistCompressedSize;
uint32_t mkextPlistFullSize;
CFStringRef errorString = NULL; char * errorCString = NULL; CFDataRef mkextPlistUncompressedData = NULL; const char * mkextPlistDataBuffer = NULL; CFIndex count, i;
pthread_once(&__sOSKextInitialized, __OSKextInitialize);
kexts = CFArrayCreateMutable(allocator, 0,
&kCFTypeArrayCallBacks);
if (!kexts) {
OSKextLogMemError();
goto finish;
}
magic = MAGIC32(CFDataGetBytePtr(mkextData));
if (ISFAT(magic)) {
fatIterator = fat_iterator_for_data(CFDataGetBytePtr(mkextData),
CFDataGetBytePtr(mkextData) + CFDataGetLength(mkextData),
1 );
if (!fatIterator) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"Can't read mkext fat header.");
goto finish;
}
mkextHeader = fat_iterator_find_arch(fatIterator,
OSKextGetArchitecture()->cputype,
OSKextGetArchitecture()->cpusubtype,
&mkextEnd);
if (!mkextHeader) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"Architecture %s not found in mkext.",
OSKextGetArchitecture()->name);
goto finish;
}
} else {
mkextHeader = (mkext2_header *)CFDataGetBytePtr(mkextData);
mkextEnd = (char *)mkextHeader + CFDataGetLength(mkextData);
}
if ((MKEXT_GET_MAGIC(mkextHeader) != MKEXT_MAGIC) ||
(MKEXT_GET_SIGNATURE(mkextHeader) != MKEXT_SIGN)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"Bad mkext magic/signature.");
goto finish;
}
if ((int32_t)OSSwapBigToHostInt32(mkextHeader->length) !=
((char *)mkextEnd - (char *)mkextHeader)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"Mkext length field %d does not match mkext actual size %d.",
OSSwapBigToHostInt32(mkextHeader->length),
(int)((char *)mkextEnd - (char *)mkextHeader));
goto finish;
}
if ((OSSwapBigToHostInt32(mkextHeader->version) != MKEXT_VERS_2)) {
OSKextLog( NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"Unsupported mkext version 0x%x.",
OSSwapBigToHostInt32(mkextHeader->version));
goto finish;
}
mkextEnd = ((void *)mkextHeader + CFDataGetLength(mkextData));
adlerChecksum = mkext_adler32((uint8_t *)&(mkextHeader->version),
mkextEnd - (void *)&(mkextHeader->version));
if (OSSwapBigToHostInt32(mkextHeader->adler32) != adlerChecksum) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"Mkext checksum error.");
goto finish;
}
mkextPlistOffset = OSSwapBigToHostInt32(mkextHeader->plist_offset);
mkextPlistCompressedSize =
OSSwapBigToHostInt32(mkextHeader->plist_compressed_size);
OSKextLog( NULL,
kOSKextLogDebugLevel | kOSKextLogFileAccessFlag,
"Mkext plist compressed size is %u.", mkextPlistCompressedSize);
mkextPlistFullSize = OSSwapBigToHostInt32(mkextHeader->plist_full_size);
OSKextLog( NULL,
kOSKextLogDebugLevel | kOSKextLogFileAccessFlag,
"Mkext plist full size is %u.", mkextPlistFullSize);
if (mkextPlistCompressedSize) {
mkextPlistUncompressedData = __OSKextUncompressMkext2FileData(
CFGetAllocator(mkextData),
(const UInt8 *)mkextHeader + mkextPlistOffset,
mkextPlistCompressedSize, mkextPlistFullSize);
if (!mkextPlistUncompressedData) {
goto finish;
}
mkextPlistDataBuffer = (const char *)
CFDataGetBytePtr(mkextPlistUncompressedData);
} else {
mkextPlistDataBuffer = (const char *)mkextHeader + mkextPlistOffset;
}
mkextPlist = IOCFUnserialize(
mkextPlistDataBuffer, allocator,
kNilOptions, &errorString);
if (!mkextPlist || (CFGetTypeID(mkextPlist) != CFDictionaryGetTypeID())) {
errorCString = createUTF8CStringForCFString(errorString);
OSKextLog( NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"Failed to read XML from mkext: %s.",
errorCString ? errorCString : "(unknown error)");
goto finish;
}
mkextInfoDictArray = (CFArrayRef)CFDictionaryGetValue(
mkextPlist, CFSTR(kMKEXTInfoDictionariesKey));
if (!mkextInfoDictArray ||
(CFGetTypeID(mkextInfoDictArray) != CFArrayGetTypeID())) {
OSKextLog( NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"Mkext plist has no kexts.");
goto finish;
}
count = CFArrayGetCount(mkextInfoDictArray);
for (i = 0; i < count; i++) {
CFDictionaryRef infoDict =
(CFDictionaryRef)CFArrayGetValueAtIndex(mkextInfoDictArray, i);
OSKextRef aKext = __OSKextAlloc(allocator, NULL);
if (!aKext) {
OSKextLogMemError();
goto finish;
}
if (!__OSKextInitFromMkext(aKext, infoDict, mkextURL, mkextData)) {
CFRelease(aKext);
goto finish;
}
CFArrayAppendValue(kexts, aKext);
}
result = kexts;
CFRetain(result);
finish:
SAFE_RELEASE(kexts);
SAFE_RELEASE(errorString);
SAFE_RELEASE(mkextPlist);
SAFE_RELEASE(mkextPlistUncompressedData);
SAFE_FREE(errorCString);
if (fatIterator) fat_iterator_close(fatIterator);
return result;
}
CFDataRef __OSKextExtractMkext2FileEntry(
OSKextRef aKext,
CFDataRef mkextData,
CFNumberRef offsetNum,
CFStringRef filename) {
CFDataRef result = NULL;
const UInt8 * mkext = CFDataGetBytePtr(mkextData);
uint32_t entryOffset;
mkext2_file_entry * fileEntry;
uint32_t fullSize;
uint32_t compressedSize;
char * filenameCString = NULL; char mkextPath[PATH_MAX] = "";
if (aKext->mkextInfo->mkextURL) {
__OSKextGetFileSystemPath( NULL,
aKext->mkextInfo->mkextURL,
false, mkextPath);
}
if (filename) {
filenameCString = createUTF8CStringForCFString(filename);
}
OSKextLog(aKext,
kOSKextLogDetailLevel | kOSKextLogArchiveFlag,
"Extracting %s%s from %s.",
(filenameCString ? "resource file " : "executable"),
(filenameCString ? filenameCString : ""),
(mkextPath[0] ? mkextPath : "mkext data"));
if (!CFNumberGetValue(offsetNum, kCFNumberSInt32Type,
(SInt32 *)&entryOffset)) {
goto finish;
}
fileEntry = (mkext2_file_entry *)(mkext + entryOffset);
fullSize = OSSwapBigToHostInt32(fileEntry->full_size);
compressedSize = OSSwapBigToHostInt32(fileEntry->compressed_size);
if (compressedSize) {
result = __OSKextUncompressMkext2FileData(CFGetAllocator(aKext),
fileEntry->data,
compressedSize, fullSize);
if (!result) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"Failed to uncompress %s%s from %s.",
(filenameCString ? "resource file " : "executable"),
(filenameCString ? filenameCString : ""),
(mkextPath[0] ? mkextPath : "mkext data"));
}
goto finish;
} else {
result = CFDataCreate(CFGetAllocator(aKext), fileEntry->data, fullSize);
}
finish:
SAFE_FREE(filenameCString);
return result;
}
#ifndef IOKIT_EMBEDDED
static boolean_t __OSKextSwapHeaders(
CFDataRef kernelImage)
{
u_char *file = (u_char *) CFDataGetBytePtr(kernelImage);
return macho_swap(file);
}
static boolean_t __OSKextUnswapHeaders(
CFDataRef kernelImage)
{
u_char *file = (u_char *) CFDataGetBytePtr(kernelImage);
return macho_unswap(file);
}
static boolean_t __OSKextGetLastKernelLoadAddr(
CFDataRef kernelImage,
uint64_t * lastLoadAddrOut)
{
boolean_t result = false;
const UInt8 * kernelImagePtr = CFDataGetBytePtr(kernelImage);
uint64_t lastLoadAddr = 0;
uint64_t i;
if (ISMACHO64(MAGIC32(kernelImagePtr))) {
struct mach_header_64 * kernel_header =
(struct mach_header_64 *)kernelImagePtr;
struct segment_command_64 * seg_cmd = NULL;
seg_cmd = (struct segment_command_64 *)
((uintptr_t)kernel_header + sizeof(*kernel_header));
for (i = 0; i < kernel_header->ncmds; i++){
if (seg_cmd->cmd == LC_SEGMENT_64) {
if (seg_cmd->vmaddr + seg_cmd->vmsize > lastLoadAddr) {
lastLoadAddr = seg_cmd->vmaddr + seg_cmd->vmsize;
}
}
seg_cmd = (struct segment_command_64 *)
((uintptr_t)seg_cmd + seg_cmd->cmdsize);
}
} else {
struct mach_header * kernel_header =
(struct mach_header *)kernelImagePtr;
struct segment_command * seg_cmd = NULL;
seg_cmd = (struct segment_command *)
((uintptr_t)kernel_header + sizeof(*kernel_header));
for (i = 0; i < kernel_header->ncmds; i++){
if (seg_cmd->cmd == LC_SEGMENT) {
if (seg_cmd->vmaddr + seg_cmd->vmsize > lastLoadAddr) {
lastLoadAddr = seg_cmd->vmaddr + seg_cmd->vmsize;
}
}
seg_cmd = (struct segment_command *)
((uintptr_t)seg_cmd + seg_cmd->cmdsize);
}
}
if (lastLoadAddrOut) *lastLoadAddrOut = lastLoadAddr;
result = true;
return result;
}
static boolean_t __OSKextSetLinkInfo(
OSKextRef aKext,
plkInfo * plkInfo,
CFDataRef kextExecutable)
{
boolean_t result = false;
boolean_t swapped = false;
boolean_t isSplitKext = false;
struct mach_header_64 * kextHeader;
uint32_t maxAlign;
uint64_t next_vmaddr;
if (!__OSKextCreateLoadInfo(aKext)) {
goto finish;
}
{
char *kextID = NULL;
kextID = createUTF8CStringForCFString(aKext->bundleID);
kcgen_verboseLog("processing %s", kextID);
SAFE_FREE(kextID);
}
kextHeader = (struct mach_header_64 *)CFDataGetBytePtr(kextExecutable);
isSplitKext = __OSKextIsSplitKextMacho64(kextHeader);
swapped = __OSKextSwapHeaders(kextExecutable);
if (OSKextIsInterface(aKext)) {
maxAlign = KEXT_MIN_ALIGN;
aKext->loadInfo->linkedExecutable = CFRetain(kextExecutable);
aKext->loadInfo->prelinkedExecutable = CFRetain(kextExecutable);
aKext->loadInfo->linkInfo.linkedKext = (u_char *) CFDataGetBytePtr(kextExecutable);
aKext->loadInfo->linkInfo.linkedKextSize = CFDataGetLength(kextExecutable);
next_vmaddr = __OSKextAlignAddress(getKCPlkSegNextVMAddr(plkInfo, SEG_IDX_TEXT), maxAlign);
aKext->loadInfo->linkInfo.vmaddr_TEXT = next_vmaddr;
if (nSplitKexts) {
setKCPlkSegNextVMAddr(plkInfo, SEG_IDX_TEXT, next_vmaddr + sizeof(kextHeader) + kextHeader->sizeofcmds);
} else {
setKCPlkSegNextVMAddr(plkInfo, SEG_IDX_TEXT, next_vmaddr + roundPageCrossSafe(aKext->loadInfo->linkInfo.linkedKextSize));
}
result = true;
goto finish;
}
aKext->loadInfo->linkInfo.kextExecutable = (u_char *) CFDataGetBytePtr(kextExecutable);
aKext->loadInfo->linkInfo.kextSize = CFDataGetLength(kextExecutable);
if (!nSplitKexts) {
uint64_t vmaddr;
vmaddr = getKCPlkSegNextVMAddr(plkInfo, SEG_IDX_TEXT);
setKextVMAddr(aKext, SEG_IDX_TEXT, vmaddr);
result = true;
goto finish;
}
for(enum enumSegIdx segIndex = SEG_IDX_TEXT; segIndex < SEG_IDX_COUNT; segIndex++) {
uint64_t my_vmaddr, my_vmsize, my_fileoff, my_filesize, my_maxalign, next_vmaddr;
char *segName = segIdxToName(segIndex);
result = __OSKextGetSegmentInfo(aKext->loadInfo->linkInfo.kextExecutable, segName, &my_vmaddr, &my_vmsize, &my_fileoff, &my_filesize, &my_maxalign, true);
if (result == false)
continue;
kcgen_verboseLog("segName %s vmaddr %llx vmsize %llx fileoff %llx filesize %llx maxalign %llx", segName, my_vmaddr, my_vmsize, my_fileoff, my_filesize, my_maxalign);
{
assert(__OSKextIsArchitectureLP64());
struct segment_command_64 *seg = macho_get_segment_by_name_64(kextHeader, segName);
assert(seg);
seg->vmsize = my_vmsize;
seg->filesize = my_filesize;
}
next_vmaddr = __OSKextAlignAddress(getKCPlkSegNextVMAddr(plkInfo, segIndex), my_maxalign);
kcgen_verboseLog("segName %s new vmaddr %llx", segName, next_vmaddr);
setKextVMAddr(aKext, segIndex, next_vmaddr);
setKCPlkSegNextVMAddr(plkInfo, segIndex, next_vmaddr + my_vmsize);
}
if (__OSKextValidatePLKInfo(aKext, kextHeader, plkInfo) == false) {
abort();
goto finish;
}
result = true;
finish:
if (swapped) {
__OSKextUnswapHeaders(kextExecutable);
}
return result;
}
static boolean_t __OSKextValidatePLKInfo(
OSKextRef aKext,
struct mach_header_64 *kextHeader,
plkInfo *plkInfo )
{
boolean_t result = true;
boolean_t isSplitKext = false;
plkSegInfo *plkSeg[SEG_IDX_COUNT] = { NULL };
char kextPath[PATH_MAX];
__OSKextGetFileSystemPath(aKext, NULL, true, kextPath);
isSplitKext = __OSKextIsSplitKextMacho64(kextHeader);
plkSeg[SEG_IDX_TEXT] = &plkInfo->plk_TEXT;
if (isSplitKext) {
plkSeg[SEG_IDX_TEXT_EXEC] = &plkInfo->plk_TEXT_EXEC;
plkSeg[SEG_IDX_DATA] = &plkInfo->plk_DATA;
plkSeg[SEG_IDX_DATA_CONST] = &plkInfo->plk_DATA_CONST;
plkSeg[SEG_IDX_LINKEDIT] = &plkInfo->plk_LINKEDIT;
plkSeg[SEG_IDX_LLVM_COV] = &plkInfo->plk_LLVM_COV;
}
for (size_t i = 0; i < sizeof(plkSeg)/sizeof(plkSeg[0]); i++) {
if (!plkSeg[i])
break;
SegInfo *segInfo = &plkSeg[i]->plkSegInfo;
uint64_t seg_vmoff = plkSeg[i]->plk_next_kext_vmaddr - segInfo->vmaddr;
uint64_t seg_fileoff = segInfo->fileoff + seg_vmoff;
if (segInfo->vmaddr > plkSeg[i]->plk_next_kext_vmaddr) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s vmaddr %p larger than plk_next_kext_vmaddr %p for %s",
plkSeg[i]->plk_seg_name,
(void *)segInfo->vmaddr,
(void *)plkSeg[i]->plk_next_kext_vmaddr,
kextPath);
result = false;
}
if (segInfo->vmaddr + segInfo->vmsize < plkSeg[i]->plk_next_kext_vmaddr) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s overflow! plk_next_kext_vmaddr %p past end of segment %p for %s",
plkSeg[i]->plk_seg_name,
(void *)plkSeg[i]->plk_next_kext_vmaddr,
(void *)(segInfo->vmaddr + segInfo->vmsize),
kextPath);
result = false;
}
if (segInfo->fileoff > seg_fileoff) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s fileoff %llu larger than plk_next_kext_fileoff %llu for %s",
plkSeg[i]->plk_seg_name,
segInfo->fileoff,
seg_fileoff,
kextPath);
result = false;
}
if (segInfo->fileoff + segInfo->vmsize < seg_fileoff) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s overflow! plk_next_kext_fileoff %llu past end of segment %llu for %s",
plkSeg[i]->plk_seg_name,
seg_fileoff,
(segInfo->fileoff + segInfo->vmsize),
kextPath);
result = false;
}
if (result == false) {
__OSKextShowPLKInfo(plkInfo);
break;
}
}
return result;
}
static boolean_t __OSKextOldCopyToPLK(OSKextRef aKext, plkInfo *plkInfo, Boolean stripSymbolsFlag __unused) {
uint64_t vmsize;
u_char *prelinkData = CFDataGetMutableBytePtr(plkInfo->kernelCacheImage);
CFIndex prelinkDataSize = CFDataGetLength(plkInfo->kernelCacheImage);
uint64_t plkSegVMBase = plkInfo->plk_TEXT.plkSegInfo.vmaddr;
uint64_t plkSegFileBase = plkInfo->plk_TEXT.plkSegInfo.fileoff;
uint64_t vmoff = aKext->loadInfo->linkInfo.vmaddr_TEXT - plkSegVMBase;
uint64_t fileoff = plkSegFileBase + vmoff;
vmsize = aKext->loadInfo->linkInfo.linkedKextSize;
if (OSKextIsInterface(aKext)) {
memcpy(prelinkData + fileoff,
aKext->loadInfo->linkInfo.linkedKext,
vmsize);
return true;
}
if (stripSymbolsFlag) {
aKext->loadInfo->prelinkedExecutable = __OSKextCopyStrippedExecutable(aKext);
if (aKext->loadInfo->prelinkedExecutable == NULL) {
char tempStr[1024];
CFStringGetCString(aKext->bundleID, tempStr, sizeof(tempStr),
kCFStringEncodingUTF8);
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogLinkFlag,
"Can't strip executable for %s <%s>", tempStr, __func__);
aKext->loadInfo->prelinkedExecutable = CFRetain(aKext->loadInfo->linkedExecutable);
} else {
aKext->loadInfo->linkInfo.linkedKextSize = CFDataGetLength(aKext->loadInfo->prelinkedExecutable);
aKext->loadInfo->linkInfo.linkedKext = (u_char *)CFDataGetBytePtr(aKext->loadInfo->prelinkedExecutable);
{
u_char *file = (u_char *) CFDataGetMutableBytePtr((CFMutableDataRef) aKext->loadInfo->prelinkedExecutable);
Boolean is_32bit_kext = ((struct mach_header *) file)->magic == MH_MAGIC;
u_int64_t kmodInfo_offset = aKext->loadInfo->kmodInfoAddress - aKext->loadInfo->linkInfo.vmaddr_TEXT;
vm_size_t *size_field = (vm_size_t *) (is_32bit_kext ?
(file + kmodInfo_offset + offsetof(kmod_info_32_v1_t, size)) :
(file + kmodInfo_offset + offsetof(kmod_info_64_v1_t, size)));
*size_field = aKext->loadInfo->linkInfo.linkedKextSize;
}
}
}
if (!aKext->loadInfo->prelinkedExecutable) {
aKext->loadInfo->prelinkedExecutable = CFRetain(aKext->loadInfo->linkedExecutable);
}
vmsize = roundPageCrossSafe(aKext->loadInfo->linkInfo.linkedKextSize);
if (fileoff + vmsize > (uint64_t) prelinkDataSize) {
OSKextLog(NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s %d - overflow !!! plk_TEXT.plk_next_kext_fileoff %llu + vmsize %llu > prelinkDataSize %ld ",
__func__, __LINE__,
fileoff,
vmsize,
prelinkDataSize);
return false;
}
if (vmoff + vmsize > plkInfo->plk_TEXT.plkSegInfo.vmsize) {
OSKextLog(NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s %d - overflow !!! vmsize %llu > plk_TEXT.plkSegInfo.filesize %llu ",
__func__, __LINE__,
vmsize,
plkInfo->plk_TEXT.plkSegInfo.vmsize);
return false;
}
kcgen_verboseLog("copying to fileoff %llu sz %llu", fileoff, vmsize);
memcpy(prelinkData + fileoff,
aKext->loadInfo->linkInfo.linkedKext,
vmsize);
plkInfo->plk_TEXT.plk_next_kext_vmaddr += vmsize;
return true;
}
static void __OSKextStripMachHeader64(struct mach_header_64 *mh, uint64_t le_fileoff)
{
struct segment_command_64 * le_seg;
struct section_64 *sect;
le_seg = macho_get_segment_by_name_64(mh, SEG_LINKEDIT);
if (NULL == le_seg)
return;
le_seg->fileoff = le_fileoff;
le_seg->filesize = 0;
le_seg->vmsize = 0;
if (le_seg->nsects > 0) {
sect = (struct section_64 *)(&le_seg[1]);
for (uint32_t j = 0; j < le_seg->nsects; ++j, ++sect) {
sect->addr = le_seg->vmaddr;
sect->size = 0;
sect->offset = le_seg->fileoff;
sect->reloff = sect->nreloc = 0;
}
}
size_t offset = sizeof(*mh);
for (unsigned i = 0; i < mh->ncmds; ++i) {
struct load_command *lc = (struct load_command *)((char *)mh + offset);
switch (lc->cmd) {
case LC_SYMTAB: {
struct symtab_command *symtab = (struct symtab_command *)lc;
symtab->symoff = symtab->nsyms = symtab->stroff = symtab->strsize = 0;
} break;
case LC_DYSYMTAB: {
struct dysymtab_command *dstab= (struct dysymtab_command *)lc;
dstab->ilocalsym = dstab->nlocalsym = 0;
dstab->iextdefsym = dstab->nextdefsym =0;
dstab->iundefsym = dstab->nundefsym = 0;
dstab->tocoff = dstab->ntoc = 0;
dstab->modtaboff = dstab->nmodtab = 0;
dstab->extrefsymoff = dstab->nextrefsyms = 0;
dstab->indirectsymoff = dstab->nindirectsyms = 0;
dstab->extreloff = dstab->nextrel = 0;
dstab->locreloff = dstab->nlocrel = 0;
} break;
case LC_SEGMENT_SPLIT_INFO: {
struct linkedit_data_command *ldc = (struct linkedit_data_command *)lc;
ldc->dataoff = ldc->datasize = 0;
} break;
case LC_SEGMENT_64: {
struct segment_command_64 *seg = (struct segment_command_64 *)lc;
sect = (struct section_64 *)(&seg[1]);
for (uint32_t j = 0; j < seg->nsects; ++j, ++sect) {
sect->reloff = sect->nreloc = 0;
}
} break;
default:
break;
}
offset += lc->cmdsize;
}
return;
}
static boolean_t __OSKextCopyToPLK(OSKextRef aKext,
plkInfo *plkInfo,
u_char *prelinkData,
uint64_t prelinkDataSize,
uint64_t *in_orig_vmaddrs,
uint64_t *in_new_vmaddrs,
uint64_t *in_kc_offs,
uint64_t *out_sizes,
Boolean stripSymbolsFlag)
{
boolean_t isSplitKext;
struct mach_header_64 *mh;
splitKextLinkInfo *linkInfo;
uint32_t mh_size, mh_size_round;
int result;
uint64_t segCacheFileOffsets[SEG_IDX_COUNT];
uint64_t segCacheFileSizes[SEG_IDX_COUNT];
uint64_t segOrigStartAddresses[SEG_IDX_COUNT];
uint64_t segNewStartAddresses[SEG_IDX_COUNT];
uint64_t segKCOffsets[SEG_IDX_COUNT];
uintptr_t *pointersForASLR = NULL;
size_t numPointers = 0;
unsigned int adjustSegment;
if (!aKext || !in_kc_offs || !in_new_vmaddrs)
return false;
isSplitKext = __OSKextIsSplitKext(aKext);
linkInfo = &aKext->loadInfo->linkInfo;
#if SPLIT_KEXTS_DEBUG
char kextPath[PATH_MAX];
__OSKextGetFileSystemPath(aKext, NULL, false, kextPath);
__OSKextShowMachoHeader(linkInfo->linkedKext, linkInfo->linkedKextSize);
#endif
mh = (struct mach_header_64 *)linkInfo->linkedKext;
if (OSKextIsInterface(aKext)) {
mh_size = sizeof(struct mach_header_64) + mh->sizeofcmds;
mh_size_round = __OSKextAlignAddress(mh_size, 6);
memcpy(prelinkData + in_kc_offs[SEG_IDX_TEXT], linkInfo->linkedKext, mh_size_round);
out_sizes[SEG_IDX_TEXT] = mh_size_round;
goto finish;
}
if (isSplitKext) {
__OSKextMachOSetSegmentProtection(mh, SEG_TEXT, PROT_READ, PROT_READ);
__OSKextMachOSetSegmentProtection(mh, SEG_TEXT_EXEC, PROT_READ | PROT_EXEC, PROT_READ | PROT_EXEC);
__OSKextMachOSetSegmentProtection(mh, SEG_DATA, PROT_READ | PROT_WRITE, PROT_READ | PROT_WRITE);
__OSKextMachOSetSegmentProtection(mh, SEG_DATA_CONST, PROT_READ, PROT_READ);
__OSKextMachOSetSegmentProtection(mh, SEG_LLVM_COV, PROT_READ, PROT_READ | PROT_WRITE);
__OSKextMachOSetSegmentProtection(mh, SEG_LINKEDIT, PROT_READ, PROT_READ);
}
adjustSegment = 0;
for (unsigned int segIndex = 0; segIndex < SEG_IDX_COUNT; segIndex++) {
uint64_t my_vmaddr, my_vmsize, my_fileoff, my_filesize, my_maxalign;
uint64_t segEnd;
char *segName;
segKCOffsets[segIndex] = in_kc_offs[segIndex];
segCacheFileOffsets[adjustSegment] = in_kc_offs[segIndex];
segOrigStartAddresses[adjustSegment] = in_orig_vmaddrs[segIndex];
segNewStartAddresses[adjustSegment] = in_new_vmaddrs[segIndex];
segName = segIdxToName(segIndex);
assert(segName);
result = __OSKextGetSegmentInfo(linkInfo->linkedKext, segName, &my_vmaddr, &my_vmsize, &my_fileoff, &my_filesize, &my_maxalign, true);
if (result == false) {
kcgen_verboseLog("Kext doesn't contain '%s' segment: skipping.", segName);
continue;
}
assert(my_vmsize == my_filesize);
if (!isSplitKext) {
my_vmsize = linkInfo->linkedKextSize;
my_filesize = my_vmsize;
}
segEnd = in_kc_offs[segIndex] + my_filesize;
assert(segEnd < prelinkDataSize);
kcgen_verboseLog("segName %s kc_off %llx vmaddr %llx vmsize %llx fileoff %llx filesize %llx",
segName, in_kc_offs[segIndex], my_vmaddr, my_vmsize, my_fileoff, my_filesize);
kcgen_verboseLog("memcpy(%llx, %llx, %llx)", (uintptr_t)prelinkData + in_kc_offs[segIndex], (uintptr_t)linkInfo->linkedKext + my_fileoff, my_filesize);
assert((my_fileoff + my_filesize) < linkInfo->linkedKextSize);
memcpy(prelinkData + in_kc_offs[segIndex], linkInfo->linkedKext + my_fileoff, my_filesize);
segCacheFileSizes[adjustSegment] = my_filesize;
if (out_sizes)
out_sizes[segIndex] = my_vmsize;
adjustSegment++;
}
if (isSplitKext) {
kcgen_verboseLog("Adjusting intra-kext relocations...");
result = kcgen_adjustKextSegmentLocations(OSKextGetArchitecture()->cputype,
OSKextGetArchitecture()->cpusubtype,
prelinkData,
adjustSegment,
segCacheFileOffsets,
segCacheFileSizes,
segOrigStartAddresses,
segNewStartAddresses,
&pointersForASLR,
&numPointers);
if (result != 0) {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"kcgen_adjustKextSegmentLocations failed with %d (%x) <%s %d>",
result, result, __func__, __LINE__);
abort();
}
if (!__OSKextHandlePointersForASLR(aKext, plkInfo, prelinkData, segKCOffsets, pointersForASLR, numPointers)) {
char kextPath[PATH_MAX];
__OSKextGetFileSystemPath(aKext, NULL, false, kextPath);
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Couldn't handle slide info for %s (KC most likely won't boot)!", kextPath);
}
pointersForASLR = NULL;
numPointers = 0;
if (!__OSKextCompactKext(aKext)) {
char kextPath[PATH_MAX];
__OSKextGetFileSystemPath(aKext, NULL, false, kextPath);
kcgen_verboseLog("Couldn't release kext memory for %s!", kextPath);
}
if (stripSymbolsFlag) {
struct mach_header_64 *kc_mh;
uint64_t le_fileoff;
kc_mh = (struct mach_header_64 *)(prelinkData + segKCOffsets[SEG_IDX_TEXT]);
le_fileoff = getKCPlkSegFileOff(plkInfo, SEG_IDX_LINKEDIT);
kcgen_verboseLog("\tStripping symbols from kext header...");
__OSKextStripMachHeader64(kc_mh, le_fileoff);
} }
finish:
return true;
}
static uint32_t kextcacheFileOffsetToPLKTEXTOffset(
uint64_t kcOffset,
plkInfo * plkInfo)
{
uint64_t plkSegOffset = 0;
uint64_t plkSegVMAddr = 0;
uint32_t plkTextOffset = 0;
for (unsigned int segIndex = 0; segIndex < SEG_IDX_COUNT; segIndex++) {
uint64_t plkseg_fileoff = getKCPlkSegFileOff(plkInfo, segIndex);
uint64_t plkseg_size = getKCPlkSegVMSize(plkInfo, segIndex);
if (kcOffset >= plkseg_fileoff && kcOffset < plkseg_fileoff + plkseg_size) {
plkSegOffset = kcOffset - plkseg_fileoff;
plkSegVMAddr = getKCPlkSegVMAddr(plkInfo, segIndex);
break;
}
}
if (plkSegOffset == 0) {
kcgen_verboseLog("plkSegOffset:%lld (VMAddr:0x%llx) from kcOffset:%lld", plkSegOffset, plkSegVMAddr, kcOffset);
}
assert(plkSegVMAddr > 0);
plkTextOffset = (uint32_t)((plkSegVMAddr - getKCPlkSegVMAddr(plkInfo, SEG_IDX_TEXT)) + plkSegOffset);
return plkTextOffset;
}
static boolean_t __OSKextInit_plkInfo(
CFDataRef kernelImage,
CFArrayRef kextArray,
plkInfo * plkInfo )
{
boolean_t result = false;
const UInt8 * kernelImagePtr = CFDataGetBytePtr(kernelImage);
uint64_t next_vmaddr;
uint64_t next_fileoff;
struct SegInfo * mySegInfo;
struct mach_header_64 * kernelHeader;
struct segment_command_64 * seg_cmd;
bzero((UInt8 *)plkInfo, sizeof(*plkInfo));
if (ISMACHO64(MAGIC32(kernelImagePtr)) == false) {
goto finish;
}
kernelHeader = (struct mach_header_64 *)kernelImagePtr;
plkInfo->kaslrOffsets = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks);
if (plkInfo->kaslrOffsets == NULL) {
OSKextLogMemError();
goto finish;
}
if (kextArray) {
if (__OSKextGetPLKSegSizes(kextArray, plkInfo) == false) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s %d - __OSKextGetPLKSegSizes failed ",
__func__, __LINE__);
goto finish;
}
}
plkInfo->kernelImage = kernelImage;
next_fileoff = roundPageCrossSafe(CFDataGetLength(kernelImage));
seg_cmd = macho_get_segment_by_name_64(kernelHeader, SEG_TEXT);
if (NULL == seg_cmd) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s %d - could not find kernel __TEXT ",
__func__, __LINE__);
goto finish;
}
mySegInfo = &plkInfo->kernel_TEXT;
mySegInfo->vmaddr = seg_cmd->vmaddr;
mySegInfo->vmsize = next_fileoff;
mySegInfo->fileoff = seg_cmd->fileoff;
#if SPLIT_KEXTS_DEBUG
kcgen_verboseLog("kernel __TEXT vmaddr %p to %p vmsize %llu fileoff %llu to %llu filesize %llu",
(void *)mySegInfo->vmaddr,
(void *) (mySegInfo->vmaddr + mySegInfo->vmsize),
mySegInfo->vmsize,
mySegInfo->fileoff,
(mySegInfo->fileoff + mySegInfo->vmsize),
mySegInfo->vmsize);
#endif
plkInfo->plk_TEXT.plkSegInfo.vmsize = roundPageCrossSafeFixedWidth(plkInfo->plk_TEXT.plkSegInfo.vmsize);
plkInfo->plk_TEXT_EXEC.plkSegInfo.vmsize = roundPageCrossSafeFixedWidth(plkInfo->plk_TEXT_EXEC.plkSegInfo.vmsize);
plkInfo->plk_DATA_CONST.plkSegInfo.vmsize = roundPageCrossSafeFixedWidth(plkInfo->plk_DATA_CONST.plkSegInfo.vmsize);
plkInfo->plk_DATA.plkSegInfo.vmsize = roundPageCrossSafeFixedWidth(plkInfo->plk_DATA.plkSegInfo.vmsize);
plkInfo->plk_LLVM_COV.plkSegInfo.vmsize = roundPageCrossSafeFixedWidth(plkInfo->plk_LLVM_COV.plkSegInfo.vmsize);
plkInfo->plk_LINKEDIT.plkSegInfo.vmsize = roundPageCrossSafeFixedWidth(plkInfo->plk_LINKEDIT.plkSegInfo.vmsize);
next_vmaddr = roundPageCrossSafeFixedWidth(mySegInfo->vmaddr -
plkInfo->plk_TEXT.plkSegInfo.vmsize -
plkInfo->plk_TEXT_EXEC.plkSegInfo.vmsize -
plkInfo->plk_DATA_CONST.plkSegInfo.vmsize);
seg_cmd = macho_get_segment_by_name_64(kernelHeader, kPrelinkTextSegment);
if (NULL == seg_cmd) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s %d - could not find %s ",
__func__, __LINE__, kPrelinkTextSegment);
goto finish;
}
mySegInfo = &plkInfo->plk_TEXT.plkSegInfo;
mySegInfo->vmaddr = next_vmaddr;
plkInfo->plk_TEXT.plk_next_kext_vmaddr = mySegInfo->vmaddr;
mySegInfo->fileoff = next_fileoff;
next_fileoff += mySegInfo->vmsize;
next_vmaddr = mySegInfo->vmaddr + mySegInfo->vmsize;
#if SPLIT_KEXTS_DEBUG
kcgen_verboseLog("__PRELINK_TEXT vmaddr %p to %p next vmaddr %p vmsize %llu fileoff %llu to %llu filesize %llu",
(void *)mySegInfo->vmaddr,
(void *) (mySegInfo->vmaddr + mySegInfo->vmsize),
(void *)plkInfo->plk_TEXT.plk_next_kext_vmaddr,
mySegInfo->vmsize,
mySegInfo->fileoff,
(mySegInfo->fileoff + mySegInfo->vmsize),
mySegInfo->vmsize);
#endif
seg_cmd = macho_get_segment_by_name_64(kernelHeader, kPrelinkTextExecSegment);
if (NULL == seg_cmd) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s %d - could not find %s, will skip this segment... ",
__func__, __LINE__, kPrelinkTextExecSegment);
}
else {
mySegInfo = &plkInfo->plk_TEXT_EXEC.plkSegInfo;
mySegInfo->vmaddr = next_vmaddr;
plkInfo->plk_TEXT_EXEC.plk_next_kext_vmaddr = mySegInfo->vmaddr;
mySegInfo->fileoff = next_fileoff;
next_fileoff += mySegInfo->vmsize;
next_vmaddr = mySegInfo->vmaddr + mySegInfo->vmsize;
#if SPLIT_KEXTS_DEBUG
kcgen_verboseLog("__PLK_TEXT_EXEC vmaddr %p to %p next vmaddr %p vmsize %llu fileoff %llu to %llu filesize %llu",
(void *)mySegInfo->vmaddr,
(void *) (mySegInfo->vmaddr + mySegInfo->vmsize),
(void *)plkInfo->plk_TEXT_EXEC.plk_next_kext_vmaddr,
mySegInfo->vmsize,
mySegInfo->fileoff,
(mySegInfo->fileoff + mySegInfo->vmsize),
mySegInfo->vmsize);
#endif
}
seg_cmd = macho_get_segment_by_name_64(kernelHeader, kPrelinkDataConstSegment);
if (NULL == seg_cmd) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s %d - could not find %s, will skip this segment... ",
__func__, __LINE__, kPrelinkDataConstSegment);
}
else {
mySegInfo = &plkInfo->plk_DATA_CONST.plkSegInfo;
mySegInfo->vmaddr = next_vmaddr;
plkInfo->plk_DATA_CONST.plk_next_kext_vmaddr = mySegInfo->vmaddr;
mySegInfo->fileoff = next_fileoff;
next_fileoff += mySegInfo->vmsize;
next_vmaddr = mySegInfo->vmaddr + mySegInfo->vmsize;
#if SPLIT_KEXTS_DEBUG
kcgen_verboseLog("__PLK_DATA_CONST vmaddr %p to %p next vmaddr %p vmsize %llu fileoff %llu to %llu filesize %llu",
(void *)mySegInfo->vmaddr,
(void *) (mySegInfo->vmaddr + mySegInfo->vmsize),
(void *)plkInfo->plk_DATA_CONST.plk_next_kext_vmaddr,
mySegInfo->vmsize,
mySegInfo->fileoff,
mySegInfo->fileoff + mySegInfo->vmsize,
mySegInfo->vmsize);
#endif
}
result = __OSKextGetLastKernelLoadAddr(kernelImage, &next_vmaddr);
if (result == false) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s %d - __OSKextGetLastKernelLoadAddr failed ",
__func__, __LINE__);
goto finish;
}
next_vmaddr = roundPageCrossSafeFixedWidth(next_vmaddr);
seg_cmd = macho_get_segment_by_name_64(kernelHeader, kPrelinkDataSegment);
if (NULL == seg_cmd) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s %d - could not find %s ",
__func__, __LINE__, kPrelinkDataSegment);
}
else {
mySegInfo = &plkInfo->plk_DATA.plkSegInfo;
mySegInfo->vmaddr = next_vmaddr;
plkInfo->plk_DATA.plk_next_kext_vmaddr = mySegInfo->vmaddr;
mySegInfo->fileoff = next_fileoff;
next_fileoff += mySegInfo->vmsize;
next_vmaddr = mySegInfo->vmaddr + mySegInfo->vmsize;
#if SPLIT_KEXTS_DEBUG
kcgen_verboseLog("__PRELINK_DATA vmaddr %p to %p next vmaddr %p vmsize %llu fileoff %llu to %llu filesize %llu",
(void *)mySegInfo->vmaddr,
(void *) (mySegInfo->vmaddr + mySegInfo->vmsize),
(void *)plkInfo->plk_DATA.plk_next_kext_vmaddr,
mySegInfo->vmsize,
mySegInfo->fileoff,
mySegInfo->fileoff + mySegInfo->vmsize,
mySegInfo->vmsize);
#endif
}
seg_cmd = macho_get_segment_by_name_64(kernelHeader, kPrelinkLLVMCovSegment);
if (NULL == seg_cmd) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s %d - could not find %s, will skip this segment... ",
__func__, __LINE__, kPrelinkLLVMCovSegment);
}
else {
mySegInfo = &plkInfo->plk_LLVM_COV.plkSegInfo;
mySegInfo->vmaddr = next_vmaddr;
plkInfo->plk_LLVM_COV.plk_next_kext_vmaddr = mySegInfo->vmaddr;
mySegInfo->fileoff = next_fileoff;
next_fileoff += mySegInfo->vmsize;
next_vmaddr = mySegInfo->vmaddr + mySegInfo->vmsize;
#if SPLIT_KEXTS_DEBUG
kcgen_verboseLog("__PLK_LLVM_COV vmaddr %p to %p next vmaddr %p vmsize %llu fileoff %llu to %llu filesize %llu",
(void *)mySegInfo->vmaddr,
(void *) (mySegInfo->vmaddr + mySegInfo->vmsize),
(void *)plkInfo->plk_LLVM_COV.plk_next_kext_vmaddr,
mySegInfo->vmsize,
mySegInfo->fileoff,
mySegInfo->fileoff + mySegInfo->vmsize,
mySegInfo->vmsize);
#endif
}
seg_cmd = macho_get_segment_by_name_64(kernelHeader, kPrelinkLinkeditSegment);
if (NULL == seg_cmd) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s %d - could not find %s, will skip this segment... ",
__func__, __LINE__, kPrelinkLinkeditSegment);
}
else {
mySegInfo = &plkInfo->plk_LINKEDIT.plkSegInfo;
mySegInfo->vmaddr = next_vmaddr;
plkInfo->plk_LINKEDIT.plk_next_kext_vmaddr = mySegInfo->vmaddr;
mySegInfo->fileoff = next_fileoff;
next_fileoff += mySegInfo->vmsize;
next_vmaddr = mySegInfo->vmaddr + mySegInfo->vmsize;
#if SPLIT_KEXTS_DEBUG
kcgen_verboseLog("__PLK_LINKEDIT vmaddr %p to %p next vmaddr %p vmsize %llu fileoff %llu to %llu filesize %llu",
(void *)mySegInfo->vmaddr,
(void *) (mySegInfo->vmaddr + mySegInfo->vmsize),
(void *)plkInfo->plk_LINKEDIT.plk_next_kext_vmaddr,
mySegInfo->vmsize,
mySegInfo->fileoff,
mySegInfo->fileoff + mySegInfo->vmsize,
mySegInfo->vmsize);
#endif
}
seg_cmd = macho_get_segment_by_name_64(kernelHeader, kPrelinkInfoSegment);
if (NULL == seg_cmd) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s %d - could not find %s ",
__func__, __LINE__, kPrelinkInfoSegment);
goto finish;
}
mySegInfo = &plkInfo->plk_INFO;
mySegInfo->vmaddr = next_vmaddr;
mySegInfo->fileoff = next_fileoff;
mySegInfo->vmsize = seg_cmd->vmsize;
#if SPLIT_KEXTS_DEBUG
kcgen_verboseLog("__PRELINK_INFO vmaddr %p vmsize %llu fileoff %llu filesize %llu",
(void *)mySegInfo->vmaddr,
mySegInfo->vmsize,
mySegInfo->fileoff,
mySegInfo->vmsize);
#endif
plkInfo->kernelCacheImage = CFDataCreateMutable(kCFAllocatorDefault, next_fileoff);
if (plkInfo->kernelCacheImage == NULL) {
OSKextLogMemError();
goto finish;
}
CFDataSetLength(plkInfo->kernelCacheImage, next_fileoff);
#if SPLIT_KEXTS_DEBUG
{
u_char *prelinkData = CFDataGetMutableBytePtr(plkInfo->kernelCacheImage);
kcgen_verboseLog("plkInfo->kernelCacheImage @%p - %p (%d bytes)",
prelinkData, prelinkData + (unsigned int)next_fileoff, (unsigned int)next_fileoff);
}
#endif
result = true;
finish:
return result;
}
static macho_seek_result
_macho_section_size_accumulator(struct load_command *lc, const void * file_end __unused, uint8_t swap __unused, void *user_data) {
struct segment_command_64 *seg;
struct section_64 *sec;
uint64_t start_off, end_off;
uint64_t max_off = 0;
struct max_data *md;
enum enumSegIdx segIndex;
md = (struct max_data *)user_data;
switch(lc->cmd) {
case LC_SEGMENT_64:
seg = (struct segment_command_64 *)lc;
sec = (struct section_64 *)((char *)seg + sizeof(struct segment_command_64));
for(unsigned int i=0; i < seg->nsects; ++i) {
start_off = sec->addr - seg->vmaddr;
end_off = start_off + sec->size;
max_off = (end_off > max_off) ? end_off : max_off;
sec = (struct section_64 *)((char *)sec + sizeof(struct section_64));
}
assert(getSegIndex(seg->segname, &segIndex));
assert(segIndex < SEG_IDX_COUNT);
md->seg_data[segIndex].vmaddr = seg->vmaddr;
md->seg_data[segIndex].vmsz = seg->vmsize;
md->seg_data[segIndex].max_off = max_off ? max_off : seg->vmsize;
break;
default:
return macho_seek_result_not_found;
}
return macho_seek_result_not_found;
}
static void __OSKextGetEffectiveSegmentSizes(OSKextRef kext, struct max_data *md) {
CFDataRef kextExecutable = NULL;
struct mach_header *hdr;
void *file_end;
kextExecutable = OSKextCopyExecutableForArchitecture(kext,
OSKextGetArchitecture());
assert(kextExecutable);
hdr = (struct mach_header *)CFDataGetBytePtr(kextExecutable);
file_end = (char *)hdr + CFDataGetLength(kextExecutable);
macho_scan_load_commands(hdr, file_end, _macho_section_size_accumulator, md);
CFRelease(kextExecutable);
}
static boolean_t __OSKextGetPLKSegSizes(
CFArrayRef kextArray,
plkInfo * segInfo )
{
boolean_t result = false;
CFIndex count, i;
CFDataRef kextExecutable = NULL; struct mach_header_64 * kextHeader;
CFMutableArrayRef loadList = NULL; OSKextRef thisKext = NULL;
uint64_t segWaste_TEXT, segWaste_TEXT_EXEC, segWaste_DATA, segWaste_DATA_CONST;
uint64_t segWasteTotal;
uint64_t plkseg_vmsize;
char kextPath[PATH_MAX];
uint64_t prevSegEndAddr[SEG_IDX_COUNT] = {0};
loadList = OSKextCopyLoadListForKexts(kextArray, false);
if (loadList == NULL) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s %d - OSKextCopyLoadListForKexts failed ",
__func__, __LINE__);
goto finish;
}
segInfo->plk_TEXT.plk_seg_name = kPrelinkTextSegment;
segInfo->plk_TEXT_EXEC.plk_seg_name = kPrelinkTextExecSegment;
segInfo->plk_DATA.plk_seg_name = kPrelinkDataSegment;
segInfo->plk_DATA_CONST.plk_seg_name = kPrelinkDataConstSegment;
segInfo->plk_LLVM_COV.plk_seg_name = kPrelinkLLVMCovSegment;
segInfo->plk_LINKEDIT.plk_seg_name = kPrelinkLinkeditSegment;
count = CFArrayGetCount(loadList);
nNonSplitKexts = 0;
nSplitKexts = 0;
for(i = 0; i < count; i++) {
SAFE_RELEASE_NULL(kextExecutable);
thisKext = (OSKextRef)CFArrayGetValueAtIndex(loadList, i);
if (OSKextDeclaresExecutable(thisKext) == false || OSKextIsInterface(thisKext)) {
continue;
}
kextExecutable = OSKextCopyExecutableForArchitecture(thisKext,
OSKextGetArchitecture());
if (kextExecutable == NULL) {
continue;
}
kextHeader = (struct mach_header_64 *)CFDataGetBytePtr(kextExecutable);
if (__OSKextIsSplitKextMacho64(kextHeader)) {
nSplitKexts++;
if (nNonSplitKexts > 0) {
__OSKextGetFileSystemPath(thisKext, NULL, true, kextPath);
OSKextLog(thisKext,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"non-split KC: kext '%s' was compiled as a 'split' kext: aborting! <%s %d>",
kextPath, __func__, __LINE__);
abort();
}
} else {
nNonSplitKexts++;
if (nSplitKexts > 0) {
__OSKextGetFileSystemPath(thisKext, NULL, true, kextPath);
OSKextLog(thisKext,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"split KC: kext '%s' was not compiled as a 'split' kext: aborting! <%s %d>",
kextPath, __func__, __LINE__);
abort();
}
}
}
if (nSplitKexts) {
assert(nNonSplitKexts == 0);
}
segWaste_TEXT = segWaste_TEXT_EXEC = segWaste_DATA = segWaste_DATA_CONST = 0;
for (i = 0; i < count; i++) {
SAFE_RELEASE_NULL(kextExecutable);
thisKext = (OSKextRef)CFArrayGetValueAtIndex(loadList, i);
kextExecutable = OSKextCopyExecutableForArchitecture(thisKext,
OSKextGetArchitecture());
if (kextExecutable == NULL) {
continue;
}
__OSKextGetFileSystemPath(thisKext, NULL, true, kextPath);
kcgen_verboseLog("Processing kext: %s (sz:%ld)",
kextPath, CFDataGetLength(kextExecutable));
if (!nSplitKexts) {
uint64_t vmsize;
assert(nNonSplitKexts);
vmsize = getKCPlkSegVMSize(segInfo, SEG_IDX_TEXT);
vmsize += roundPageCrossSafe(CFDataGetLength(kextExecutable));
setKCPlkSegVMSize(segInfo, SEG_IDX_TEXT, vmsize);
continue;
}
{
struct max_data md;
bzero(&md, sizeof md);
__OSKextGetEffectiveSegmentSizes(thisKext, &md);
segWaste_TEXT += md.seg_data[SEG_IDX_TEXT].vmsz - md.seg_data[SEG_IDX_TEXT].max_off;
segWaste_TEXT_EXEC += md.seg_data[SEG_IDX_TEXT_EXEC].vmsz - md.seg_data[SEG_IDX_TEXT_EXEC].max_off;
segWaste_DATA += md.seg_data[SEG_IDX_DATA].vmsz - md.seg_data[SEG_IDX_DATA].max_off;
segWaste_DATA_CONST += md.seg_data[SEG_IDX_DATA_CONST].vmsz - md.seg_data[SEG_IDX_DATA_CONST].max_off;
}
kextHeader = (struct mach_header_64 *)CFDataGetBytePtr(kextExecutable);
macho_swap((u_char *) kextHeader);
if (!OSKextIsInterface(thisKext)) {
assert(macho_get_segment_by_name_64((struct mach_header_64 *)CFDataGetBytePtr(kextExecutable), SEG_TEXT_EXEC) != NULL);
}
for (enum enumSegIdx segIndex = SEG_IDX_TEXT; segIndex < SEG_IDX_COUNT; segIndex++) {
uint64_t __unused my_vmaddr, __unused my_fileoff, __unused my_filesize, __unused next_vmaddr;
uint64_t my_vmsize, my_maxalign, prevAlignWaste;
char *segName = segIdxToName(segIndex);
boolean_t result;
uint64_t sz;
result = __OSKextGetSegmentInfo((const UInt8 *)kextHeader, segName, &my_vmaddr, &my_vmsize, &my_fileoff, &my_filesize, &my_maxalign, true);
sz = my_vmsize;
if (!result && segIndex == SEG_IDX_TEXT) {
sz = sizeof(*kextHeader) + kextHeader->sizeofcmds;
} else if (!result) {
continue;
}
prevAlignWaste = __OSKextAlignAddress(prevSegEndAddr[segIndex], my_maxalign) - prevSegEndAddr[segIndex];
sz += prevAlignWaste;
prevSegEndAddr[segIndex] += sz;
plkseg_vmsize = getKCPlkSegVMSize(segInfo, segIndex);
kcgen_verboseLog("segIndex %d segName %s kcSegVMSize %llx sz %llx", segIndex, segName, plkseg_vmsize, sz);
setKCPlkSegVMSize(segInfo, segIndex, plkseg_vmsize + sz);
} }
segWasteTotal = segWaste_TEXT + segWaste_TEXT_EXEC + segWaste_DATA + segWaste_DATA_CONST;
kcgen_verboseLog("nkexts %ld segment waste TEXT %llu TEXT_EXEC %llu DATA %llu DATA_CONST %llu totals %llu",
count, segWaste_TEXT, segWaste_TEXT_EXEC, segWaste_DATA, segWaste_DATA_CONST, segWasteTotal);
for (enum enumSegIdx i = SEG_IDX_TEXT; i < SEG_IDX_COUNT; ++i) {
plkseg_vmsize = getKCPlkSegVMSize(segInfo, i);
setKCPlkSegVMSize(segInfo, i, roundPageCrossSafe(plkseg_vmsize));
}
for(enum enumSegIdx i = SEG_IDX_TEXT; i < SEG_IDX_COUNT; ++i) {
kcgen_verboseLog("PLK_%s vmsize:%llu", segIdxToName(i), getKCPlkSegVMSize(segInfo, i));
}
result = true;
finish:
SAFE_RELEASE(loadList);
SAFE_RELEASE(kextExecutable);
return result;
}
static boolean_t __OSKextSetPLKSegInfo(plkInfo *plkInfo)
{
struct _seginfo {
const char *seg_name;
uint64_t seg_vmsize;
uint64_t seg_vmaddr;
SegInfo *seg_info;
vm_prot_t seg_maxprot;
vm_prot_t seg_initprot;
const char *sect_name;
SegInfo *sect_info;
};
boolean_t ret = false;
struct _seginfo plkseg[SEG_IDX_COUNT] = {
{ .seg_name = plkInfo->plk_TEXT.plk_seg_name,
.seg_vmsize = getKCPlkSegVMSize(plkInfo, SEG_IDX_TEXT),
.seg_vmaddr = getKCPlkSegVMAddr(plkInfo, SEG_IDX_TEXT),
.seg_info = &plkInfo->plk_TEXT.plkSegInfo,
.seg_maxprot = PROT_READ,
.seg_initprot = PROT_READ,
.sect_name = kPrelinkTextSection,
.sect_info = &plkInfo->plk_TEXT.plkSegInfo
},
{ .seg_name = plkInfo->plk_TEXT_EXEC.plk_seg_name,
.seg_vmsize = getKCPlkSegVMSize(plkInfo, SEG_IDX_TEXT_EXEC),
.seg_vmaddr = getKCPlkSegVMAddr(plkInfo, SEG_IDX_TEXT_EXEC),
.seg_info = &plkInfo->plk_TEXT_EXEC.plkSegInfo,
.seg_maxprot = PROT_READ | PROT_EXEC,
.seg_initprot = PROT_READ | PROT_EXEC,
.sect_name = kPrelinkTextSection,
.sect_info = &plkInfo->plk_TEXT_EXEC.plkSegInfo
},
{ .seg_name = plkInfo->plk_DATA.plk_seg_name,
.seg_vmsize = getKCPlkSegVMSize(plkInfo, SEG_IDX_DATA),
.seg_vmaddr = getKCPlkSegVMAddr(plkInfo, SEG_IDX_DATA),
.seg_info = &plkInfo->plk_DATA.plkSegInfo,
.seg_maxprot = PROT_READ | PROT_WRITE,
.seg_initprot = PROT_READ | PROT_WRITE,
.sect_name = kPrelinkDataSection,
.sect_info = &plkInfo->plk_DATA.plkSegInfo
},
{ .seg_name = plkInfo->plk_DATA_CONST.plk_seg_name,
.seg_vmsize = getKCPlkSegVMSize(plkInfo, SEG_IDX_DATA_CONST),
.seg_vmaddr = getKCPlkSegVMAddr(plkInfo, SEG_IDX_DATA_CONST),
.seg_info = &plkInfo->plk_DATA_CONST.plkSegInfo,
.seg_maxprot = PROT_READ,
.seg_initprot = PROT_READ,
.sect_name = kPrelinkDataSection,
.sect_info = &plkInfo->plk_DATA_CONST.plkSegInfo
},
{ .seg_name = plkInfo->plk_LLVM_COV.plk_seg_name,
.seg_vmsize = getKCPlkSegVMSize(plkInfo, SEG_IDX_LLVM_COV),
.seg_vmaddr = getKCPlkSegVMAddr(plkInfo, SEG_IDX_LLVM_COV),
.seg_info = &plkInfo->plk_LLVM_COV.plkSegInfo,
.seg_maxprot = PROT_READ | PROT_WRITE,
.seg_initprot = PROT_READ | PROT_WRITE,
.sect_name = kPrelinkLLVMCovSection,
.sect_info = &plkInfo->plk_LLVM_COV.plkSegInfo
},
{ .seg_name = plkInfo->plk_LINKEDIT.plk_seg_name,
.seg_vmsize = getKCPlkSegVMSize(plkInfo, SEG_IDX_LINKEDIT),
.seg_vmaddr = getKCPlkSegVMAddr(plkInfo, SEG_IDX_LINKEDIT),
.seg_info = &plkInfo->plk_LINKEDIT.plkSegInfo,
.seg_maxprot = PROT_READ,
.seg_initprot = PROT_READ,
.sect_name = kPrelinkLinkeditSection,
.sect_info = &plkInfo->plk_LINKEDIT.plkSegInfo
}
};
for (size_t i = 0; i < sizeof(plkseg)/sizeof(plkseg[0]); i++) {
uint64_t seg_vmbase = plkseg[i].seg_vmaddr;
uint64_t seg_vmsize = plkseg[i].seg_vmsize;
kcgen_verboseLog("SetSegInfo[%s] = {0x%llX + %lld (ofst:%lld, sz:%lld)}",
plkseg[i].seg_name,
plkseg[i].seg_info->vmaddr,
seg_vmsize,
plkseg[i].seg_info->fileoff,
seg_vmsize);
if (__OSKextSetSegmentAddress(plkInfo->kernelCacheImage,
plkseg[i].seg_name,
seg_vmbase) == false) {
kcgen_warning("__OSKextSetSegmentAddress FAILED for %s", plkseg[i].seg_name);
goto finish;
}
if (__OSKextSetSegmentVMSize(plkInfo->kernelCacheImage,
plkseg[i].seg_name,
seg_vmsize) == false) {
kcgen_warning("__OSKextSetSegmentVMSize FAILED for %s", plkseg[i].seg_name);
goto finish;
}
if (__OSKextSetSegmentOffset(plkInfo->kernelCacheImage,
plkseg[i].seg_name,
plkseg[i].seg_info->fileoff) == false) {
kcgen_warning("__OSKextSetSegmentOffset FAILED for %s", plkseg[i].seg_name);
goto finish;
}
if (__OSKextSetSegmentFilesize(plkInfo->kernelCacheImage,
plkseg[i].seg_name,
seg_vmsize) == false) {
kcgen_warning("__OSKextSetSegmentFilesize FAILED for %s", plkseg[i].seg_name);
goto finish;
}
if (__OSKextSetSegmentProtection(plkInfo->kernelCacheImage,
plkseg[i].seg_name,
plkseg[i].seg_initprot,
plkseg[i].seg_maxprot) == false) {
kcgen_warning("__OSKextSetSegmentProtection FAILED for %s", plkseg[i].seg_name);
goto finish;
}
if (__OSKextSetSectionAddress(plkInfo->kernelCacheImage,
plkseg[i].seg_name, plkseg[i].sect_name,
seg_vmbase) == false) {
kcgen_warning("__OSKextSetSectionAddress FAILED for %s,%s",
plkseg[i].seg_name, plkseg[i].sect_name);
goto finish;
}
if (__OSKextSetSectionOffset(plkInfo->kernelCacheImage,
plkseg[i].seg_name, plkseg[i].sect_name,
plkseg[i].sect_info->fileoff) == false) {
kcgen_warning("__OSKextSetSectionOffset FAILED for %s,%s",
plkseg[i].seg_name, plkseg[i].sect_name);
goto finish;
}
if (__OSKextSetSectionSize(plkInfo->kernelCacheImage,
plkseg[i].seg_name, plkseg[i].sect_name,
seg_vmsize) == false) {
kcgen_warning("__OSKextSetSectionSize FAILED for %s,%s",
plkseg[i].seg_name, plkseg[i].sect_name);
goto finish;
}
}
ret = true;
finish:
return ret;
}
static uint32_t __OSKextGetSegMaxAlignment(
struct segment_command_64 * seg_cmd)
{
struct section_64 * sect = NULL;
uint32_t i = 0;
uint32_t result = KEXT_MIN_ALIGN;
if (seg_cmd->cmd != LC_SEGMENT_64)
goto finish;
sect = (struct section_64 *)(&seg_cmd[1]);
for (i = 0; i < seg_cmd->nsects; ++i, ++sect) {
if (sect->align > result) {
result = sect->align;
}
}
if (!strncmp(seg_cmd->segname, SEG_TEXT_EXEC, sizeof(seg_cmd->segname))) {
result = 12;
}
if (g_max_align_to_4k && result < 12)
result = 12;
finish:
return result;
}
static uint64_t __OSKextAlignAddress(uint64_t address, uint32_t align)
{
uint64_t alignment = (1 << align);
uint64_t low_bits = 0;
if (align == 0) return address;
low_bits = (address) & (alignment - 1);
if (low_bits) {
address += (alignment - low_bits);
}
return address;
}
static boolean_t __OSKextGetSegmentAddressAndOffsetDataRef(
CFDataRef imageRef,
const char * segname,
uint32_t * fileOffsetOut,
uint64_t * loadAddrOut)
{
boolean_t result;
const UInt8 * imagePtr = CFDataGetBytePtr(imageRef);
result = __OSKextGetSegmentAddressAndOffset(imagePtr, segname, fileOffsetOut, loadAddrOut);
return result;
}
static boolean_t __OSKextGetSegmentAddressAndOffset(
const UInt8 * imagePtr,
const char * segname,
uint32_t * fileOffsetOut,
uint64_t * loadAddrOut)
{
boolean_t result = false;
uint32_t fileOffset = 0;
uint64_t loadAddr = 0;
if (!__OSKextIsArchitectureLP64()) {
struct mach_header *mach_header = (struct mach_header *) imagePtr;
struct segment_command *seg = macho_get_segment_by_name(mach_header, segname);
if (!seg) {
goto finish;
}
fileOffset = seg->fileoff;
loadAddr = seg->vmaddr;
} else {
struct mach_header_64 *mach_header = (struct mach_header_64 *) imagePtr;
struct segment_command_64 *seg = macho_get_segment_by_name_64(mach_header, segname);
if (!seg) {
goto finish;
}
fileOffset = seg->fileoff;
loadAddr = seg->vmaddr;
}
if (fileOffsetOut) *fileOffsetOut = fileOffset;
if (loadAddrOut) *loadAddrOut = loadAddr;
result = true;
finish:
return result;
}
static boolean_t __OSKextGetSegmentInfo(
const UInt8 * imagePtr,
const char * segname,
uint64_t * vmaddrOut,
uint64_t * vmsizeOut,
uint64_t * fileoffOut,
uint64_t * filesizeOut,
uint64_t * maxAlignOut,
boolean_t truncateSegs)
{
boolean_t result = false;
uint64_t max_off = 0;
if (truncateSegs && (!strcmp(segname, SEG_LINKEDIT) || !strcmp(segname, SEG_LLVM_COV)))
truncateSegs = false;
if (__OSKextIsArchitectureLP64()) {
struct mach_header_64 *mach_header = (struct mach_header_64 *) imagePtr;
struct segment_command_64 *seg = macho_get_segment_by_name_64(mach_header, segname);
if (!seg) {
goto finish;
}
if (maxAlignOut) *maxAlignOut = __OSKextGetSegMaxAlignment(seg);
if (truncateSegs && !strcmp(segname, "__TEXT")) {
max_off = sizeof(*mach_header) + mach_header->sizeofcmds;
}
if (vmaddrOut) *vmaddrOut = seg->vmaddr;
if (vmsizeOut) {
if (truncateSegs) {
struct section_64 *sect = (struct section_64 *) &seg[1];
uint64_t off, end_off;
for (unsigned int i = 0; i < seg->nsects; ++i, ++sect) {
off = sect->addr - seg->vmaddr;
end_off = off + sect->size;
max_off = (end_off > max_off) ? end_off : max_off;
#if 0
kcgen_verboseLog("sect %d %s %s seg_vmaddr %llx seg_vmsize %llx sect_off %llx sect_end_off %llx max_off %llx",
i, sect->segname, sect->sectname, seg->vmaddr, seg->vmsize, off, end_off, max_off);
#endif
}
*vmsizeOut = max_off;
} else {
*vmsizeOut = seg->vmsize;
}
}
if (fileoffOut) *fileoffOut = seg->fileoff;
if (filesizeOut) {
if (truncateSegs) {
assert(vmsizeOut);
*filesizeOut = max_off;
} else {
*filesizeOut = seg->filesize;
}
}
} else {
struct mach_header *mach_header = (struct mach_header *) imagePtr;
struct segment_command *seg = macho_get_segment_by_name(mach_header, segname);
if (!seg) {
goto finish;
}
if (truncateSegs && !strcmp(segname, "__TEXT")) {
max_off = sizeof(*mach_header) + mach_header->sizeofcmds;
}
if (vmaddrOut) *vmaddrOut = seg->vmaddr;
if (vmsizeOut) {
if (truncateSegs) {
struct section *sect = (struct section *) &seg[1];
uint32_t off, end_off, max_off = 0;
for(unsigned int i = 0; i < seg->nsects; ++i, ++sect) {
off = sect->addr - seg->vmaddr;
end_off = off + sect->size;
max_off = (end_off > max_off) ? end_off : max_off;
kcgen_verboseLog("sect %d %s %s seg_vmaddr %x seg_vmsize %x sect_off %x sect_end_off %x max_off %x",
i, sect->segname, sect->sectname, seg->vmaddr, seg->vmsize, off, end_off, max_off);
}
*vmsizeOut = max_off;
} else {
*vmsizeOut = seg->vmsize;
}
*vmsizeOut = seg->vmsize;
}
if (fileoffOut) *fileoffOut = seg->fileoff;
if (filesizeOut) *filesizeOut = seg->filesize;
}
result = true;
finish:
return result;
}
static boolean_t __OSKextGetSegmentInfoForOffset(
const UInt8 * imagePtr,
uint64_t imageOffset,
char segnameOut[MAX_SEGNAME_LEN],
uint64_t * segOffsetOut,
uint64_t * vmaddrOut,
uint64_t * vmsizeOut,
uint64_t * fileoffOut,
uint64_t * filesizeOut)
{
assert(segnameOut);
assert(vmaddrOut);
assert(vmsizeOut);
assert(fileoffOut);
assert(filesizeOut);
static const char *SegsToLookup[] = {SEG_DATA_CONST, SEG_DATA, SEG_TEXT_EXEC, SEG_LLVM_COV, SEG_LINKEDIT};
static const size_t nSegsToLookup = sizeof(SegsToLookup) / sizeof(char *);
uint64_t text_vmaddr = 0, text_vmsize = 0, text_fileoff = 0, text_filesize = 0;
if (!__OSKextGetSegmentInfo(imagePtr,
SEG_TEXT,
&text_vmaddr,
&text_vmsize,
&text_fileoff,
&text_filesize, NULL, false)) {
return false;
}
uint64_t imageVMAddr = text_vmaddr + imageOffset;
if (imageVMAddr < text_vmaddr + text_vmsize) {
strncpy(segnameOut, SEG_TEXT, MAX_SEGNAME_LEN);
*vmaddrOut = text_vmaddr;
*vmsizeOut = text_vmsize;
*fileoffOut = text_fileoff;
*filesizeOut = text_filesize;
*segOffsetOut = imageVMAddr - text_vmaddr;
return true;
}
for (size_t i = 0; i < nSegsToLookup; i++) {
if (__OSKextGetSegmentInfo(imagePtr,
SegsToLookup[i],
vmaddrOut,
vmsizeOut,
fileoffOut,
filesizeOut, NULL, false)) {
if (imageVMAddr >= *vmaddrOut &&
imageVMAddr < (*vmaddrOut + *vmsizeOut)) {
strncpy(segnameOut, SegsToLookup[i], MAX_SEGNAME_LEN);
*segOffsetOut = imageVMAddr - *vmaddrOut;
return true;
}
}
}
return false;
}
static boolean_t __OSKextGetSegmentFileAndVMSizeDataRef(
CFDataRef imageRef,
const char * segname,
uint64_t * fileSizeOut,
uint64_t * VMSizeOut)
{
boolean_t result;
const UInt8 * imagePtr = CFDataGetBytePtr(imageRef);
result = __OSKextGetSegmentFileAndVMSize(imagePtr, segname, fileSizeOut, VMSizeOut);
return result;
}
static boolean_t __OSKextGetSegmentFileAndVMSize(
const UInt8 * imagePtr,
const char * segname,
uint64_t * fileSizeOut,
uint64_t * VMSizeOut)
{
boolean_t result = false;
uint64_t filesize = 0;
uint64_t vmsize = 0;
if (!__OSKextIsArchitectureLP64()) {
struct mach_header *mach_header = (struct mach_header *) imagePtr;
struct segment_command *seg = macho_get_segment_by_name(mach_header, segname);
if (!seg) {
goto finish;
}
filesize = seg->filesize;
vmsize = seg->vmsize;
} else {
struct mach_header_64 *mach_header = (struct mach_header_64 *) imagePtr;
struct segment_command_64 *seg = macho_get_segment_by_name_64(mach_header, segname);
if (!seg) {
goto finish;
}
filesize = seg->filesize;
vmsize = seg->vmsize;
}
if (fileSizeOut) *fileSizeOut = filesize;
if (VMSizeOut) *VMSizeOut = vmsize;
result = true;
finish:
return result;
}
static boolean_t __OSKextSetSegmentAddress(
CFDataRef kernelImage, const char *segname, uint64_t loadAddr)
{
boolean_t result = false;
if (!__OSKextIsArchitectureLP64()) {
struct mach_header *mach_header = (struct mach_header *) CFDataGetBytePtr(kernelImage);
struct segment_command *seg = macho_get_segment_by_name(mach_header, segname);
if (!seg) {
goto finish;
}
seg->vmaddr = (uint32_t) loadAddr;
} else {
struct mach_header_64 *mach_header = (struct mach_header_64 *) CFDataGetBytePtr(kernelImage);
struct segment_command_64 *seg = macho_get_segment_by_name_64(mach_header, segname);
if (!seg) {
goto finish;
}
seg->vmaddr = loadAddr;
}
result = true;
finish:
return result;
}
static boolean_t __OSKextSetSegmentVMSize(
CFDataRef kernelImage, const char *segname, uint64_t vmsize)
{
boolean_t result = false;
if (!__OSKextIsArchitectureLP64()) {
struct mach_header *mach_header = (struct mach_header *) CFDataGetBytePtr(kernelImage);
struct segment_command *seg = macho_get_segment_by_name(mach_header, segname);
if (!seg) {
goto finish;
}
seg->vmsize = (uint32_t) vmsize;
} else {
struct mach_header_64 *mach_header = (struct mach_header_64 *) CFDataGetBytePtr(kernelImage);
struct segment_command_64 *seg = macho_get_segment_by_name_64(mach_header, segname);
if (!seg) {
goto finish;
}
seg->vmsize = vmsize;
}
result = true;
finish:
return result;
}
static boolean_t __OSKextSetSegmentOffset(
CFDataRef kernelImage, const char *segname, uint64_t fileOffset)
{
boolean_t result = false;
if (!__OSKextIsArchitectureLP64()) {
struct mach_header *mach_header = (struct mach_header *) CFDataGetBytePtr(kernelImage);
struct segment_command *seg = macho_get_segment_by_name(mach_header, segname);
if (!seg) {
goto finish;
}
seg->fileoff = (uint32_t) fileOffset;
} else {
struct mach_header_64 *mach_header = (struct mach_header_64 *) CFDataGetBytePtr(kernelImage);
struct segment_command_64 *seg = macho_get_segment_by_name_64(mach_header, segname);
if (!seg) {
goto finish;
}
seg->fileoff = fileOffset;
}
result = true;
finish:
return result;
}
static boolean_t __OSKextSetSegmentFilesize(
CFDataRef kernelImage, const char *segname, uint64_t filesize)
{
boolean_t result = false;
if (!__OSKextIsArchitectureLP64()) {
struct mach_header *mach_header = (struct mach_header *) CFDataGetBytePtr(kernelImage);
struct segment_command *seg = macho_get_segment_by_name(mach_header, segname);
if (!seg) {
goto finish;
}
seg->filesize = (uint32_t) filesize;
} else {
struct mach_header_64 *mach_header = (struct mach_header_64 *) CFDataGetBytePtr(kernelImage);
struct segment_command_64 *seg = macho_get_segment_by_name_64(mach_header, segname);
if (!seg) {
goto finish;
}
seg->filesize = filesize;
}
result = true;
finish:
return result;
}
static boolean_t __OSKextSetSegmentProtection(
CFDataRef kernelImage, const char *segname, vm_prot_t initprot, vm_prot_t maxprot)
{
boolean_t result = false;
if (!__OSKextIsArchitectureLP64()) {
struct mach_header *mach_header = (struct mach_header *) CFDataGetBytePtr(kernelImage);
struct segment_command *seg = macho_get_segment_by_name(mach_header, segname);
if (!seg) {
goto finish;
}
seg->initprot = initprot;
seg->maxprot = maxprot;
} else {
struct mach_header_64 *mach_header = (struct mach_header_64 *) CFDataGetBytePtr(kernelImage);
struct segment_command_64 *seg = macho_get_segment_by_name_64(mach_header, segname);
if (!seg) {
goto finish;
}
seg->initprot = initprot;
seg->maxprot = maxprot;
}
kcgen_verboseLog(" --> SET %s to initprot:0x%X, maxprot:0x%X", segname, initprot, maxprot);
result = true;
finish:
return result;
}
static boolean_t __OSKextMachOSetSegmentProtection(
void *header, const char *segname, vm_prot_t initprot, vm_prot_t maxprot)
{
boolean_t result = false;
if (!__OSKextIsArchitectureLP64()) {
struct mach_header *mach_header = (struct mach_header *)header;
struct segment_command *seg = macho_get_segment_by_name(mach_header, segname);
if (!seg) {
goto finish;
}
seg->initprot = initprot;
seg->maxprot = maxprot;
} else {
struct mach_header_64 *mach_header = (struct mach_header_64 *)header;
struct segment_command_64 *seg = macho_get_segment_by_name_64(mach_header, segname);
if (!seg) {
goto finish;
}
seg->initprot = initprot;
seg->maxprot = maxprot;
}
result = true;
finish:
return result;
}
static boolean_t __OSKextSetSectionAddress(
CFDataRef kernelImage, const char *segname,
const char *sectname, uint64_t loadAddr)
{
boolean_t result = false;
if (!__OSKextIsArchitectureLP64()) {
struct mach_header *mach_header = (struct mach_header *) CFDataGetBytePtr(kernelImage);
struct section *sect = macho_get_section_by_name(mach_header, segname, sectname);
if (!sect) {
goto finish;
}
sect->addr = (uint32_t) loadAddr;
} else {
struct mach_header_64 *mach_header = (struct mach_header_64 *) CFDataGetBytePtr(kernelImage);
struct section_64 *sect = macho_get_section_by_name_64(mach_header, segname, sectname);
if (!sect) {
goto finish;
}
sect->addr = loadAddr;
}
result = true;
finish:
return result;
}
static boolean_t __OSKextSetSectionSize(
CFDataRef kernelImage, const char *segname,
const char *sectname, uint64_t size)
{
boolean_t result = false;
if (!__OSKextIsArchitectureLP64()) {
struct mach_header *mach_header = (struct mach_header *) CFDataGetBytePtr(kernelImage);
struct section *sect = macho_get_section_by_name(mach_header, segname, sectname);
if (!sect) {
goto finish;
}
sect->size = (uint32_t) size;
} else {
struct mach_header_64 *mach_header = (struct mach_header_64 *) CFDataGetBytePtr(kernelImage);
struct section_64 *sect = macho_get_section_by_name_64(mach_header, segname, sectname);
if (!sect) {
goto finish;
}
sect->size = size;
}
result = true;
finish:
return result;
}
static boolean_t __OSKextSetSectionOffset(
CFDataRef kernelImage, const char *segname,
const char *sectname, uint32_t fileOffset)
{
boolean_t result = false;
if (!__OSKextIsArchitectureLP64()) {
struct mach_header *mach_header = (struct mach_header *) CFDataGetBytePtr(kernelImage);
struct section *sect = macho_get_section_by_name(mach_header, segname, sectname);
if (!sect) {
goto finish;
}
sect->offset = fileOffset;
} else {
struct mach_header_64 *mach_header = (struct mach_header_64 *) CFDataGetBytePtr(kernelImage);
struct section_64 *sect = macho_get_section_by_name_64(mach_header, segname, sectname);
if (!sect) {
goto finish;
}
sect->offset = fileOffset;
}
result = true;
finish:
return result;
}
static Boolean __OSKextConvertExeOfstsToLinkedOfsts(OSKextRef aKext)
{
uint32_t *kaslr_offsets;
uint32_t kaslr_offsets_count;
SegInfo linked_info[SEG_IDX_COUNT];
for (uint32_t i = 0; i < SEG_IDX_COUNT; i++) {
uint64_t seg_vmaddr, seg_vmsize, seg_fileoff;
char *segName = segIdxToName(i);
if (!__OSKextGetSegmentInfo(aKext->loadInfo->linkInfo.linkedKext,
segName, &seg_vmaddr, &seg_vmsize,
&seg_fileoff, NULL, NULL, true)) {
linked_info[i].vmaddr = 0;
linked_info[i].vmsize = 0;
linked_info[i].fileoff = 0;
continue;
}
linked_info[i].vmaddr = seg_vmaddr;
linked_info[i].vmsize = seg_vmsize;
linked_info[i].fileoff = seg_fileoff;
}
kaslr_offsets = aKext->loadInfo->linkInfo.kaslr_offsets;
kaslr_offsets_count = aKext->loadInfo->linkInfo.kaslr_offsets_count;
uint32_t __unused seg_ofst_count[SEG_IDX_COUNT] = {0};
kcgen_verboseLog("Converting %d offsets from exe -> linked", kaslr_offsets_count);
for (uint32_t i = 0; i < kaslr_offsets_count; i++) {
uint32_t kaslr_ofst;
uint64_t seg_ofst;
uint64_t linked_ofst;
char ofst_segname[MAX_SEGNAME_LEN];
uint64_t __unused exe_vmaddr;
uint64_t __unused exe_vmsize;
uint64_t __unused exe_fileoff;
uint64_t __unused exe_filesize;
enum enumSegIdx seg_idx;
kaslr_ofst = kaslr_offsets[i];
if (__OSKextGetSegmentInfoForOffset(aKext->loadInfo->linkInfo.kextExecutable,
(uint64_t)kaslr_ofst,
ofst_segname,
&seg_ofst,
&exe_vmaddr,
&exe_vmsize,
&exe_fileoff,
&exe_filesize) == false) {
char kextPath[PATH_MAX];
__OSKextGetFileSystemPath(aKext, NULL, false, kextPath);
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"kaslr could not find offset %u (%p) in kextExecutable '%s' <%s %d>",
kaslr_ofst, (void *) ((uint64_t)kaslr_ofst),
kextPath, __func__, __LINE__);
abort();
}
if (!getSegIndex(ofst_segname, &seg_idx)) {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Invalid/Unknown segment: %s <%s %d>",
ofst_segname, __func__, __LINE__);
abort();
}
if (seg_idx == SEG_IDX_LINKEDIT) {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Offset[%d]:%u points to LINKEDIT! <%s %d>",
i, kaslr_ofst, __func__, __LINE__);
abort();
}
if (linked_info[seg_idx].vmsize == 0) {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"segment offset:%llu (exe fileoff:%llu) is in zero-sized segment '%s'? <%s %d>",
seg_ofst, exe_fileoff, ofst_segname,
__func__, __LINE__);
abort();
}
linked_ofst = linked_info[seg_idx].vmaddr + seg_ofst;
if (linked_ofst > linked_info[seg_idx].vmaddr + linked_info[seg_idx].vmsize) {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"segment offset[%d]:%llu (exe fileoff:%llu) > %s: %llu + %llu, vmsize:%llu (kaslr_ofst:%u) <%s %d>",
i, seg_ofst, exe_fileoff, ofst_segname, linked_info[seg_idx].vmaddr,
linked_info[seg_idx].vmsize, linked_info[seg_idx].vmsize, kaslr_ofst,
__func__, __LINE__);
abort();
}
linked_ofst -= linked_info[SEG_IDX_TEXT].vmaddr;
kaslr_offsets[i] = (uint32_t)linked_ofst;
seg_ofst_count[seg_idx]++;
}
for (uint32_t i = 0; i < SEG_IDX_COUNT; i++) {
kcgen_verboseLog("\trelocs: %s:%d", segIdxToName(i), seg_ofst_count[i]);
}
return true;
}
static boolean_t
__OSKextHandlePointersForASLR(OSKextRef aKext, plkInfo *plkInfo, u_char *prelinkData,
uint64_t segKCOffsets[SEG_IDX_COUNT],
uintptr_t *pointersForASLR, size_t numPointers)
{
uint32_t slideOffset;
if (pointersForASLR) {
assert(numPointers > 0);
kcgen_verboseLog("\tHandling %lu adjustment pointers for ASLR", numPointers);
for (size_t i = 0; i < numPointers; i++) {
uintptr_t kcOffset;
CFNumberRef cfnum = NULL;
kcOffset = (*(pointersForASLR + i) - (uintptr_t)prelinkData);
slideOffset = kextcacheFileOffsetToPLKTEXTOffset((uint64_t)kcOffset, plkInfo);
cfnum = CFNumberCreate(kCFAllocatorDefault,
kCFNumberSInt32Type,
&slideOffset);
CFSetAddValue(plkInfo->kaslrOffsets, cfnum);
SAFE_RELEASE_NULL(cfnum);
}
free(pointersForASLR);
}
if (aKext->loadInfo->linkInfo.kaslr_offsets) {
uint32_t *kaslr_offsets;
uint32_t kaslr_offsets_count;
kaslr_offsets = aKext->loadInfo->linkInfo.kaslr_offsets;
kaslr_offsets_count = aKext->loadInfo->linkInfo.kaslr_offsets_count;
aKext->loadInfo->linkInfo.kaslr_offsets = NULL;
aKext->loadInfo->linkInfo.kaslr_offsets_count = 0;
uint32_t seg_ofst_count[SEG_IDX_COUNT] = {0};
kcgen_verboseLog("\tHandling %d kxld reloc pointers for ASLR", kaslr_offsets_count);
for (uint32_t i = 0; i < kaslr_offsets_count; i++) {
uint32_t kaslr_ofst;
CFNumberRef cfnum = NULL; uint64_t seg_ofst;
enum enumSegIdx seg_idx;
char seg_name[MAX_SEGNAME_LEN] = {0};
uint64_t kcOffset;
uint64_t __unused seg_vmaddr;
uint64_t __unused seg_vmsize;
uint64_t __unused seg_fileoff;
uint64_t __unused seg_filesize;
kaslr_ofst = kaslr_offsets[i];
if (__OSKextGetSegmentInfoForOffset(aKext->loadInfo->linkInfo.linkedKext,
(uint64_t)kaslr_ofst,
seg_name,
&seg_ofst,
&seg_vmaddr,
&seg_vmsize,
&seg_fileoff,
&seg_filesize) == false) {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"kaslr could not find offset[%d] %u (%p) in linkedKext <%s %d>",
i, kaslr_ofst, (void *)((uint64_t)kaslr_ofst), __func__, __LINE__);
abort();
}
if (!getSegIndex(seg_name, &seg_idx)) {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"kaslr bad segment lookup (%s)!!! <%s %d>",
seg_name, __func__, __LINE__);
abort();
}
if (seg_idx == SEG_IDX_LINKEDIT) {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"kaslr offset[%d]:%u points into LINKEDIT segment! <%s %d>",
i, kaslr_ofst, __func__, __LINE__);
abort();
}
assert(segKCOffsets[seg_idx] > 0);
kcOffset = segKCOffsets[seg_idx] + seg_ofst;
slideOffset = kextcacheFileOffsetToPLKTEXTOffset((uint64_t)kcOffset, plkInfo);
cfnum = CFNumberCreate(kCFAllocatorDefault,
kCFNumberSInt32Type,
&slideOffset);
CFSetAddValue(plkInfo->kaslrOffsets, cfnum);
SAFE_RELEASE_NULL(cfnum);
seg_ofst_count[seg_idx]++;
}
for (uint32_t i = 0; i < SEG_IDX_COUNT; i++) {
kcgen_verboseLog("\t\trelocs: %s:%d", segIdxToName(i), seg_ofst_count[i]);
}
free(kaslr_offsets);
}
return true;
}
static boolean_t
__OSKextCompactKext(OSKextRef aKext)
{
splitKextLinkInfo *linkInfo;
struct mach_header_64 *kext_mh;
u_char *new_kext_mh;
struct load_command *lc;
size_t offset;
struct segment_command_64 *le_seg, *dc_seg;
size_t mh_sz = 0, new_kext_sz = 0;
uint32_t dc_ofst_diff = 0, le_ofst_diff = 0;
linkInfo = &aKext->loadInfo->linkInfo;
kext_mh = (struct mach_header_64 *)(linkInfo->linkedKext);
mh_sz = sizeof(*kext_mh) + kext_mh->sizeofcmds;
le_seg = macho_get_segment_by_name_64(kext_mh, SEG_LINKEDIT);
if (!le_seg) {
char kextPath[PATH_MAX];
__OSKextGetFileSystemPath(aKext, NULL, false, kextPath);
kcgen_verboseLog("Cannot compress %s: no LINKEDIT segment!", kextPath);
return false;
}
new_kext_sz = mh_sz + le_seg->vmsize;
le_ofst_diff = (uint32_t)((uint64_t)le_seg->fileoff - (uint64_t)mh_sz);
dc_seg = macho_get_segment_by_name_64(kext_mh, SEG_DATA_CONST);
if (dc_seg) {
new_kext_sz += dc_seg->vmsize;
dc_ofst_diff = (uint32_t)((uint64_t)dc_seg->fileoff - (mh_sz + le_seg->vmsize));
}
new_kext_mh = (u_char *)malloc(new_kext_sz);
memcpy(new_kext_mh, kext_mh, mh_sz);
memcpy(new_kext_mh + mh_sz, linkInfo->linkedKext + le_seg->fileoff, le_seg->vmsize);
if (__OSKextShouldLog(aKext, kOSKextLogDebugLevel | kOSKextLogGeneralFlag)) {
char kextPath[PATH_MAX];
__OSKextGetFileSystemPath(aKext, NULL, false, kextPath);
kcgen_verboseLog("Compressing: %lld -> %lu bytes [%p-%p]",
(uint64_t)linkInfo->linkedKextSize, new_kext_sz,
(void *)new_kext_mh, (void *)(new_kext_mh + new_kext_sz));
}
le_seg = macho_get_segment_by_name_64((struct mach_header_64 *)new_kext_mh, SEG_LINKEDIT);
kcgen_verboseLog("\t__LINKEDIT %llu -> %llu (%llu bytes)", le_seg->fileoff, le_seg->fileoff - le_ofst_diff, le_seg->vmsize);
le_seg->fileoff -= le_ofst_diff;
if (dc_seg) {
memcpy(new_kext_mh + mh_sz + le_seg->vmsize,
linkInfo->linkedKext + dc_seg->fileoff, dc_seg->vmsize);
dc_seg = macho_get_segment_by_name_64((struct mach_header_64 *)new_kext_mh, SEG_DATA_CONST);
kcgen_verboseLog("\t__DATA_CONST %llu -> %llu (%llu bytes)", dc_seg->fileoff, dc_seg->fileoff - dc_ofst_diff, dc_seg->filesize);
dc_seg->fileoff -= dc_ofst_diff;
}
#define adj_off(_cmd, _member, _ofst) \
if ((_cmd)->_member > _ofst) { \
(_cmd)->_member = (__typeof__((_cmd)->_member))((uint32_t)((_cmd)->_member) - _ofst); \
kcgen_verboseLog("\t\t [%s] %s->%s = %d", getSegmentCommandName(lc->cmd), \
#_cmd, #_member, (uint32_t)((_cmd)->_member)); \
}
offset = sizeof(*kext_mh);
for (unsigned i = 0; i < ((struct mach_header_64 *)new_kext_mh)->ncmds; ++i) {
lc = (struct load_command *)(new_kext_mh + offset);
switch (lc->cmd) {
case LC_SEGMENT_64: {
struct segment_command_64 *seg_cmd = (struct segment_command_64 *)lc;
struct section_64 *sect = NULL;
sect = (struct section_64 *)(&seg_cmd[1]);
if (strcmp(seg_cmd->segname, SEG_LINKEDIT) != 0 && strcmp(seg_cmd->segname, SEG_DATA_CONST) != 0) {
seg_cmd->fileoff = 0;
seg_cmd->filesize = 0;
}
kcgen_verboseLog("\t\tseg %s (%u sects), fileoff:%lld",
seg_cmd->segname, seg_cmd->nsects, seg_cmd->fileoff);
for (uint32_t j = 0; j < seg_cmd->nsects; ++j, ++sect) {
if (seg_cmd->filesize == 0) {
sect->offset = 0;
sect->reloff = 0;
sect->nreloc = 0;
sect->size = 0;
sect->flags |= S_ZEROFILL;
} else {
adj_off(sect, offset, dc_ofst_diff);
adj_off(sect, reloff, le_ofst_diff);
}
kcgen_verboseLog("\t\t `->sect %s offset:%d, reloff:%d (%d relocs)", sect->sectname, sect->offset, sect->reloff, sect->nreloc);
}
} break;
case LC_SYMTAB: {
struct symtab_command *symtab_cmd = (struct symtab_command *)lc;
adj_off(symtab_cmd, symoff, le_ofst_diff);
adj_off(symtab_cmd, stroff, le_ofst_diff);
} break;
case LC_DYSYMTAB: {
struct dysymtab_command *dstab_cmd = (struct dysymtab_command *)lc;
adj_off(dstab_cmd, tocoff, le_ofst_diff);
adj_off(dstab_cmd, modtaboff, le_ofst_diff);
adj_off(dstab_cmd, extrefsymoff, le_ofst_diff);
adj_off(dstab_cmd, indirectsymoff, le_ofst_diff);
adj_off(dstab_cmd, extreloff, le_ofst_diff);
adj_off(dstab_cmd, locreloff, le_ofst_diff);
} break;
case LC_SEGMENT_SPLIT_INFO: {
struct linkedit_data_command *ldc = (struct linkedit_data_command *)lc;
adj_off(ldc, dataoff, le_ofst_diff);
} break;
}
offset += lc->cmdsize;
}
if (aKext->loadInfo->linkedExecutable) {
SAFE_RELEASE(aKext->loadInfo->linkedExecutable);
}
if (aKext->loadInfo->prelinkedExecutable) {
SAFE_RELEASE(aKext->loadInfo->prelinkedExecutable);
}
SAFE_FREE(linkInfo->linkedKext);
aKext->loadInfo->linkedExecutable = CFDataCreateWithBytesNoCopy(CFGetAllocator(aKext),
new_kext_mh,
new_kext_sz,
kCFAllocatorMalloc);
aKext->loadInfo->prelinkedExecutable = CFRetain(aKext->loadInfo->linkedExecutable);
linkInfo->linkedKext = new_kext_mh;
return true;
}
#define FAKE_32BIT_LOAD_ADDRESS (0x10000000)
#define LP64_LOAD_ADDRESS_OFFSET (2 * 1024 * 1024 * 1024ULL)
static uint64_t __OSKextGetFakeLoadAddress(CFDataRef kernelImage)
{
uint64_t result = 0;
const UInt8 * executable = NULL; const UInt8 * executableEnd = NULL; fat_iterator fatIterator = NULL; struct mach_header_64 * machHeader = NULL; void * machEnd = NULL; struct segment_command_64 * textSegment = NULL;
if (!__OSKextIsArchitectureLP64()) {
result = FAKE_32BIT_LOAD_ADDRESS;
goto finish;
}
if (!kernelImage) {
goto finish;
}
executable = CFDataGetBytePtr(kernelImage);
executableEnd = executable + CFDataGetLength(kernelImage);
fatIterator = fat_iterator_for_data(executable, executableEnd,
1 );
if (!fatIterator) {
OSKextLog( NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Can't read kernel file.");
goto finish;
}
machHeader = (struct mach_header_64 *)fat_iterator_find_arch(fatIterator,
OSKextGetArchitecture()->cputype, OSKextGetArchitecture()->cpusubtype,
(void **)&machEnd);
if (!machHeader) {
OSKextLog( NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Can't find architecture %s in kernel file.",
OSKextGetArchitecture()->name);
goto finish;
}
textSegment = macho_get_segment_by_name_64(machHeader, SEG_TEXT);
if (!textSegment) {
OSKextLog( NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Can't find text segment in kernel file.");
goto finish;
}
result = roundPageCrossSafeFixedWidth(textSegment->vmaddr + textSegment->vmsize)
- LP64_LOAD_ADDRESS_OFFSET;
finish:
if (fatIterator) {
fat_iterator_close(fatIterator);
}
return result;
}
static void __OSKextRemovePLKLinkedit(plkInfo *plkInfo)
{
CFMutableDataRef prelinkImage = plkInfo->kernelCacheImage;
CFIndex prelinkSize = CFDataGetLength(prelinkImage);
uint64_t vmaddr = getKCPlkSegVMAddr(plkInfo, SEG_IDX_LINKEDIT);
uint64_t vmsize = getKCPlkSegVMSize(plkInfo, SEG_IDX_LINKEDIT);
uint64_t fileoff = getKCPlkSegFileOff(plkInfo, SEG_IDX_LINKEDIT);
prelinkSize -= (CFIndex)vmsize;
CFDataSetLength(prelinkImage, prelinkSize);
setKCPlkSegVMSize(plkInfo, SEG_IDX_LINKEDIT, 0);
struct SegInfo *seg_info = &plkInfo->plk_INFO;
seg_info->vmaddr = vmaddr;
seg_info->fileoff = fileoff;
}
static CFArrayRef __OSKextPrelinkSplitKexts(
CFArrayRef kextArray,
plkInfo * plkInfo,
KXLDContext * kxldContext,
Boolean needAllFlag,
Boolean skipAuthenticationFlag,
Boolean printDiagnosticsFlag,
Boolean stripSymbolsFlag)
{
CFArrayRef result = NULL;
boolean_t success = false;
CFMutableArrayRef loadList = NULL;
OSKextLogSpec linkLogLevel;
char * kextIdentifierCString = NULL; CFIndex i;
int badLinkCount = 0;
u_char *prelinkData;
CFIndex prelinkDataSize;
linkLogLevel = needAllFlag ? kOSKextLogErrorLevel : kOSKextLogWarningLevel;
loadList = OSKextCopyLoadListForKexts(kextArray, needAllFlag);
if (!loadList) {
OSKextLog( NULL, kOSKextLogErrorLevel,
"Can't resolve dependencies amongst kexts for prelinked kernel.");
goto finish;
}
for (i = 0; i < CFArrayGetCount(loadList); ++i) {
OSKextRef aKext = (OSKextRef) CFArrayGetValueAtIndex(loadList, i);
if (!__OSKextCheckForPrelinkedKernel(aKext, needAllFlag,
skipAuthenticationFlag, printDiagnosticsFlag)) {
if (needAllFlag) {
OSKextLog( aKext, linkLogLevel | kOSKextLogLinkFlag,
"Aborting prelink.");
goto finish;
} else {
CFArrayRemoveValueAtIndex(loadList, i--);
}
}
}
for (i = 0; i < CFArrayGetCount(loadList); ++i) {
uint64_t orig_vmaddrs[SEG_IDX_COUNT], vmaddrs[SEG_IDX_COUNT], kc_offs[SEG_IDX_COUNT], sizes[SEG_IDX_COUNT];
OSKextRef aKext = (OSKextRef) CFArrayGetValueAtIndex(loadList, i);
SAFE_FREE_NULL(kextIdentifierCString);
if (!OSKextDeclaresExecutable(aKext)) {
continue;
}
kextIdentifierCString =
createUTF8CStringForCFString(OSKextGetIdentifier(aKext));
for (enum enumSegIdx idx = SEG_IDX_TEXT; idx < SEG_IDX_COUNT; ++idx) {
orig_vmaddrs[idx] = getKextVMAddr(aKext, idx);
}
success = __OSKextPerformSplitLink(aKext,
plkInfo,
kxldContext);
if (!success) {
if ( needAllFlag == false ) {
if (OSKextMatchesRequiredFlags(aKext,
kOSKextOSBundleRequiredRootFlag |
kOSKextOSBundleRequiredLocalRootFlag |
kOSKextOSBundleRequiredNetworkRootFlag)) {
if (++badLinkCount > 3) {
needAllFlag = true;
linkLogLevel = kOSKextLogErrorLevel;
}
}
}
OSKextLog( aKext, linkLogLevel | kOSKextLogLinkFlag,
"Prelink failed for %s; %s.",
kextIdentifierCString,
needAllFlag ? "aborting prelink" : "omitting from prelinked kernel");
if (needAllFlag) {
goto finish;
}
CFArrayRemoveValueAtIndex(loadList, i--);
continue;
}
if (nSplitKexts) {
assert(nNonSplitKexts == 0);
bzero(sizes, sizeof(sizes));
for (enum enumSegIdx idx = SEG_IDX_TEXT; idx < SEG_IDX_COUNT; ++idx) {
uint64_t va_base = getKCPlkSegVMAddr(plkInfo, idx);
uint64_t kc_base = getKCPlkSegFileOff(plkInfo, idx);
vmaddrs[idx] = getKextVMAddr(aKext, idx);
kc_offs[idx] = kc_base + (vmaddrs[idx] - va_base);
kcgen_verboseLog("seg %s va_base %llx vmaddr %llx kc_base %llx kc_off %llx",
segIdxToName(idx), va_base, vmaddrs[idx], kc_base, kc_offs[idx]);
}
prelinkData = CFDataGetMutableBytePtr(plkInfo->kernelCacheImage);
prelinkDataSize = CFDataGetLength(plkInfo->kernelCacheImage);
if (!__OSKextCopyToPLK(aKext, plkInfo, prelinkData, prelinkDataSize, orig_vmaddrs, vmaddrs, kc_offs, sizes, stripSymbolsFlag)) {
OSKextLog( aKext, linkLogLevel | kOSKextLogLinkFlag,
"%s %d Aborting prelink.",
__func__, __LINE__);
goto finish;
}
} else {
assert(nSplitKexts == 0);
if (!__OSKextOldCopyToPLK(aKext, plkInfo, stripSymbolsFlag)) {
OSKextLog( aKext, linkLogLevel | kOSKextLogLinkFlag,
"%s %d Aborting prelink.",
__func__, __LINE__);
goto finish;
}
}
}
if (stripSymbolsFlag && nSplitKexts != 0) {
kcgen_verboseLog("Stripping symbols and " kPrelinkLinkeditSegment "...");
__OSKextRemovePLKLinkedit(plkInfo);
}
result = CFRetain(loadList);
finish:
SAFE_RELEASE(loadList);
SAFE_FREE(kextIdentifierCString);
return result;
}
static CFArrayRef __OSKextPrelinkKexts(
CFArrayRef kextArray,
CFDataRef kernelImage,
uint64_t loadAddrBase,
uint64_t sourceAddrBase,
KXLDContext * kxldContext,
u_long * loadSizeOut,
Boolean needAllFlag,
Boolean skipAuthenticationFlag,
Boolean printDiagnosticsFlag,
Boolean stripSymbolsFlag)
{
CFArrayRef result = NULL;
boolean_t success = false;
CFMutableArrayRef loadList = NULL;
uint64_t loadAddr = loadAddrBase;
uint64_t sourceAddr = sourceAddrBase;
u_long loadSize = 0;
OSKextLogSpec linkLogLevel;
char * kextIdentifierCString = NULL; CFIndex i;
int badLinkCount = 0;
linkLogLevel = needAllFlag ? kOSKextLogErrorLevel : kOSKextLogWarningLevel;
loadList = OSKextCopyLoadListForKexts(kextArray, needAllFlag);
if (!loadList) {
OSKextLog( NULL, kOSKextLogErrorLevel,
"Can't resolve dependencies amongst kexts for prelinked kernel.");
goto finish;
}
for (i = 0; i < CFArrayGetCount(loadList); ++i) {
OSKextRef aKext = (OSKextRef) CFArrayGetValueAtIndex(loadList, i);
if (!__OSKextCheckForPrelinkedKernel(aKext, needAllFlag,
skipAuthenticationFlag, printDiagnosticsFlag)) {
if (needAllFlag) {
OSKextLog( aKext, linkLogLevel | kOSKextLogLinkFlag,
"Aborting prelink.");
goto finish;
} else {
CFArrayRemoveValueAtIndex(loadList, i--);
}
}
}
for (i = 0; i < CFArrayGetCount(loadList); ++i) {
OSKextRef aKext = (OSKextRef) CFArrayGetValueAtIndex(loadList, i);
SAFE_FREE_NULL(kextIdentifierCString);
if (!OSKextDeclaresExecutable(aKext)) {
continue;
}
kextIdentifierCString =
createUTF8CStringForCFString(OSKextGetIdentifier(aKext));
loadAddr = loadAddrBase + loadSize;
sourceAddr = sourceAddrBase + loadSize;
OSKextSetLoadAddress(aKext, loadAddr);
aKext->loadInfo->sourceAddress = sourceAddr;
success = __OSKextPerformLink(aKext, kernelImage,
0, stripSymbolsFlag, kxldContext);
if (!success) {
if ( needAllFlag == false ) {
if (OSKextMatchesRequiredFlags(aKext,
kOSKextOSBundleRequiredRootFlag |
kOSKextOSBundleRequiredLocalRootFlag |
kOSKextOSBundleRequiredNetworkRootFlag)) {
if (++badLinkCount > 3) {
needAllFlag = true;
linkLogLevel = kOSKextLogErrorLevel;
}
}
}
OSKextLog( aKext, linkLogLevel | kOSKextLogLinkFlag,
"Prelink failed for %s; %s.",
kextIdentifierCString,
needAllFlag ? "aborting prelink" : "omitting from prelinked kernel");
if (needAllFlag) {
goto finish;
}
CFArrayRemoveValueAtIndex(loadList, i--);
continue;
}
loadSize += roundPageCrossSafe(aKext->loadInfo->linkInfo.linkedKextSize);
}
result = CFRetain(loadList);
*loadSizeOut = loadSize;
finish:
SAFE_RELEASE(loadList);
SAFE_FREE(kextIdentifierCString);
return result;
}
typedef struct kaslrPackedOffsets {
uint32_t count;
uint32_t offsetsArray[];
} kaslrPackedOffsets;
static CFDataRef __OSKextCreatePrelinkInfoDictionary(
plkInfo *plkInfo,
CFArrayRef loadList,
CFURLRef volumeRootURL,
Boolean includeAllPersonalities,
Boolean isSplitKexts,
CFDataRef kernelUUID)
{
CFDataRef result = NULL;
char kextPath[PATH_MAX] = "";
char volumePath[PATH_MAX] = "";
CFArrayRef allKextsByBundleID = NULL; CFArrayRef kextPersonalities = NULL; CFMutableArrayRef kextInfoDictArray = NULL; CFDataRef prelinkInfoData = NULL; CFDataRef uuid = NULL; CFMutableDictionaryRef kextInfoDict = NULL; CFMutableDictionaryRef prelinkInfoDict = NULL; CFNumberRef cfnum = NULL; CFStringRef bundleVolPath = NULL; CFStringRef executableRelPath = NULL; CFStringRef archPersonalitiesKey = NULL; CFSetRef loadListIDs = NULL; char * kextVolPath = NULL; int i = 0;
int count = 0;
CC_SHA256_CTX ctx;
unsigned char kernelCacheHash[CC_SHA256_DIGEST_LENGTH];
CFDataRef kcID = NULL;
if (volumeRootURL) {
if (!CFURLGetFileSystemRepresentation(volumeRootURL,
TRUE, (UInt8 *)volumePath, sizeof(volumePath))) {
OSKextLogStringError( NULL);
goto finish;
}
}
prelinkInfoDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (!prelinkInfoDict) {
OSKextLogMemError();
goto finish;
}
kextInfoDictArray = CFArrayCreateMutable(kCFAllocatorDefault,
CFArrayGetCount(loadList), &kCFTypeArrayCallBacks);
if (!kextInfoDictArray) {
OSKextLogMemError();
goto finish;
}
CFDictionarySetValue(prelinkInfoDict, CFSTR(kPrelinkInfoDictionaryKey),
kextInfoDictArray);
archPersonalitiesKey = __OSKextCreateCompositeKey(
CFSTR("kIOKitPersonalitiesKey"), OSKextGetArchitecture()->name);
if (!archPersonalitiesKey) {
OSKextLogMemError();
goto finish;
}
int offsets = 0;
if (isSplitKexts && plkInfo->kaslrOffsets && (offsets = CFSetGetCount(plkInfo->kaslrOffsets))) {
kaslrPackedOffsets * myPackedOffsets = NULL;
int mySize;
CFDataRef kaslrOffsetsData = NULL;
mySize = sizeof(*myPackedOffsets) + (offsets * sizeof(uint32_t));
myPackedOffsets = (kaslrPackedOffsets *) malloc(mySize);
if (myPackedOffsets == NULL) {
OSKextLogMemError();
goto finish;
}
myPackedOffsets->count = 0;
CFSetApplyFunction(plkInfo->kaslrOffsets, (CFSetApplierFunction)__OSKextPackKASLROffsets, myPackedOffsets);
if (myPackedOffsets->count == 0) {
OSKextLogMemError();
goto finish;
}
CFRelease(plkInfo->kaslrOffsets);
plkInfo->kaslrOffsets = NULL;
kaslrOffsetsData = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)myPackedOffsets, mySize);
if (kaslrOffsetsData == NULL) {
OSKextLogMemError();
goto finish;
}
CFDictionarySetValue(prelinkInfoDict,
CFSTR(kPrelinkLinkKASLROffsetsKey),
kaslrOffsetsData);
free(myPackedOffsets);
}
CC_SHA256_Init(&ctx);
if (kernelUUID) {
CC_SHA256_Update(&ctx, (unsigned char*) CFDataGetBytePtr(kernelUUID), CFDataGetLength(kernelUUID));
}
count = CFArrayGetCount(loadList);
for (i = 0; i < count; ++i) {
Boolean gotPath = FALSE;
OSKextRef aKext = (OSKextRef)CFArrayGetValueAtIndex(loadList, i);
SAFE_RELEASE_NULL(kextInfoDict);
SAFE_RELEASE_NULL(bundleVolPath);
gotPath = __OSKextGetFileSystemPath(aKext, NULL,
true, kextPath);
OSKextLog(aKext, kOSKextLogStepLevel | kOSKextLogArchiveFlag,
"Adding %s to prelinked kernel.", kextPath);
kextInfoDict = OSKextCopyInfoDictionary(aKext);
if (!kextInfoDict) {
OSKextLogMemError();
goto finish;
}
if (!includeAllPersonalities) {
if (!__OSKextRequiredAtEarlyBoot(aKext)) {
CFDictionaryRemoveValue(kextInfoDict, CFSTR(kIOKitPersonalitiesKey));
CFDictionaryRemoveValue(kextInfoDict, archPersonalitiesKey);
}
}
if (OSKextDeclaresExecutable(aKext)) {
if (isSplitKexts) {
int64_t kextExecSize = (int64_t)(aKext->loadInfo->linkInfo.linkedKextSize);
cfnum = CFNumberCreate(kCFAllocatorDefault,
kCFNumberSInt64Type,
&aKext->loadInfo->linkInfo.vmaddr_TEXT);
if (!cfnum) {
OSKextLogMemError();
goto finish;
}
CFDictionarySetValue(kextInfoDict, CFSTR(kPrelinkExecutableLoadKey), cfnum);
CFDictionarySetValue(kextInfoDict, CFSTR(kPrelinkExecutableSourceKey), cfnum);
SAFE_RELEASE_NULL(cfnum);
if (aKext->loadInfo->linkInfo.vmaddr_TEXT_EXEC) {
struct mach_header_64 * kextHeader = (struct mach_header_64 *)(aKext->loadInfo->linkInfo.linkedKext);
struct segment_command_64 *seg_cmd;
seg_cmd = macho_get_segment_by_name_64(kextHeader, SEG_TEXT);
if (seg_cmd)
kextExecSize = (int64_t)(seg_cmd->vmsize);
}
cfnum = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &kextExecSize);
if (!cfnum) {
OSKextLogMemError();
goto finish;
}
CFDictionarySetValue(kextInfoDict, CFSTR(kPrelinkExecutableSizeKey), cfnum);
SAFE_RELEASE_NULL(cfnum);
uint64_t seg_addr = 0;
for (enum enumSegIdx idx = SEG_IDX_TEXT; idx < SEG_IDX_COUNT; ++idx) {
seg_addr = getKextVMAddr(aKext, idx);
CC_SHA256_Update(&ctx, &seg_addr, sizeof(seg_addr));
}
}
else {
cfnum = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type,
&aKext->loadInfo->linkInfo.vmaddr_TEXT);
if (!cfnum) {
OSKextLogMemError();
goto finish;
}
CFDictionarySetValue(kextInfoDict, CFSTR(kPrelinkExecutableLoadKey), cfnum);
SAFE_RELEASE_NULL(cfnum);
cfnum = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type,
&aKext->loadInfo->sourceAddress);
if (!cfnum) {
OSKextLogMemError();
goto finish;
}
CFDictionarySetValue(kextInfoDict, CFSTR(kPrelinkExecutableSourceKey), cfnum);
SAFE_RELEASE_NULL(cfnum);
u_long num;
num = CFDataGetLength(aKext->loadInfo->prelinkedExecutable);
cfnum = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &num);
if (!cfnum) {
OSKextLogMemError();
goto finish;
}
CFDictionarySetValue(kextInfoDict, CFSTR(kPrelinkExecutableSizeKey), cfnum);
SAFE_RELEASE_NULL(cfnum);
CC_SHA256_Update(&ctx, &aKext->loadInfo->linkInfo.vmaddr_TEXT,
sizeof(aKext->loadInfo->linkInfo.vmaddr_TEXT));
}
cfnum = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type,
&aKext->loadInfo->kmodInfoAddress);
if (!cfnum) {
OSKextLogMemError();
goto finish;
}
CFDictionarySetValue(kextInfoDict, CFSTR(kPrelinkKmodInfoKey), cfnum);
SAFE_RELEASE_NULL(cfnum);
}
uuid = OSKextCopyUUIDForArchitecture(aKext, OSKextGetArchitecture());
if (uuid) {
CC_SHA256_Update(&ctx, CFDataGetBytePtr(uuid), CFDataGetLength(uuid));
if (OSKextDeclaresExecutable(aKext) && OSKextIsInterface(aKext)) {
CFDictionarySetValue(kextInfoDict, CFSTR(kPrelinkInterfaceUUIDKey),
uuid);
}
SAFE_RELEASE_NULL(uuid);
}
if (gotPath) {
kextVolPath = __absPathOnVolume(kextPath, volumePath);
if (!kextVolPath) {
OSKextLogMemError();
goto finish;
}
bundleVolPath = CFStringCreateWithBytes(CFGetAllocator(aKext),
(UInt8 *)kextVolPath, strlen(kextVolPath),
kCFStringEncodingUTF8, false);
if (!bundleVolPath) {
OSKextLogMemError();
goto finish;
}
CFDictionarySetValue(kextInfoDict, CFSTR(kPrelinkBundlePathKey), bundleVolPath);
executableRelPath = __OSKextCopyExecutableRelativePath(aKext);
if (executableRelPath) {
CFDictionarySetValue(kextInfoDict, CFSTR(kPrelinkExecutableRelativePathKey),
executableRelPath);
}
}
CFArrayAppendValue(kextInfoDictArray, kextInfoDict);
}
CC_SHA256_Final((unsigned char*)&kernelCacheHash, &ctx);
kcID = CFDataCreate(kCFAllocatorDefault, kernelCacheHash, sizeof(uuid_t));
if (kcID) {
CFDictionarySetValue(prelinkInfoDict, CFSTR(kPrelinkInfoKCIDKey), kcID);
printf("KernelCache ID: ");
for (unsigned long i = 0; i < sizeof(kernelCacheHash)/2; i++) {
printf("%02X", kernelCacheHash[i]);
}
printf("\n");
} else {
kcgen_verboseLog("Failed to allocate kernelcache ID");
}
prelinkInfoData = IOCFSerialize(prelinkInfoDict, kNilOptions);
if (!prelinkInfoData) {
OSKextLogMemError();
goto finish;
}
result = CFRetain(prelinkInfoData);
finish:
SAFE_RELEASE(allKextsByBundleID);
SAFE_RELEASE(kextPersonalities);
SAFE_RELEASE(kextInfoDictArray);
SAFE_RELEASE(prelinkInfoData);
SAFE_RELEASE(uuid);
SAFE_RELEASE(kcID);
SAFE_RELEASE(kextInfoDict);
SAFE_RELEASE(prelinkInfoDict);
SAFE_RELEASE(cfnum);
SAFE_RELEASE(bundleVolPath);
SAFE_RELEASE(executableRelPath);
SAFE_RELEASE(archPersonalitiesKey);
SAFE_RELEASE(loadListIDs);
return result;
}
static void __OSKextPackKASLROffsets(CFNumberRef value, void *context)
{
kaslrPackedOffsets * myPackedOffsets = (kaslrPackedOffsets *) context;
uint32_t myOffset;
if (CFNumberGetValue(value, kCFNumberSInt32Type, (SInt32 *)&myOffset)) {
myPackedOffsets->offsetsArray[myPackedOffsets->count++] = myOffset;
}
return;
}
static Boolean
__OSKextRequiredAtEarlyBoot(
OSKextRef theKext)
{
CFStringRef bundleRequired = NULL;
bundleRequired = (CFStringRef)OSKextGetValueForInfoDictionaryKey(theKext,
CFSTR(kOSBundleRequiredKey));
return (bundleRequired && kCFCompareEqualTo !=
CFStringCompare(bundleRequired, CFSTR(kOSBundleRequiredSafeBoot), 0));
}
#if 0
static CFArrayRef
__OSKextCopyKextsByBundleID(void)
{
CFArrayRef result = NULL; CFArrayRef kextList = NULL; CFStringRef * bundleIDs = NULL; OSKextRef * theKexts = NULL; int count = 0;
int i = 0;
count = CFDictionaryGetCount(__sOSKextsByIdentifier);
bundleIDs = malloc(count * sizeof(CFStringRef));
if (!bundleIDs) {
OSKextLogMemError();
goto finish;
}
CFDictionaryGetKeysAndValues(__sOSKextsByIdentifier,
(const void **)bundleIDs, NULL);
theKexts = malloc(count * sizeof(OSKextRef));
if (!theKexts) {
OSKextLogMemError();
goto finish;
}
for (i = 0; i < count; ++i) {
theKexts[i] = OSKextGetKextWithIdentifier(bundleIDs[i]);
if (!theKexts[i]) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLinkFlag,
"Internal error: failed to find a kext with bundle ID: %s",
CFStringGetCStringPtr(bundleIDs[i], kCFStringEncodingMacRoman));
goto finish;
}
}
kextList = CFArrayCreate(kCFAllocatorDefault, (const void **) theKexts,
count, &kCFTypeArrayCallBacks);
if (!kextList) {
OSKextLogMemError();
goto finish;
}
result = CFRetain(kextList);
finish:
SAFE_RELEASE(kextList);
SAFE_FREE(bundleIDs);
SAFE_FREE(theKexts);
return result;
}
static CFSetRef
__OSKextCopyBundleIDsForKexts(
CFArrayRef theKexts)
{
CFSetRef result = NULL; CFSetRef bundleIDSet = NULL; CFStringRef * bundleIDs = NULL; OSKextRef aKext = NULL; int count = 0;
int i = 0;
count = CFArrayGetCount(theKexts);
bundleIDs = malloc(count * sizeof(CFStringRef));
if (!bundleIDs) {
OSKextLogMemError();
goto finish;
}
for (i = 0; i < count; ++i) {
aKext = (OSKextRef) CFArrayGetValueAtIndex(theKexts, i);
bundleIDs[i] = aKext->bundleID;
}
bundleIDSet = CFSetCreate(kCFAllocatorDefault,
(const void **) bundleIDs, count, &kCFTypeSetCallBacks);
if (!bundleIDSet) {
OSKextLogMemError();
goto finish;
}
result = CFRetain(bundleIDSet);
finish:
SAFE_RELEASE(bundleIDSet);
SAFE_FREE(bundleIDs);
return result;
}
#endif
static u_long __OSKextCopyPrelinkedKexts(
CFMutableDataRef prelinkImage,
CFArrayRef loadList,
u_long fileOffsetBase,
uint64_t sourceAddrBase)
{
boolean_t success = false;
u_char * prelinkData = CFDataGetMutableBytePtr(prelinkImage);
u_long size = 0;
u_long totalSize = 0;
u_long fileOffset = fileOffsetBase;
uint64_t sourceAddr = sourceAddrBase;
int i = 0;
success = __OSKextSetSegmentAddress(prelinkImage, kPrelinkTextSegment,
sourceAddrBase);
if (!success) {
goto finish;
}
success = __OSKextSetSegmentOffset(prelinkImage, kPrelinkTextSegment,
fileOffsetBase);
if (!success) {
goto finish;
}
success = __OSKextSetSectionAddress(prelinkImage, kPrelinkTextSegment,
kPrelinkTextSection, sourceAddrBase);
if (!success) {
goto finish;
}
success = __OSKextSetSectionOffset(prelinkImage, kPrelinkTextSegment,
kPrelinkTextSection, fileOffset);
if (!success) {
goto finish;
}
for (i = 0; i < CFArrayGetCount(loadList); ++i) {
OSKextRef aKext = (OSKextRef) CFArrayGetValueAtIndex(loadList, i);
if (!OSKextDeclaresExecutable(aKext)) {
continue;
}
memcpy(prelinkData + fileOffset + size,
CFDataGetBytePtr(aKext->loadInfo->prelinkedExecutable),
CFDataGetLength(aKext->loadInfo->prelinkedExecutable));
size += roundPageCrossSafe(CFDataGetLength(aKext->loadInfo->prelinkedExecutable));
}
sourceAddr += size;
fileOffset += size;
totalSize += size;
success = __OSKextSetSegmentVMSize(prelinkImage, kPrelinkTextSegment, size);
if (!success) {
goto finish;
}
success = __OSKextSetSegmentFilesize(prelinkImage, kPrelinkTextSegment, size);
if (!success) {
goto finish;
}
success = __OSKextSetSectionSize(prelinkImage, kPrelinkTextSegment,
kPrelinkTextSection, size);
if (!success) {
goto finish;
}
finish:
return totalSize;
}
static Boolean __OSKextCopySplitPrelinkInfoDictionary(
plkInfo * plkInfo,
CFDataRef prelinkInfoData)
{
Boolean success = false;
u_char * kernelCacheData = CFDataGetMutableBytePtr(plkInfo->kernelCacheImage);
u_long size = CFDataGetLength(prelinkInfoData);
memcpy(kernelCacheData + plkInfo->plk_INFO.fileoff,
CFDataGetBytePtr(prelinkInfoData), size);
#if SPLIT_KEXTS_DEBUG
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"copy prelink info data to kernelcache file %p to %p (%lu) <%s %d>",
(void *) kernelCacheData + plkInfo->plk_INFO.fileoff,
(void *) kernelCacheData + plkInfo->plk_INFO.fileoff + size,
size,
__func__, __LINE__);
size = CFDataGetLength(plkInfo->kernelCacheImage);
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"kernel cache start %p end %p (len %lu) <%s %d>",
(void *) kernelCacheData,
(void *) (kernelCacheData + size),
size,
__func__, __LINE__);
u_char * end_kernel_cache;
u_char * end_copy;
end_copy = kernelCacheData + plkInfo->plk_INFO.fileoff + CFDataGetLength(prelinkInfoData);
end_kernel_cache = kernelCacheData + CFDataGetLength(plkInfo->kernelCacheImage);
if (end_copy > end_kernel_cache) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"overflow!!! prelink info end %p > kernelcache file end %p <%s %d>",
end_copy, end_kernel_cache,
__func__, __LINE__);
abort();
}
#endif
if (__OSKextSetSegmentAddress(plkInfo->kernelCacheImage,
kPrelinkInfoSegment,
plkInfo->plk_INFO.vmaddr) == false) {
goto finish;
}
if (__OSKextSetSegmentVMSize(plkInfo->kernelCacheImage,
kPrelinkInfoSegment,
plkInfo->plk_INFO.vmsize) == false) {
goto finish;
}
if (__OSKextSetSegmentOffset(plkInfo->kernelCacheImage,
kPrelinkInfoSegment,
plkInfo->plk_INFO.fileoff) == false) {
goto finish;
}
if (__OSKextSetSegmentFilesize(plkInfo->kernelCacheImage,
kPrelinkInfoSegment,
plkInfo->plk_INFO.vmsize) == false) {
goto finish;
}
if (__OSKextSetSectionAddress(plkInfo->kernelCacheImage,
kPrelinkInfoSegment,
kPrelinkInfoSection,
plkInfo->plk_INFO.vmaddr) == false) {
goto finish;
}
if (__OSKextSetSectionOffset(plkInfo->kernelCacheImage,
kPrelinkInfoSegment,
kPrelinkInfoSection,
plkInfo->plk_INFO.fileoff) == false) {
goto finish;
}
if (__OSKextSetSectionSize(plkInfo->kernelCacheImage,
kPrelinkInfoSegment,
kPrelinkInfoSection,
plkInfo->plk_INFO.vmsize) == false) {
goto finish;
}
success = true;
finish:
if (success == false) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s %d - failed ",
__func__, __LINE__);
}
return success;
}
static u_long __OSKextCopyPrelinkInfoDictionary(
CFMutableDataRef prelinkImage,
CFDataRef prelinkInfoData,
u_long fileOffset,
uint64_t sourceAddr)
{
boolean_t success = false;
u_char * prelinkData = CFDataGetMutableBytePtr(prelinkImage);
u_long size = 0;
size = CFDataGetLength(prelinkInfoData);
memcpy(prelinkData + fileOffset, CFDataGetBytePtr(prelinkInfoData), size);
success = __OSKextSetSegmentAddress(prelinkImage, kPrelinkInfoSegment,
sourceAddr);
if (!success) {
goto finish;
}
success = __OSKextSetSegmentVMSize(prelinkImage, kPrelinkInfoSegment,
roundPageCrossSafe(size));
if (!success) {
goto finish;
}
success = __OSKextSetSegmentOffset(prelinkImage, kPrelinkInfoSegment,
fileOffset);
if (!success) {
goto finish;
}
success = __OSKextSetSegmentFilesize(prelinkImage, kPrelinkInfoSegment,
size);
if (!success) {
goto finish;
}
success = __OSKextSetSectionAddress(prelinkImage, kPrelinkInfoSegment,
kPrelinkInfoSection, sourceAddr);
if (!success) {
goto finish;
}
success = __OSKextSetSectionOffset(prelinkImage, kPrelinkInfoSegment,
kPrelinkInfoSection, fileOffset);
if (!success) {
goto finish;
}
success = __OSKextSetSectionSize(prelinkImage, kPrelinkInfoSegment,
kPrelinkInfoSection, size);
if (!success) {
goto finish;
}
finish:
return roundPageCrossSafe(size);
}
CFDataRef OSKextCreatePrelinkedKernel(
CFDataRef kernelImage,
CFArrayRef kextArray,
CFURLRef volumeRootURL,
uint32_t flags,
CFDictionaryRef * symbolsOut)
{
CFDataRef result = NULL;
kern_return_t kxldResult = KERN_FAILURE;
boolean_t success = false;
boolean_t swapped = false;
boolean_t kextsFirst = false;
KXLDContext * kxldContext = NULL;
KXLDFlags kxldFlags = kKxldFlagDefault;
CFArrayRef loadList = NULL;
CFDataRef prelinkInfoData = NULL;
CFMutableDictionaryRef symbols = NULL;
u_long prelinkSize = 0;
u_long size = 0;
uint64_t kxldPageSize = 0;
uint64_t lastKernelLoadAddr = 0;
uint64_t kextTextLoadAddr = 0;
plkInfo plkInfo;
uint32_t fileOffset = 0;
CFMutableDataRef prelinkImage = NULL;
uint32_t baseFileOffset = 0;
uint64_t textLoadAddr = 0;
uint64_t textVMSize = 0;
uint64_t baseLoadAddr = 0;
uint64_t baseSourceAddr = 0;
uint64_t sourceAddr = 0;
boolean_t isARM64 = false;
CFDataRef kernelUUID = NULL;
const struct mach_header * mach_header = NULL;
const void * file_end;
macho_seek_result seek_result;
struct _uuid_stuff seek_uuid;
int uuid_order_swap = 0;
bzero((UInt8 *)&plkInfo, sizeof(plkInfo));
if (flags & kOSKextKernelcacheKASLRFlag) {
kxldFlags |= kKXLDFlagIncludeRelocs;
}
if (__sOSKextStrictAuthentication &&
(flags & kOSKextKernelcacheSkipAuthenticationFlag) != 0) {
OSKextLog( NULL, kOSKextLogWarningLevel | kOSKextLogLinkFlag,
"Ignoring skip authentication flag due to strict authentication mode.");
flags &= ~kOSKextKernelcacheSkipAuthenticationFlag;
}
kxldPageSize = __OSKextSetupCrossLinkByArch(OSKextGetArchitecture()->cputype);
if (isCrossLinking()) {
kxldPageSize = getEffectivePageSize();
} else {
kxldPageSize = 0;
}
#if defined(CPU_TYPE_ARM64)
if (OSKextGetArchitecture()->cputype == CPU_TYPE_ARM64) {
isARM64 = true;
}
#endif
kxldResult = kxld_create_context(&kxldContext,
isARM64 ? NULL : __OSKextLinkAddressCallback,
__OSKextLoggingCallback,
kxldFlags,
OSKextGetArchitecture()->cputype,
OSKextGetArchitecture()->cpusubtype,
kxldPageSize);
if (kxldResult != KERN_SUCCESS) {
OSKextLog( NULL, kOSKextLogErrorLevel | kOSKextLogLinkFlag,
"Can't create link context.");
goto finish;
}
swapped = __OSKextSwapHeaders(kernelImage);
if (isARM64) {
kextsFirst = __OSKextWantKextsFirst(kernelImage);
success = __OSKextInit_plkInfo(kernelImage, kextArray, &plkInfo);
if (!success) {
OSKextLog( NULL, kOSKextLogErrorLevel,
"Can't get kextcache segment info.");
goto finish;
}
goto doLinks;
}
success = __OSKextGetLastKernelLoadAddr(kernelImage, &baseSourceAddr);
if (!success) {
OSKextLog( NULL, kOSKextLogErrorLevel,
"Can't get last load address for kernel.");
goto finish;
}
baseFileOffset = roundPageCrossSafe(CFDataGetLength(kernelImage));
baseSourceAddr = roundPageCrossSafeFixedWidth(baseSourceAddr);
if (kextsFirst) {
lastKernelLoadAddr = baseSourceAddr;
}
if (OSKextGetArchitecture()->cputype == CPU_TYPE_X86_64) {
success = __OSKextGetSegmentAddressAndOffsetDataRef(kernelImage,
SEG_TEXT, NULL, &textLoadAddr);
if (!success) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"Could not get kernel text address.");
goto finish;
}
success = __OSKextGetSegmentFileAndVMSizeDataRef(kernelImage,
SEG_TEXT, NULL, &textVMSize);
if (!success) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"Could not get kernel text vmsize.");
goto finish;
}
baseLoadAddr = roundPageCrossSafeFixedWidth(textLoadAddr + textVMSize);
if (baseLoadAddr < __kOSKextMaxKextDisplacement_x86_64) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"Kext base load address underflow.");
goto finish;
}
baseLoadAddr -= __kOSKextMaxKextDisplacement_x86_64;
} else {
if (kextsFirst) {
success = __OSKextGetSegmentAddressAndOffsetDataRef(kernelImage,
kPrelinkTextSegment,
NULL,
&kextTextLoadAddr);
if (!success) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"Could not get kernel text address.");
goto finish;
}
baseSourceAddr = roundPageCrossSafeFixedWidth(kextTextLoadAddr);
}
baseLoadAddr = baseSourceAddr;
}
prelinkSize = baseFileOffset;
sourceAddr = baseSourceAddr;
doLinks:
if (isARM64) {
loadList = __OSKextPrelinkSplitKexts(kextArray,
&plkInfo,
kxldContext,
(flags & kOSKextKernelcacheNeedAllFlag),
(flags & kOSKextKernelcacheSkipAuthenticationFlag),
(flags & kOSKextKernelcachePrintDiagnosticsFlag),
(flags & kOSKextKernelcacheStripSymbolsFlag));
}
else {
loadList = __OSKextPrelinkKexts(kextArray,
kernelImage,
baseLoadAddr, sourceAddr,
kxldContext,
&size,
(flags & kOSKextKernelcacheNeedAllFlag),
(flags & kOSKextKernelcacheSkipAuthenticationFlag),
(flags & kOSKextKernelcachePrintDiagnosticsFlag),
(flags & kOSKextKernelcacheStripSymbolsFlag));
}
if (!loadList) {
goto finish;
}
if (isARM64 == false) {
prelinkSize += size;
sourceAddr += size;
}
if (isARM64) {
mach_header = (const struct mach_header *)CFDataGetBytePtr(plkInfo.kernelImage);
file_end = (((const char *)mach_header) + CFDataGetLength(plkInfo.kernelImage));
} else {
mach_header = (const struct mach_header *)CFDataGetBytePtr(kernelImage);
file_end = (((const char *)mach_header) + CFDataGetLength(kernelImage));
}
if (ISSWAPPEDMACHO(MAGIC32(mach_header))) {
uuid_order_swap = 1;
}
seek_result = macho_scan_load_commands(mach_header, file_end, __OSKextUUIDCallback, (const void **)&seek_uuid);
if (seek_result == macho_seek_result_found) {
kernelUUID = CFDataCreate(kCFAllocatorDefault, (u_char *)seek_uuid.uuid,
CondSwapInt32(uuid_order_swap, seek_uuid.uuid_size));
} else {
kcgen_verboseLog("Unable to find kernel UUID from kernel image, macho_scan_load_commands returned %d", macho_seek_result_found);
}
prelinkInfoData = __OSKextCreatePrelinkInfoDictionary(&plkInfo,
loadList,
volumeRootURL,
(flags & kOSKextKernelcacheIncludeAllPersonalitiesFlag),
isARM64,
kernelUUID);
if (!prelinkInfoData) {
goto finish;
}
size = roundPageCrossSafe(CFDataGetLength(prelinkInfoData));
if (isARM64) {
CFIndex kcSize = CFDataGetLength(plkInfo.kernelCacheImage);
kcgen_verboseLog("grow plkInfo.kernelCacheImage by size %ld to %ld",
size, kcSize + size);
plkInfo.plk_INFO.vmsize = size;
CFMutableDataRef expandedImage = NULL;
expandedImage = CFDataCreateMutableCopy(kCFAllocatorDefault,
kcSize + size,
plkInfo.kernelCacheImage);
if (expandedImage == NULL) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s %d - expandedImage failed",
__func__, __LINE__);
goto finish;
}
CFRelease(plkInfo.kernelCacheImage);
plkInfo.kernelCacheImage = expandedImage;
CFDataSetLength(plkInfo.kernelCacheImage, kcSize + size);
#if SPLIT_KEXTS_DEBUG
__OSKextShowPLKInfo(&plkInfo);
#endif
if (swapped) {
__OSKextUnswapHeaders(kernelImage);
swapped = false;
}
CFDataReplaceBytes(plkInfo.kernelCacheImage,
CFRangeMake(0, CFDataGetLength(kernelImage)),
CFDataGetBytePtr(kernelImage),
CFDataGetLength(kernelImage));
if (__OSKextCopySplitPrelinkInfoDictionary(&plkInfo, prelinkInfoData) == false) {
goto finish;
}
if (__OSKextSetPLKSegInfo(&plkInfo) == false) {
goto finish;
}
}
else {
prelinkSize += size;
prelinkImage = CFDataCreateMutable(kCFAllocatorDefault, prelinkSize);
if (!prelinkImage) {
OSKextLogMemError();
goto finish;
}
CFDataSetLength(prelinkImage, prelinkSize);
if (swapped) {
__OSKextUnswapHeaders(kernelImage);
swapped = false;
}
CFDataReplaceBytes(prelinkImage, CFRangeMake(0, CFDataGetLength(kernelImage)),
CFDataGetBytePtr(kernelImage), CFDataGetLength(kernelImage));
fileOffset = baseFileOffset;
sourceAddr = baseSourceAddr;
size = __OSKextCopyPrelinkedKexts(prelinkImage,
loadList,
fileOffset,
sourceAddr);
fileOffset += size;
if (kextsFirst) {
sourceAddr = lastKernelLoadAddr;
}
else {
sourceAddr += size;
}
size = __OSKextCopyPrelinkInfoDictionary(prelinkImage, prelinkInfoData,
fileOffset, sourceAddr);
fileOffset += size;
sourceAddr += size;
CFDataSetLength(prelinkImage, fileOffset);
}
if (symbolsOut) {
int i = 0;
symbols = CFDictionaryCreateMutable(kCFAllocatorDefault,
CFArrayGetCount(loadList), &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!symbols) {
goto finish;
}
for (i = 0; i < CFArrayGetCount(loadList); ++i) {
OSKextRef aKext = (OSKextRef) CFArrayGetValueAtIndex(loadList, i);
if (!aKext) {
printf("%u: NULL kext\n", i);
continue;
}
success = __OSKextExtractDebugSymbols(aKext, symbols);
if (!success) {
goto finish;
}
}
*symbolsOut = CFRetain(symbols);
}
if (isARM64) {
result = CFRetain(plkInfo.kernelCacheImage);
}
else {
result = CFRetain(prelinkImage);
}
finish:
if (swapped) __OSKextUnswapHeaders(kernelImage);
SAFE_RELEASE(loadList);
SAFE_RELEASE(prelinkInfoData);
if (isARM64) {
SAFE_RELEASE(plkInfo.kernelCacheImage);
}
else {
SAFE_RELEASE(prelinkImage);
}
SAFE_RELEASE(kernelUUID);
SAFE_RELEASE(symbols);
if (kxldContext) kxld_destroy_context(kxldContext);
return result;
}
static boolean_t __OSKextWantKextsFirst(CFDataRef kernelImage)
{
boolean_t result = false;
const UInt8 * kernelImagePtr = CFDataGetBytePtr(kernelImage);
uint64_t i;
struct mach_header_64 * kernel_header;
struct segment_command_64 * seg_cmd = NULL;
if (ISMACHO64(MAGIC32(kernelImagePtr))) {
kernel_header = (struct mach_header_64 *)kernelImagePtr;
seg_cmd = (struct segment_command_64 *) ((uintptr_t)kernel_header + sizeof(*kernel_header));
for (i = 0; i < kernel_header->ncmds; i++){
if (seg_cmd->cmd == LC_SEGMENT_64 &&
strncmp(seg_cmd->segname, kPrelinkDataSegment, sizeof(seg_cmd->segname)) == 0) {
result = true;
break;
}
seg_cmd = (struct segment_command_64 *) ((uintptr_t)seg_cmd + seg_cmd->cmdsize);
}
}
return result;
}
Boolean __OSKextCheckForPrelinkedKernel(
OSKextRef aKext,
Boolean needAllFlag,
Boolean skipAuthenticationFlag,
Boolean printDiagnosticsFlag)
{
char kextPath[PATH_MAX];
const NXArchInfo * arch = NULL;
OSKextLogSpec logLevel = needAllFlag ? kOSKextLogErrorLevel :
kOSKextLogWarningLevel;
__OSKextGetFileSystemPath(aKext, NULL,
TRUE, kextPath);
if (!__OSKextIsValid(aKext)) {
OSKextLog(aKext,
logLevel | kOSKextLogArchiveFlag,
"%s is not valid; omitting from prelinked kernel.",
kextPath);
if (printDiagnosticsFlag) {
OSKextLogDiagnostics(aKext, kOSKextDiagnosticsFlagAll);
}
return false;
}
arch = OSKextGetArchitecture();
if (!OSKextSupportsArchitecture(aKext, arch)) {
OSKextLog(aKext,
logLevel | kOSKextLogArchiveFlag,
"%s doesn't support architecture %s; "
"omitting from prelinked kernel.",
kextPath, arch->name);
return false;
}
if (!skipAuthenticationFlag && !OSKextIsAuthentic(aKext)) {
OSKextLog(aKext,
logLevel | kOSKextLogArchiveFlag,
"%s is not authentic; omitting from prelinked kernel.",
kextPath);
if (printDiagnosticsFlag) {
OSKextLogDiagnostics(aKext, kOSKextDiagnosticsFlagAll);
}
return false;
}
return true;
}
#endif
#pragma mark Misc
CFComparisonResult __OSKextCompareIdentifiers(
const void * val1,
const void * val2,
void * context __unused)
{
CFStringRef identifier1 = OSKextGetIdentifier((OSKextRef)val1);
CFStringRef identifier2 = OSKextGetIdentifier((OSKextRef)val2);
return CFStringCompare(identifier1, identifier2,
kCFCompareCaseInsensitive|kCFCompareForcedOrdering);
}
char * __absPathOnVolume(
const char * path,
const char * volumePath)
{
char * result = (char *)path;
if (volumePath && volumePath[0]) {
size_t volumePathLength = strlen(volumePath);
if (volumePath[volumePathLength - 1] == '/') {
volumePathLength--;
}
if (volumePathLength &&
!strncmp(result, volumePath, volumePathLength))
{
result += volumePathLength;
}
}
return result;
}
CFStringRef __OSKextCopyExecutableRelativePath(OSKextRef aKext)
{
CFStringRef result = NULL;
CFURLRef kextAbsURL = NULL; CFStringRef kextAbsPath = NULL; CFURLRef executableURL = NULL; CFURLRef executableAbsURL = NULL; CFStringRef executableAbsPath = NULL; CFStringRef executableRelPath = NULL;
kextAbsURL = CFURLCopyAbsoluteURL(aKext->bundleURL);
if (!kextAbsURL) {
goto finish;
}
kextAbsPath = CFURLCopyFileSystemPath(kextAbsURL, kCFURLPOSIXPathStyle);
if (!kextAbsPath) {
goto finish;
}
executableURL = OSKextGetExecutableURL(aKext);
if (!executableURL) {
goto finish;
}
executableAbsURL = CFURLCopyAbsoluteURL(executableURL);
if (!executableAbsURL) {
goto finish;
}
executableAbsPath = CFURLCopyFileSystemPath(executableAbsURL, kCFURLPOSIXPathStyle);
if (!executableAbsPath) {
goto finish;
}
CFRange subRange;
subRange.location = CFStringGetLength(kextAbsPath) + 1;
subRange.length = CFStringGetLength(executableAbsPath) - subRange.location;
result = CFStringCreateWithSubstring(kCFAllocatorDefault,
executableAbsPath, subRange);
finish:
SAFE_RELEASE(kextAbsURL);
SAFE_RELEASE(kextAbsPath);
SAFE_RELEASE(executableAbsURL);
SAFE_RELEASE(executableAbsPath);
SAFE_RELEASE(executableRelPath);
return result;
}
CFStringRef OSKextCopyExecutableName(OSKextRef aKext)
{
CFStringRef result = NULL;
CFURLRef executableURL = NULL;
executableURL = OSKextGetExecutableURL(aKext);
if (!executableURL) {
goto finish;
}
result = CFURLCopyLastPathComponent(executableURL);
finish:
return result;
}
#if PRAGMA_MARK
#pragma mark URL Utilities
#endif
CFStringRef _CFURLCopyAbsolutePath(CFURLRef anURL)
{
CFStringRef result = NULL;
CFURLRef absURL = NULL;
absURL = CFURLCopyAbsoluteURL(anURL);
if (!absURL) {
goto finish;
}
result = CFURLCopyFileSystemPath(absURL, kCFURLPOSIXPathStyle);
finish:
SAFE_RELEASE(absURL);
return result;
}
#if PRAGMA_MARK
#endif
static inline bool logSpecMatch(
OSKextLogSpec msgLogSpec,
OSKextLogSpec logFilter) __attribute__((always_inline));
static inline bool logSpecMatch(
OSKextLogSpec msgLogSpec,
OSKextLogSpec logFilter)
{
OSKextLogSpec filterKextGlobal = logFilter & kOSKextLogKextOrGlobalMask;
OSKextLogSpec filterLevel = logFilter & kOSKextLogLevelMask;
OSKextLogSpec filterFlags = logFilter & kOSKextLogFlagsMask;
OSKextLogSpec msgKextGlobal = msgLogSpec & kOSKextLogKextOrGlobalMask;
OSKextLogSpec msgLevel = msgLogSpec & kOSKextLogLevelMask;
OSKextLogSpec msgFlags = msgLogSpec & kOSKextLogFlagsMask;
if (msgLevel == kOSKextLogExplicitLevel) {
return true;
}
if (msgLevel <= kOSKextLogBasicLevel && (msgLevel <= filterLevel)) {
return true;
}
if (!msgKextGlobal && !filterKextGlobal) {
return false;
}
if ((msgFlags & filterFlags) && (msgLevel <= filterLevel)) {
return true;
}
return false;
}
static bool __OSKextShouldLog(
OSKextRef aKext,
OSKextLogSpec msgLogSpec)
{
if (!aKext || aKext->flags.loggingEnabled) {
msgLogSpec = msgLogSpec | kOSKextLogKextOrGlobalMask;
}
return logSpecMatch(msgLogSpec, __sUserLogFilter);
}
void OSKextLog(
OSKextRef aKext,
OSKextLogSpec msgLogSpec,
const char * format, ...)
{
va_list argList;
va_start(argList, format);
OSKextVLog(aKext, msgLogSpec, format, argList);
va_end(argList);
}
void OSKextVLog(
OSKextRef aKext,
OSKextLogSpec msgLogSpec,
const char * format,
va_list srcArgList)
{
va_list argList;
char * outputCString = NULL;
if (!__sOSKextLogOutputFunction) {
goto finish;
}
if (!__OSKextShouldLog(aKext, msgLogSpec)) {
goto finish;
}
va_copy(argList, srcArgList);
vasprintf(&outputCString, format, argList);
va_end(argList);
if (outputCString) {
__sOSKextLogOutputFunction(aKext, msgLogSpec, "%s", outputCString);
}
finish:
SAFE_FREE(outputCString);
return;
}
void OSKextLogCFString(
OSKextRef aKext,
OSKextLogSpec msgLogSpec,
CFStringRef format, ...)
{
va_list argList;
va_start(argList, format);
OSKextVLogCFString(aKext, msgLogSpec, format, argList);
va_end(argList);
}
void OSKextVLogCFString(
OSKextRef aKext,
OSKextLogSpec msgLogSpec,
CFStringRef format,
va_list srcArgList)
{
va_list argList;
CFStringRef outputString = NULL; size_t cStringLength;
char * outputCString = NULL;
if (!__sOSKextLogOutputFunction) {
goto finish;
}
if (!__OSKextShouldLog(aKext, msgLogSpec)) {
goto finish;
}
va_copy(argList, srcArgList);
outputString = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault,
NULL, format, srcArgList);
va_end(argList);
if (!outputString) {
goto finish;
}
cStringLength = CFStringGetMaximumSizeForEncoding(
CFStringGetLength(outputString), kCFStringEncodingUTF8);
outputCString = (char *)malloc(cStringLength);
if (!outputCString) {
goto finish;
}
if (!CFStringGetCString(outputString, outputCString, cStringLength,
kCFStringEncodingUTF8)) {
goto finish;
}
if (outputCString) {
__sOSKextLogOutputFunction(aKext, msgLogSpec, "%s", outputCString);
}
finish:
SAFE_RELEASE(outputString);
SAFE_FREE(outputCString);
return;
}
void __sOSKextDefaultLogFunction(
OSKextRef aKext __unused,
OSKextLogSpec msgLogSpec __unused,
const char * format, ...)
{
va_list argList;
FILE * stream = stderr;
va_start(argList, format);
vfprintf(stream, format, argList);
va_end(argList);
fprintf(stream, "\n");
return;
}
void __OSKextLogKernelMessages(
OSKextRef aKext,
CFTypeRef kernelMessages)
{
CFArrayRef logInfoArray = (CFArrayRef)kernelMessages;
CFArrayRef flagsArray = NULL; CFArrayRef messagesArray = NULL; CFIndex count, i;
if (!__sOSKextLogOutputFunction) {
goto finish;
}
if (CFGetTypeID(logInfoArray) != CFArrayGetTypeID()) {
goto finish;
}
if (CFArrayGetCount(logInfoArray) != 2) {
goto finish;
}
flagsArray = (CFArrayRef)CFArrayGetValueAtIndex(logInfoArray, 0);
messagesArray = (CFArrayRef)CFArrayGetValueAtIndex(logInfoArray, 1);
if ((CFGetTypeID(flagsArray) != CFArrayGetTypeID()) ||
(CFGetTypeID(messagesArray) != CFArrayGetTypeID()) ||
(CFArrayGetCount(flagsArray) != CFArrayGetCount(messagesArray))) {
goto finish;
}
count = CFArrayGetCount(messagesArray);
for (i = 0; i < count; i++) {
CFNumberRef flagsNum = (CFNumberRef)CFArrayGetValueAtIndex(
flagsArray, i);
OSKextLogSpec msgLogSpec;
CFStringRef string = (CFStringRef)CFArrayGetValueAtIndex(
messagesArray, i);
if (CFNumberGetValue(flagsNum, kCFNumberSInt32Type, &msgLogSpec)) {
char * cString = createUTF8CStringForCFString(string);
if (cString) {
__sOSKextLogOutputFunction(aKext, msgLogSpec, "(kernel) %s", cString);
SAFE_FREE_NULL(cString);
}
} else {
}
}
finish:
return;
}
static const char * safe_mach_error_string(mach_error_t error_code)
{
const char * result = mach_error_string(error_code);
if (!result) {
result = "(unknown)";
}
return result;
}
#if PRAGMA_MARK
#pragma mark Dictionary Utilities
#endif
Boolean _isDictionary(CFTypeRef cf)
{
if (cf) {
return CFGetTypeID(cf) == CFDictionaryGetTypeID();
}
return false;
}
Boolean _isArray(CFTypeRef cf)
{
if (cf) {
return CFGetTypeID(cf) == CFArrayGetTypeID();
}
return false;
}
Boolean _isString(CFTypeRef cf)
{
if (cf) {
return CFGetTypeID(cf) == CFStringGetTypeID();
}
return false;
}
__unused static void __OSKextShowPLKInfo(plkInfo *info)
{
OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"\n plkInfo @%p: <%s %d>", info,
__func__, __LINE__);
if (info->kaslrOffsets) {
OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"kaslrOffsets count %ld",
CFSetGetCount(info->kaslrOffsets));
}
OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"kernelImage %p (%ld) kernelCacheImage %p (%ld)",
CFDataGetBytePtr(info->kernelImage),
CFDataGetLength(info->kernelImage),
CFDataGetBytePtr(info->kernelCacheImage),
CFDataGetLength(info->kernelCacheImage));
OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"kernel_TEXT: vmaddr %p vmsize %llu fileoff %llu filesize %llu)",
(void *)info->kernel_TEXT.vmaddr,
info->kernel_TEXT.vmsize,
info->kernel_TEXT.fileoff,
info->kernel_TEXT.vmsize);
for (enum enumSegIdx segIdx = SEG_IDX_TEXT; segIdx < SEG_IDX_COUNT; segIdx++) {
uint64_t vmbase = getKCPlkSegVMAddr(info, segIdx);
uint64_t filebase = getKCPlkSegFileOff(info, segIdx);
uint64_t vmoff = getKCPlkSegNextVMAddr(info, segIdx) - vmbase;
uint64_t fileoff = filebase + vmoff;
uint64_t vmsize = getKCPlkSegVMSize(info, segIdx);
OSKextLog(NULL, "plk_%s: vmaddr %p vmsize %llu fileoff %llu next_fileoff %llu filesize %llu",
segIdxToName(segIdx), vmbase, vmsize, filebase, fileoff, vmsize );
}
OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"plk_INFO: vmaddr %p vmsize %llu fileoff %llu filesize %llu)",
(void *)info->plk_INFO.vmaddr,
info->plk_INFO.vmsize,
info->plk_INFO.fileoff,
info->plk_INFO.vmsize);
}
#if SPLIT_KEXTS_DEBUG
#if 0
static void __OSKextShowMachoHeaderCFData(CFDataRef kextImage)
{
const UInt8 * imagePtr = CFDataGetBytePtr(kextImage);
CFIndex imageSize = CFDataGetLength(kextImage);
__OSKextShowMachoHeader(imagePtr, imageSize);
}
#endif
static void __OSKextShowMachoHeader(const UInt8 * imagePtr, CFIndex imageSize)
{
uint64_t i;
if (imagePtr == NULL || imageSize < 1) return;
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"\n macho header at %p size %ld ",
(void *) imagePtr, imageSize);
if (imageSize > 8) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"0x%02X %02X %02X %02X %02X %02X %02X %02X ",
*(imagePtr +0),
*(imagePtr +1),
*(imagePtr +2),
*(imagePtr +3),
*(imagePtr +4),
*(imagePtr +5),
*(imagePtr +6),
*(imagePtr +7) );
}
if (ISMACHO64(MAGIC32(imagePtr))) {
struct mach_header_64 * kext_header;
struct segment_command_64 * seg_cmd;
uint64_t le_fileoff = 0;
uint64_t le_filesize = 0;
kext_header = (struct mach_header_64 *)imagePtr;
seg_cmd = NULL;
seg_cmd = (struct segment_command_64 *) ((uintptr_t)kext_header + sizeof(*kext_header));
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"NCMDS=%d",
kext_header->ncmds);
for (i = 0; i < kext_header->ncmds; i++) {
if (seg_cmd->cmd == LC_SEGMENT_64) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"cmd 0x%02X '%s' segment '%s' vmaddr %p vmsize %llu fileoff %llu filesize %llu nsects %u seg data at %p ",
seg_cmd->cmd,
getSegmentCommandName(seg_cmd->cmd),
seg_cmd->segname[0] ? seg_cmd->segname : "none",
(void *)seg_cmd->vmaddr,
seg_cmd->vmsize,
seg_cmd->fileoff,
seg_cmd->filesize,
seg_cmd->nsects,
(void *) (imagePtr + seg_cmd->fileoff) );
if (seg_cmd->fileoff + seg_cmd->filesize > (uint64_t)imageSize) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"\n\n BAD MACHO! - segment fileoff %llu + filesize %llu > image size %lu \n\n",
seg_cmd->fileoff,
seg_cmd->filesize,
imageSize);
}
if (!strncmp(seg_cmd->segname, SEG_LINKEDIT, sizeof(seg_cmd->segname))) {
le_fileoff = seg_cmd->fileoff;;
le_filesize = seg_cmd->filesize;;
}
struct section_64 *sect = NULL;
u_int j = 0;
sect = (struct section_64 *) (&seg_cmd[1]);
for (j = 0; j < seg_cmd->nsects; ++j, ++sect) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"sectname '%s' addr %p size %llu offset %u reloff %u nreloc %u ",
sect->sectname[0] ? sect->sectname : "none",
(void *)sect->addr,
sect->size,
sect->offset,
sect->reloff,
sect->nreloc);
if (sect->offset + sect->size > (uint64_t)imageSize) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"\n\n BAD MACHO! - section offset %u + size %llu > image size %lu \n\n",
sect->offset,
sect->size,
imageSize);
}
}
}
else if (seg_cmd->cmd == LC_SYMTAB) {
struct symtab_command * symtab_cmd = (struct symtab_command *) seg_cmd;
uint32_t syms_end;
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"cmd 0x%02X '%s' cmdsize %u symoff %u nsyms %u stroff %u strsize %u ",
symtab_cmd->cmd,
getSegmentCommandName(symtab_cmd->cmd),
symtab_cmd->cmdsize,
symtab_cmd->symoff,
symtab_cmd->nsyms,
symtab_cmd->stroff,
symtab_cmd->strsize);
syms_end = symtab_cmd->symoff + (symtab_cmd->nsyms * sizeof(struct nlist_64));
if (symtab_cmd->symoff < le_fileoff || syms_end > le_fileoff + le_filesize) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"\n\n BAD MACHO! - LC_SYMTAB symoff %u to %u not in __LINKEDIT segment bounds %llu to %llu \n\n",
symtab_cmd->symoff,
syms_end,
le_fileoff,
le_fileoff + le_filesize);
}
syms_end = symtab_cmd->stroff + symtab_cmd->strsize;
if (symtab_cmd->stroff < le_fileoff ||
(syms_end > le_fileoff + le_filesize)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"\n\n BAD MACHO! - LC_SYMTAB stroff %u to %u not in __LINKEDIT segment bounds %llu to %llu \n\n",
symtab_cmd->stroff,
syms_end,
le_fileoff,
le_fileoff + le_filesize);
}
}
else if (seg_cmd->cmd == LC_DYSYMTAB) {
struct dysymtab_command * dsymtab_cmd = (struct dysymtab_command *) seg_cmd;
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"cmd 0x%02X '%s' cmdsize %u ilocalsym %u nlocalsym %u iextdefsym %u nextdefsym %u ",
dsymtab_cmd->cmd,
getSegmentCommandName(dsymtab_cmd->cmd),
dsymtab_cmd->cmdsize,
dsymtab_cmd->ilocalsym,
dsymtab_cmd->nlocalsym,
dsymtab_cmd->iextdefsym,
dsymtab_cmd->nextdefsym);
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"cmd 0x%02X '%s' iundefsym %u nundefsym %u tocoff %u ntoc %u modtaboff %u nmodtab %u ",
dsymtab_cmd->cmd,
getSegmentCommandName(dsymtab_cmd->cmd),
dsymtab_cmd->iundefsym,
dsymtab_cmd->nundefsym,
dsymtab_cmd->tocoff,
dsymtab_cmd->ntoc,
dsymtab_cmd->modtaboff,
dsymtab_cmd->nmodtab);
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"cmd 0x%02X '%s' extrefsymoff %u nextrefsyms %u indirectsymoff %u nindirectsyms %u ",
dsymtab_cmd->cmd,
getSegmentCommandName(dsymtab_cmd->cmd),
dsymtab_cmd->extrefsymoff,
dsymtab_cmd->nextrefsyms,
dsymtab_cmd->indirectsymoff,
dsymtab_cmd->nindirectsyms);
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"cmd 0x%02X '%s' extreloff %u nextrel %u locreloff %u nlocrel %u ",
dsymtab_cmd->cmd,
getSegmentCommandName(dsymtab_cmd->cmd),
dsymtab_cmd->extreloff,
dsymtab_cmd->nextrel,
dsymtab_cmd->locreloff,
dsymtab_cmd->nlocrel);
if (dsymtab_cmd->locreloff < le_fileoff || dsymtab_cmd->locreloff > le_fileoff + le_filesize) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"\n\n BAD MACHO! - LC_DYSYMTAB locreloff %u not in __LINKEDIT segment bounds %llu to %llu \n\n",
dsymtab_cmd->locreloff,
le_fileoff,
le_fileoff + le_filesize);
}
}
else if (seg_cmd->cmd == LC_SEGMENT_SPLIT_INFO) {
struct linkedit_data_command * lc = (struct linkedit_data_command *) seg_cmd;
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"cmd 0x%02X '%s' cmdsize %u dataoff %u datasize %u split seg info data (within LINKEDIT) at %p",
seg_cmd->cmd,
getSegmentCommandName(seg_cmd->cmd),
lc->cmdsize,
lc->dataoff,
lc->datasize,
(void *) (imagePtr + lc->dataoff) );
if (lc->dataoff < le_fileoff || lc->dataoff + lc->datasize > le_fileoff + le_filesize) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"\n\n BAD MACHO! - LC_SEGMENT_SPLIT_INFO dataoff %u to %u not in __LINKEDIT segment bounds %llu to %llu \n\n",
lc->dataoff,
lc->dataoff + lc->datasize,
le_fileoff,
le_fileoff + le_filesize);
}
}
else {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"cmd 0x%02X '%s' ",
seg_cmd->cmd,
getSegmentCommandName(seg_cmd->cmd));
}
seg_cmd = (struct segment_command_64 *) ((uintptr_t)seg_cmd + seg_cmd->cmdsize);
} } else {
struct mach_header * kext_header;
struct segment_command * seg_cmd;
kext_header = (struct mach_header *)imagePtr;
seg_cmd = NULL;
seg_cmd = (struct segment_command *) ((uintptr_t)kext_header + sizeof(*kext_header));
for (i = 0; i < kext_header->ncmds; i++){
if (seg_cmd->cmd == LC_SEGMENT) {
}
seg_cmd = (struct segment_command *) ((uintptr_t)seg_cmd + seg_cmd->cmdsize);
} }
return;
}
#if 0
static void __OSKextScanFor(const UInt8 *dataPtr, int count, const UInt8 theChar)
{
int i;
int hits = 0;
for (i = 0; i < count; i++) {
if (*(dataPtr + i) == theChar) {
OSKextLog(NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"matched at %d %p", i, (void *) (dataPtr + i));
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"LC_SEGMENT_SPLIT_INFO: 0x%02X %02X %02X %02X %02X %02X %02X %02X ",
*(dataPtr + i +0),
*(dataPtr + i +1),
*(dataPtr + i +2),
*(dataPtr + i +3),
*(dataPtr + i +4),
*(dataPtr + i +5),
*(dataPtr + i +6),
*(dataPtr + i +7) );
hits++;
}
}
OSKextLog(NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%d hits starting at %p", hits, (void *) dataPtr);
}
#endif
#endif
__unused static const char * getSegmentCommandName(uint32_t theSegCommand)
{
const char * theResult;
switch(theSegCommand) {
case LC_SYMTAB:
theResult = "LC_SYMTAB";
break;
case LC_DYSYMTAB:
theResult = "LC_DYSYMTAB";
break;
case LC_SEGMENT_64:
theResult = "LC_SEGMENT_64";
break;
case LC_UUID:
theResult = "LC_UUID";
break;
case LC_SOURCE_VERSION:
theResult = "LC_SOURCE_VERSION";
break;
case LC_SEGMENT_SPLIT_INFO:
theResult = "LC_SEGMENT_SPLIT_INFO";
break;
default:
theResult = "Unknown Load Command";
break;
}
return(theResult);
}