From 9d871a63e06bad796231b04bbb597a7e6847f827 Mon Sep 17 00:00:00 2001 From: Apple Date: Tue, 26 Mar 2019 22:07:43 +0000 Subject: [PATCH] dyld-640.2.tar.gz --- dyld3/ClosureBuilder.cpp | 16 +++++-- dyld3/ClosureFileSystemPhysical.h | 2 + dyld3/ClosurePrinter.cpp | 8 ++++ dyld3/MachOAnalyzer.cpp | 27 +++++++++-- dyld3/MachOFile.cpp | 7 +-- dyld3/shared-cache/update_dyld_shared_cache.cpp | 54 ++++++++++++++++------ .../env-DYLD_LIBRARY_PATH-cache.dtest/main.c | 4 ++ .../reexported-myzlib.c | 4 ++ .../env-DYLD_LIBRARY_PATH-cache.dtest/reexporter.c | 4 ++ 9 files changed, 101 insertions(+), 25 deletions(-) create mode 100644 testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/reexported-myzlib.c create mode 100644 testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/reexporter.c diff --git a/dyld3/ClosureBuilder.cpp b/dyld3/ClosureBuilder.cpp index e16760e..d9dd60c 100644 --- a/dyld3/ClosureBuilder.cpp +++ b/dyld3/ClosureBuilder.cpp @@ -1747,6 +1747,9 @@ const LaunchClosure* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo& fil if ( li.overrideImageNum != 0 ) { const Image* cacheImage = _dyldImageArray->imageForNum(li.overrideImageNum); STACK_ALLOC_ARRAY(Closure::PatchEntry, patches, cacheImage->patchableExportCount()); + MachOLoaded::DependentToMachOLoaded reexportFinder = ^(const MachOLoaded* mh, uint32_t depIndex) { + return (const MachOLoaded*)findDependent(mh, depIndex); + }; //fprintf(stderr, "'%s' overrides '%s'\n", li.loadedFileInfo.path, cacheImage->path()); cacheImage->forEachPatchableExport(^(uint32_t cacheOffsetOfImpl, const char* symbolName) { dyld3::MachOAnalyzer::FoundSymbol foundInfo; @@ -1754,9 +1757,10 @@ const LaunchClosure* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo& fil Closure::PatchEntry patch; patch.overriddenDylibInCache = li.overrideImageNum; patch.exportCacheOffset = cacheOffsetOfImpl; - if ( li.loadAddress()->findExportedSymbol(patchDiag, symbolName, foundInfo, nullptr) ) { + if ( li.loadAddress()->findExportedSymbol(patchDiag, symbolName, foundInfo, reexportFinder) ) { + const MachOAnalyzer* impDylib = (const MachOAnalyzer*)foundInfo.foundInDylib; patch.replacement.image.kind = Image::ResolvedSymbolTarget::kindImage; - patch.replacement.image.imageNum = li.imageNum; + patch.replacement.image.imageNum = findLoadedImage(impDylib).imageNum; patch.replacement.image.offset = foundInfo.value; } else { @@ -1998,6 +2002,9 @@ const DlopenClosure* ClosureBuilder::makeDlopenClosure(const char* path, const L if ( (li.overrideImageNum != 0) && (li.imageNum >= _startImageNum) ) { const Image* cacheImage = _dyldImageArray->imageForNum(li.overrideImageNum); STACK_ALLOC_ARRAY(Closure::PatchEntry, patches, cacheImage->patchableExportCount()); + MachOLoaded::DependentToMachOLoaded reexportFinder = ^(const MachOLoaded* mh, uint32_t depIndex) { + return (const MachOLoaded*)findDependent(mh, depIndex); + }; //fprintf(stderr, "'%s' overrides '%s'\n", li.loadedFileInfo.path, cacheImage->path()); cacheImage->forEachPatchableExport(^(uint32_t cacheOffsetOfImpl, const char* symbolName) { dyld3::MachOAnalyzer::FoundSymbol foundInfo; @@ -2005,9 +2012,10 @@ const DlopenClosure* ClosureBuilder::makeDlopenClosure(const char* path, const L Closure::PatchEntry patch; patch.overriddenDylibInCache = li.overrideImageNum; patch.exportCacheOffset = cacheOffsetOfImpl; - if ( li.loadAddress()->findExportedSymbol(patchDiag, symbolName, foundInfo, nullptr) ) { + if ( li.loadAddress()->findExportedSymbol(patchDiag, symbolName, foundInfo, reexportFinder) ) { + const MachOAnalyzer* impDylib = (const MachOAnalyzer*)foundInfo.foundInDylib; patch.replacement.image.kind = Image::ResolvedSymbolTarget::kindImage; - patch.replacement.image.imageNum = li.imageNum; + patch.replacement.image.imageNum = findLoadedImage(impDylib).imageNum; patch.replacement.image.offset = foundInfo.value; } else { diff --git a/dyld3/ClosureFileSystemPhysical.h b/dyld3/ClosureFileSystemPhysical.h index ecb0fbf..48d8942 100644 --- a/dyld3/ClosureFileSystemPhysical.h +++ b/dyld3/ClosureFileSystemPhysical.h @@ -26,6 +26,8 @@ #include "ClosureFileSystem.h" +#include + namespace dyld3 { namespace closure { diff --git a/dyld3/ClosurePrinter.cpp b/dyld3/ClosurePrinter.cpp index 98b27df..a00fd7e 100644 --- a/dyld3/ClosurePrinter.cpp +++ b/dyld3/ClosurePrinter.cpp @@ -321,6 +321,14 @@ static Node buildClosureNode(const LaunchClosure* closure, const Arrayimages(), imagesArrays, printFixups, printDependentsDetails); + closure->forEachPatchEntry(^(const Closure::PatchEntry& patchEntry) { + Node patchNode; + patchNode.map["func-dyld-cache-offset"].value = hex8(patchEntry.exportCacheOffset); + patchNode.map["func-image-num"].value = hex8(patchEntry.overriddenDylibInCache); + patchNode.map["replacement"].value = printTarget(imagesArrays, patchEntry.replacement); + root.map["dyld-cache-fixups"].array.push_back(patchNode); + }); + Image::ResolvedSymbolTarget entry; if ( closure->mainEntry(entry) ) root.map["main"].value = printTarget(imagesArrays, entry); diff --git a/dyld3/MachOAnalyzer.cpp b/dyld3/MachOAnalyzer.cpp index 8b3b72b..d38ce4e 100644 --- a/dyld3/MachOAnalyzer.cpp +++ b/dyld3/MachOAnalyzer.cpp @@ -69,6 +69,11 @@ const MachOAnalyzer* MachOAnalyzer::validMainExecutable(Diagnostics& diag, const closure::LoadedFileInfo MachOAnalyzer::load(Diagnostics& diag, const closure::FileSystem& fileSystem, const char* path, const char* reqArchName, Platform reqPlatform) { + // FIXME: This should probably be an assert, but if we happen to have a diagnostic here then something is wrong + // above us and we should quickly return instead of doing unnecessary work. + if (diag.hasError()) + return closure::LoadedFileInfo(); + closure::LoadedFileInfo info; char realerPath[MAXPATHLEN]; if (!fileSystem.loadFile(path, info, realerPath, ^(const char *format, ...) { @@ -80,6 +85,11 @@ closure::LoadedFileInfo MachOAnalyzer::load(Diagnostics& diag, const closure::Fi return closure::LoadedFileInfo(); } + // If we now have an error, but succeeded, then we must have tried multiple paths, one of which errored, but + // then succeeded on a later path. So clear the error. + if (diag.hasError()) + diag.clearError(); + // if fat, remap just slice needed bool fatButMissingSlice; const FatFile* fh = (FatFile*)info.fileContent; @@ -169,7 +179,7 @@ bool MachOAnalyzer::validMachOForArchAndPlatform(Diagnostics& diag, size_t slice { // must start with mach-o magic value if ( (this->magic != MH_MAGIC) && (this->magic != MH_MAGIC_64) ) { - diag.error("could not use '%s' because it is not a mach-o file, 0x%08X", path, this->magic); + diag.error("could not use '%s' because it is not a mach-o file: 0x%08X 0x%08X", path, this->magic, this->cputype); return false; } @@ -198,7 +208,7 @@ bool MachOAnalyzer::validMachOForArchAndPlatform(Diagnostics& diag, size_t slice case MH_BUNDLE: break; default: - diag.error("could not use '%s' because it is not a dylib, bundle, or executable", path); + diag.error("could not use '%s' because it is not a dylib, bundle, or executable, filetype=0x%08X", path, this->filetype); return false; } @@ -233,6 +243,10 @@ bool MachOAnalyzer::validMachOForArchAndPlatform(Diagnostics& diag, size_t slice return false; } + // to avoid heap smasher, don't load this dylib + if ( strcmp(path, "/usr/lib/libnetsnmp.5.2.1.dylib") == 0 ) + return false; + // further validations done in validLinkedit() return true; @@ -2043,15 +2057,18 @@ bool MachOAnalyzer::hasCodeSignature(uint32_t& fileOffset, uint32_t& size) const return false; // ignore code signatures in macOS binaries built with pre-10.9 tools - __block bool goodSignature = true; if ( (this->cputype == CPU_TYPE_X86_64) || (this->cputype == CPU_TYPE_I386) ) { + __block bool foundPlatform = false; + __block bool badSignature = false; forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) { + foundPlatform = true; if ( (platform == Platform::macOS) && (sdk < 0x000A0900) ) - goodSignature = false; + badSignature = true; }); + return foundPlatform && !badSignature; } - return goodSignature; + return true; } bool MachOAnalyzer::hasInitializer(Diagnostics& diag, bool contentRebased, const void* dyldCache) const diff --git a/dyld3/MachOFile.cpp b/dyld3/MachOFile.cpp index bf8724b..9598d6e 100644 --- a/dyld3/MachOFile.cpp +++ b/dyld3/MachOFile.cpp @@ -481,7 +481,8 @@ void MachOFile::forEachLoadCommand(Diagnostics& diag, void (^callback)(const loa else if ( this->magic == MH_MAGIC ) startCmds = (load_command*)((char *)this + sizeof(mach_header)); else { - diag.error("file does not start with MH_MAGIC[_64]"); + const uint32_t* h = (uint32_t*)this; + diag.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h[0], h [1]); return; // not a mach-o file } const load_command* const cmdsEnd = (load_command*)((char*)startCmds + this->sizeofcmds); @@ -489,11 +490,11 @@ void MachOFile::forEachLoadCommand(Diagnostics& diag, void (^callback)(const loa for (uint32_t i = 0; i < this->ncmds; ++i) { const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize); if ( cmd->cmdsize < 8 ) { - diag.error("malformed load command #%d, size too small %d", i, cmd->cmdsize); + diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) too small", i, this->ncmds, cmd, this, cmd->cmdsize); return; } if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) { - diag.error("malformed load command #%d, size too large 0x%X", i, cmd->cmdsize); + diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) is too large, load commands end at %p", i, this->ncmds, cmd, this, cmd->cmdsize, cmdsEnd); return; } callback(cmd, stop); diff --git a/dyld3/shared-cache/update_dyld_shared_cache.cpp b/dyld3/shared-cache/update_dyld_shared_cache.cpp index cf9e21a..6a0113e 100644 --- a/dyld3/shared-cache/update_dyld_shared_cache.cpp +++ b/dyld3/shared-cache/update_dyld_shared_cache.cpp @@ -146,7 +146,8 @@ static bool verbose = false; -static bool addIfMachO(const dyld3::closure::FileSystem& fileSystem, const std::string& runtimePath, const struct stat& statBuf, bool requireSIP, std::vector& files) +static bool addIfMachO(const dyld3::closure::FileSystem& fileSystem, const std::string& runtimePath, const struct stat& statBuf, bool requireSIP, + dev_t rootFS, std::vector& files) { // don't precompute closure info for any debug or profile dylibs if ( endsWith(runtimePath, "_profile.dylib") || endsWith(runtimePath, "_debug.dylib") || endsWith(runtimePath, "_profile") || endsWith(runtimePath, "_debug") ) @@ -154,6 +155,13 @@ static bool addIfMachO(const dyld3::closure::FileSystem& fileSystem, const std:: if ( startsWith(runtimePath, "/usr/lib/system/introspection/") ) return false; + // Only use files on the same volume as the boot volume + if (statBuf.st_dev != rootFS) { + if ( verbose ) + fprintf(stderr, "update_dyld_shared_cache: warning: skipping overlay file '%s' which is not on the root volume\n", runtimePath.c_str()); + return false; + } + auto warningHandler = ^(const char* msg) { if ( verbose ) fprintf(stderr, "update_dyld_shared_cache: warning: cannot build dlopen closure for '%s' because %s\n", runtimePath.c_str(), msg); @@ -201,7 +209,7 @@ static bool addIfMachO(const dyld3::closure::FileSystem& fileSystem, const std:: return result; } -static void findAllFiles(const std::vector& pathPrefixes, bool requireSIP, std::vector& files) +static void findAllFiles(const std::vector& pathPrefixes, bool requireSIP, dev_t rootFS, std::vector& files) { std::unordered_set skipDirs; for (const char* s : sDontUsePrefixes) @@ -228,7 +236,7 @@ static void findAllFiles(const std::vector& pathPrefixes, bool requ return; // if the file is mach-o, add to list - if ( addIfMachO(fileSystem, path, statBuf, requireSIP, files) ) { + if ( addIfMachO(fileSystem, path, statBuf, requireSIP, rootFS, files) ) { if ( multiplePrefixes ) alreadyUsed.insert(path); } @@ -238,7 +246,7 @@ static void findAllFiles(const std::vector& pathPrefixes, bool requ } -static void findOSFilesViaBOMS(const std::vector& pathPrefixes, bool requireSIP, std::vector& files) +static void findOSFilesViaBOMS(const std::vector& pathPrefixes, bool requireSIP, dev_t rootFS, std::vector& files) { __block std::unordered_set runtimePathsFound; for (const std::string& prefix : pathPrefixes) { @@ -291,9 +299,10 @@ static void findOSFilesViaBOMS(const std::vector& pathPrefixes, boo std::string fullPath2 = prefix2 + runPath; if ( stat(fullPath2.c_str(), &statBuf2) == 0 ) { dyld3::closure::FileSystemPhysical fileSystem(prefix2.c_str()); - addIfMachO(fileSystem, runPath, statBuf2, requireSIP, files); - runtimePathsFound.insert(runPath); - break; + if ( addIfMachO(fileSystem, runPath, statBuf2, requireSIP, rootFS, files) ) { + runtimePathsFound.insert(runPath); + break; + } } } } @@ -632,6 +641,15 @@ int main(int argc, const char* argv[], const char* envp[]) } } + // Find the boot volume so that we can ensure all overlays are on the same volume + struct stat rootStatBuf; + if ( stat(rootPath == "" ? "/" : rootPath.c_str(), &rootStatBuf) != 0 ) { + fprintf(stderr, "update_dyld_shared_cache: error: could not stat root file system because '%s'\n", strerror(errno)); + return 1; + } + + dev_t rootFS = rootStatBuf.st_dev; + // // pathPrefixes for three modes: // 1) no options: { "" } // search only boot volume @@ -639,11 +657,21 @@ int main(int argc, const char* argv[], const char* envp[]) // 3) -root: { root } // search only -root volume // std::vector pathPrefixes; - if ( !overlayPath.empty() ) - pathPrefixes.push_back(overlayPath); + if ( !overlayPath.empty() ) { + // Only add the overlay path if it exists, and is the same volume as the root + struct stat overlayStatBuf; + if ( stat(overlayPath.c_str(), &overlayStatBuf) != 0 ) { + fprintf(stderr, "update_dyld_shared_cache: warning: ignoring overlay dir '%s' because '%s'\n", overlayPath.c_str(), strerror(errno)); + overlayPath.clear(); + } else if (overlayStatBuf.st_dev != rootFS) { + fprintf(stderr, "update_dyld_shared_cache: warning: ignoring overlay dir '%s' because it is not the boot volume\n", overlayPath.c_str()); + overlayPath.clear(); + } else { + pathPrefixes.push_back(overlayPath); + } + } pathPrefixes.push_back(rootPath); - if ( cacheDir.empty() ) { // if -cache_dir is not specified, then write() will eventually fail if we are not running as root if ( geteuid() != 0 ) { @@ -692,10 +720,10 @@ int main(int argc, const char* argv[], const char* envp[]) if ( archStrs.count("i386") ) allFileSets.push_back({"i386"}); if ( searchDisk ) - findAllFiles(pathPrefixes, requireDylibsBeRootlessProtected, allFileSets); + findAllFiles(pathPrefixes, requireDylibsBeRootlessProtected, rootFS, allFileSets); else { std::unordered_set runtimePathsFound; - findOSFilesViaBOMS(pathPrefixes, requireDylibsBeRootlessProtected, allFileSets); + findOSFilesViaBOMS(pathPrefixes, requireDylibsBeRootlessProtected, rootFS, allFileSets); } // nothing in OS uses i386 dylibs, so only dylibs used by third party apps need to be in cache @@ -759,7 +787,7 @@ int main(int argc, const char* argv[], const char* envp[]) std::vector mappedFiles; mappedFiles.push_back({fileSet.archName}); - if ( addIfMachO(fileSystem, runtimePath, statBuf, requireDylibsBeRootlessProtected, mappedFiles) ) { + if ( addIfMachO(fileSystem, runtimePath, statBuf, requireDylibsBeRootlessProtected, rootFS, mappedFiles) ) { if ( !mappedFiles.back().dylibsForCache.empty() ) { if ( verbose ) fprintf(stderr, "verifySelfContained, add %s\n", mappedFiles.back().dylibsForCache.back().runtimePath.c_str()); diff --git a/testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/main.c b/testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/main.c index 37136fc..05eb478 100644 --- a/testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/main.c +++ b/testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/main.c @@ -1,11 +1,15 @@ // BUILD: mkdir -p $BUILD_DIR/override +// BUILD: mkdir -p $BUILD_DIR/re-export-override // BUILD: $CC myzlib.c -dynamiclib -o $BUILD_DIR/override/libz.1.dylib -install_name /usr/lib/libz.1.dylib -compatibility_version 1.0 -framework CoreFoundation +// BUILD: $CC reexported-myzlib.c -dynamiclib -o $BUILD_DIR/re-export-override/reexported.dylib -compatibility_version 1.0 -framework CoreFoundation +// BUILD: $CC reexporter.c -dynamiclib -o $BUILD_DIR/re-export-override/libz.1.dylib -install_name /usr/lib/libz.1.dylib -compatibility_version 1.0 -Wl,-reexport_library,$BUILD_DIR/re-export-override/reexported.dylib // BUILD: $CC main.c -o $BUILD_DIR/main.exe -lz // BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main.exe // RUN: ./main.exe // RUN: DYLD_LIBRARY_PATH=$RUN_DIR/override/ ./main.exe +// RUN: DYLD_LIBRARY_PATH=$RUN_DIR/re-export-override/ ./main.exe #include #include diff --git a/testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/reexported-myzlib.c b/testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/reexported-myzlib.c new file mode 100644 index 0000000..5bd0c77 --- /dev/null +++ b/testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/reexported-myzlib.c @@ -0,0 +1,4 @@ +const char* zlibVersion() +{ + return "my"; +} diff --git a/testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/reexporter.c b/testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/reexporter.c new file mode 100644 index 0000000..f5f664f --- /dev/null +++ b/testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/reexporter.c @@ -0,0 +1,4 @@ + +int foo() { + return 0; +} -- 2.7.4