]> git.saurik.com Git - apple/dyld.git/commitdiff
dyld-640.2.tar.gz macos-10142 v640.2
authorApple <opensource@apple.com>
Tue, 26 Mar 2019 22:07:43 +0000 (22:07 +0000)
committerApple <opensource@apple.com>
Tue, 26 Mar 2019 22:07:43 +0000 (22:07 +0000)
dyld3/ClosureBuilder.cpp
dyld3/ClosureFileSystemPhysical.h
dyld3/ClosurePrinter.cpp
dyld3/MachOAnalyzer.cpp
dyld3/MachOFile.cpp
dyld3/shared-cache/update_dyld_shared_cache.cpp
testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/main.c
testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/reexported-myzlib.c [new file with mode: 0644]
testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/reexporter.c [new file with mode: 0644]

index e16760e7082b3b049a1e8be5603b79fbc33abed7..d9dd60c2084bc443763ddc513231024cf68239ff 100644 (file)
@@ -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 {
index ecb0fbfff0741bd5e8280217aeffd2c6e957ec9a..48d8942900eae42506f094cc47ea3736a58c0cfa 100644 (file)
@@ -26,6 +26,8 @@
 
 #include "ClosureFileSystem.h"
 
+#include <sys/stat.h>
+
 namespace dyld3 {
 namespace closure {
 
index 98b27dfe51348e1edfe926132f60f8f6485e996e..a00fd7e5e86622db0eaa470f72622f05452be862 100644 (file)
@@ -321,6 +321,14 @@ static Node buildClosureNode(const LaunchClosure* closure, const Array<const Ima
     __block Node root;
     root.map["images"] = buildImageArrayNode(closure->images(), 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);
index 8b3b72b5306689f33efc20da5f1c9bad82969b33..d38ce4e42f35ec966c047557e0dd339b0306b051 100644 (file)
@@ -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;
     }
 
+    // <rdar://problem/45525884> 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;
 
     // <rdar://problem/13622786> 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
index bf8724b77308878f3a827533ed992b0dcab64e8e..9598d6e7cad5d82d3033f7b2ed123f31b1f0c873 100644 (file)
@@ -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);
index cf9e21a1b2af20a958d9b8c48e7bc2c7f312845f..6a0113e979f68932c3827e9d7d9e1d7c24ac8cb5 100644 (file)
@@ -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<MappedMachOsByCategory>& files)
+static bool addIfMachO(const dyld3::closure::FileSystem& fileSystem, const std::string& runtimePath, const struct stat& statBuf, bool requireSIP,
+                       dev_t rootFS, std::vector<MappedMachOsByCategory>& 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<std::string>& pathPrefixes, bool requireSIP, std::vector<MappedMachOsByCategory>& files)
+static void findAllFiles(const std::vector<std::string>& pathPrefixes, bool requireSIP, dev_t rootFS, std::vector<MappedMachOsByCategory>& files)
 {
     std::unordered_set<std::string> skipDirs;
     for (const char* s : sDontUsePrefixes)
@@ -228,7 +236,7 @@ static void findAllFiles(const std::vector<std::string>& 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<std::string>& pathPrefixes, bool requ
 }
 
 
-static void findOSFilesViaBOMS(const std::vector<std::string>& pathPrefixes, bool requireSIP, std::vector<MappedMachOsByCategory>& files)
+static void findOSFilesViaBOMS(const std::vector<std::string>& pathPrefixes, bool requireSIP, dev_t rootFS, std::vector<MappedMachOsByCategory>& files)
 {
     __block std::unordered_set<std::string> runtimePathsFound;
     for (const std::string& prefix : pathPrefixes) {
@@ -291,9 +299,10 @@ static void findOSFilesViaBOMS(const std::vector<std::string>& 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<std::string> 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<std::string> 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<MappedMachOsByCategory> 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());
index 37136fc1dc9728cd97250ed5eaff028e132d7b43..05eb478adb8881ea73cbe286bf4349bbad6350fe 100644 (file)
@@ -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 <stdio.h>
 #include <stdlib.h>
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 (file)
index 0000000..5bd0c77
--- /dev/null
@@ -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 (file)
index 0000000..f5f664f
--- /dev/null
@@ -0,0 +1,4 @@
+
+int foo() {
+    return 0;
+}