Loading...
dyld3/shared-cache/update_dyld_shared_cache.cpp dyld-625.13 dyld-655.1.1
--- dyld/dyld-625.13/dyld3/shared-cache/update_dyld_shared_cache.cpp
+++ dyld/dyld-655.1.1/dyld3/shared-cache/update_dyld_shared_cache.cpp
@@ -146,13 +146,21 @@
 
 
 
-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") )
         return false;
     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 )
@@ -201,7 +209,7 @@
     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 @@
                     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 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 @@
                                     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;
+                                        }
                                     }
                                 }
                             }
@@ -350,7 +359,10 @@
 
     const char* installName = aFile.mh->installName();
     if ( (pathsWithDuplicateInstallName.count(aFile.runtimePath) != 0) && (aFile.runtimePath != installName) ) {
-        if (warn) fprintf(stderr, "update_dyld_shared_cache: warning: %s skipping because of duplicate install name %s\n", archName.c_str(), aFile.runtimePath.c_str());
+        // <rdar://problem/46431467> if a dylib moves and a symlink is installed into its place, bom iterator will see both and issue a warning
+        struct stat statBuf;
+        bool isSymLink = ( (lstat(aFile.runtimePath.c_str(), &statBuf) == 0) && S_ISLNK(statBuf.st_mode) );
+        if (!isSymLink && warn) fprintf(stderr, "update_dyld_shared_cache: warning: %s skipping because of duplicate install name %s\n", archName.c_str(), aFile.runtimePath.c_str());
         return true;
     }
 
@@ -402,8 +414,11 @@
     }
 
     for (DyldSharedCache::MappedMachO& aFile : fileSet.dylibsForCache) {
-        if ( dontCache(volumePrefix, fileSet.archName, pathsWithDuplicateInstallName, aFile, true, skipDylibs) )
-            fileSet.otherDylibsAndBundles.push_back(aFile);
+        if ( dontCache(volumePrefix, fileSet.archName, pathsWithDuplicateInstallName, aFile, true, skipDylibs) ) {
+            // <rdar://problem/46423929> don't build dlopen closures for symlinks to something in the dyld cache
+            if ( pathsWithDuplicateInstallName.count(aFile.runtimePath) == 0 )
+                fileSet.otherDylibsAndBundles.push_back(aFile);
+        }
     }
     fileSet.dylibsForCache.erase(std::remove_if(fileSet.dylibsForCache.begin(), fileSet.dylibsForCache.end(),
         [&](const DyldSharedCache::MappedMachO& aFile) { return dontCache(volumePrefix, fileSet.archName, pathsWithDuplicateInstallName, aFile, false, skipDylibs); }),
@@ -632,6 +647,15 @@
         }
     }
 
+    // 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,10 +663,20 @@
     //   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
@@ -692,10 +726,10 @@
     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 +793,7 @@
 
                     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());