Avoid overwriting shared library file that is open

b/21949580

Writing different contents to an existing .so file, which is currently
open, may corrupt its globals and code. Choose a different file name
instead.

Change-Id: I3e649b09a55b43339283aa1c46f2844c66434c17
(cherry picked from commit 9844cffc8af28c4829012bc0c48f85d04b671f88)
diff --git a/cpu_ref/rsCpuExecutable.cpp b/cpu_ref/rsCpuExecutable.cpp
index e2c27b5..867a2cd 100644
--- a/cpu_ref/rsCpuExecutable.cpp
+++ b/cpu_ref/rsCpuExecutable.cpp
@@ -20,27 +20,6 @@
 
 namespace {
 
-// Create a len length string containing random characters from [A-Za-z0-9].
-static std::string getRandomString(size_t len) {
-    char buf[len + 1];
-    for (size_t i = 0; i < len; i++) {
-        uint32_t r = arc4random() & 0xffff;
-        r %= 62;
-        if (r < 26) {
-            // lowercase
-            buf[i] = 'a' + r;
-        } else if (r < 52) {
-            // uppercase
-            buf[i] = 'A' + (r - 26);
-        } else {
-            // Use a number
-            buf[i] = '0' + (r - 52);
-        }
-    }
-    buf[len] = '\0';
-    return std::string(buf);
-}
-
 // Check if a path exists and attempt to create it if it doesn't.
 static bool ensureCacheDirExists(const char *path) {
     if (access(path, R_OK | W_OK | X_OK) == 0) {
@@ -151,7 +130,8 @@
 
 void* SharedLibraryUtils::loadSharedLibrary(const char *cacheDir,
                                             const char *resName,
-                                            const char *nativeLibDir) {
+                                            const char *nativeLibDir,
+                                            bool* alreadyLoaded) {
     void *loaded = nullptr;
 
 #if defined(RS_COMPATIBILITY_LIB) && defined(__LP64__)
@@ -162,7 +142,7 @@
 
     // We should check if we can load the library from the standard app
     // location for shared libraries first.
-    loaded = loadSOHelper(scriptSOName.c_str(), cacheDir, resName);
+    loaded = loadSOHelper(scriptSOName.c_str(), cacheDir, resName, alreadyLoaded);
 
     if (loaded == nullptr) {
         ALOGE("Unable to open shared library (%s): %s",
@@ -189,8 +169,28 @@
     return loaded;
 }
 
+String8 SharedLibraryUtils::getRandomString(size_t len) {
+    char buf[len + 1];
+    for (size_t i = 0; i < len; i++) {
+        uint32_t r = arc4random() & 0xffff;
+        r %= 62;
+        if (r < 26) {
+            // lowercase
+            buf[i] = 'a' + r;
+        } else if (r < 52) {
+            // uppercase
+            buf[i] = 'A' + (r - 26);
+        } else {
+            // Use a number
+            buf[i] = '0' + (r - 52);
+        }
+    }
+    buf[len] = '\0';
+    return String8(buf);
+}
+
 void* SharedLibraryUtils::loadSOHelper(const char *origName, const char *cacheDir,
-                                       const char *resName) {
+                                       const char *resName, bool *alreadyLoaded) {
     // Keep track of which .so libraries have been loaded. Once a library is
     // in the set (per-process granularity), we must instead make a copy of
     // the original shared object (randomly named .so file) and load that one
@@ -208,6 +208,9 @@
 
     // Common path is that we have not loaded this Script/library before.
     if (LoadedLibraries.find(origName) == LoadedLibraries.end()) {
+        if (alreadyLoaded != nullptr) {
+            *alreadyLoaded = false;
+        }
         loaded = dlopen(origName, RTLD_NOW | RTLD_LOCAL);
         if (loaded) {
             LoadedLibraries.insert(origName);
@@ -215,6 +218,10 @@
         return loaded;
     }
 
+    if (alreadyLoaded != nullptr) {
+        *alreadyLoaded = true;
+    }
+
     std::string newName(cacheDir);
 
     // Append RS_CACHE_DIR only if it is not found in cacheDir
@@ -234,7 +241,7 @@
     newName.append("librs.");
     newName.append(resName);
     newName.append("#");
-    newName.append(getRandomString(6));  // 62^6 potential filename variants.
+    newName.append(getRandomString(6).string());  // 62^6 potential filename variants.
     newName.append(".so");
 
     int r = copyFile(newName.c_str(), origName);
diff --git a/cpu_ref/rsCpuExecutable.h b/cpu_ref/rsCpuExecutable.h
index 0464dac..6880970 100644
--- a/cpu_ref/rsCpuExecutable.h
+++ b/cpu_ref/rsCpuExecutable.h
@@ -43,14 +43,18 @@
     // For 64bit RS Support Lib, the shared lib path cannot be constructed from
     // cacheDir, so nativeLibDir is needed to load shared libs.
     static void* loadSharedLibrary(const char *cacheDir, const char *resName,
-                                   const char *nativeLibDir = nullptr);
+                                   const char *nativeLibDir = nullptr,
+                                   bool *alreadyLoaded = nullptr);
+
+    // Create a len length string containing random characters from [A-Za-z0-9].
+    static String8 getRandomString(size_t len);
 
 private:
     // Attempt to load the shared library from origName, but then fall back to
     // creating a copy of the shared library if necessary (to ensure instancing).
     // This function returns the dlopen()-ed handle if successful.
     static void *loadSOHelper(const char *origName, const char *cacheDir,
-                              const char *resName);
+                              const char *resName, bool* alreadyLoaded = nullptr);
 
     static const char* LD_EXE_PATH;
     static const char* RS_CACHE_DIR;
diff --git a/cpu_ref/rsCpuScript.cpp b/cpu_ref/rsCpuScript.cpp
index b95d8f7..1909e13 100644
--- a/cpu_ref/rsCpuScript.cpp
+++ b/cpu_ref/rsCpuScript.cpp
@@ -862,10 +862,8 @@
 }
 
 RsdCpuScriptImpl::~RsdCpuScriptImpl() {
-    if (mScriptExec != nullptr) {
-        delete mScriptExec;
-    }
-    if (mBoundAllocs) delete[] mBoundAllocs;
+    delete mScriptExec;
+    delete[] mBoundAllocs;
     if (mScriptSO) {
         dlclose(mScriptSO);
     }
diff --git a/cpu_ref/rsCpuScriptGroup2.cpp b/cpu_ref/rsCpuScriptGroup2.cpp
index 8923c65..bf01403 100644
--- a/cpu_ref/rsCpuScriptGroup2.cpp
+++ b/cpu_ref/rsCpuScriptGroup2.cpp
@@ -156,6 +156,7 @@
     mExecutable(nullptr), mScriptObj(nullptr) {
     rsAssert(!mGroup->mClosures.empty());
 
+    mCpuRefImpl->lockMutex();
     Batch* batch = new Batch(this, "Batch0");
     int i = 0;
     for (Closure* closure: mGroup->mClosures) {
@@ -192,6 +193,7 @@
         }
     }
 #endif  // RS_COMPATIBILITY_LIB
+    mCpuRefImpl->unlockMutex();
 }
 
 void Batch::resolveFuncPtr(void* sharedObj) {
@@ -279,6 +281,9 @@
     }
     args->push_back("-output_path");
     args->push_back(outputDir);
+
+    // The output filename has to be the last, in case we need to pop it out and
+    // replace with a different name.
     args->push_back("-o");
     args->push_back(outputFileName);
 }
@@ -396,15 +401,41 @@
     // Try to load a shared lib from code cache matching filename and checksum
     //===--------------------------------------------------------------------===//
 
-    mScriptObj = SharedLibraryUtils::loadSharedLibrary(cacheDir, resName);
+    bool alreadyLoaded = false;
+    std::string cloneName;
+
+    mScriptObj = SharedLibraryUtils::loadSharedLibrary(cacheDir, resName, nullptr,
+                                                       &alreadyLoaded);
     if (mScriptObj != nullptr) {
+        // A shared library named resName is found in code cache directory
+        // cacheDir, and loaded with the handle stored in mScriptObj.
+
         mExecutable = ScriptExecutable::createFromSharedObject(
             getCpuRefImpl()->getContext(), mScriptObj, checksum);
+
         if (mExecutable != nullptr) {
+            // The loaded shared library in mScriptObj has a matching checksum.
+            // An executable object has been created.
             return;
-        } else {
-            ALOGE("Failed to create an executable object from so file");
         }
+
+        ALOGV("Failed to create an executable object from so file due to "
+              "mismatching checksum");
+
+        if (alreadyLoaded) {
+            // The shared object found in code cache has already been loaded.
+            // A different file name is needed for the new shared library, to
+            // avoid corrupting the currently loaded instance.
+
+            cloneName.append(resName);
+            cloneName.append("#");
+            cloneName.append(SharedLibraryUtils::getRandomString(6).string());
+
+            // The last element in arguments is the output filename.
+            arguments.pop_back();
+            arguments.push_back(cloneName.c_str());
+        }
+
         dlclose(mScriptObj);
         mScriptObj = nullptr;
     }
@@ -443,6 +474,16 @@
         return;
     }
 
+    if (alreadyLoaded) {
+        // Delete the temporary, random-named file that we created to avoid
+        // interfering with an already loaded shared library.
+        string cloneFilePath(cacheDir);
+        cloneFilePath.append("/");
+        cloneFilePath.append(cloneName.c_str());
+        cloneFilePath.append(".so");
+        unlink(cloneFilePath.c_str());
+    }
+
     mExecutable = ScriptExecutable::createFromSharedObject(
         getCpuRefImpl()->getContext(),
         mScriptObj);