Home | History | Annotate | Download | only in libjnitest
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 /*
     18  * Tests accessibility of platform native libraries
     19  */
     20 
     21 #include <dirent.h>
     22 #include <dlfcn.h>
     23 #include <fcntl.h>
     24 #include <jni.h>
     25 #include <JNIHelp.h>
     26 #include <libgen.h>
     27 #include <stdlib.h>
     28 #include <sys/types.h>
     29 #include <sys/stat.h>
     30 #include <unistd.h>
     31 
     32 #include <list>
     33 #include <string>
     34 #include <unordered_set>
     35 #include <vector>
     36 
     37 #include "ScopedLocalRef.h"
     38 #include "ScopedUtfChars.h"
     39 
     40 #if defined(__LP64__)
     41 static const std::string kSystemLibraryPath = "/system/lib64";
     42 static const std::string kVendorLibraryPath = "/vendor/lib64";
     43 #else
     44 static const std::string kSystemLibraryPath = "/system/lib";
     45 static const std::string kVendorLibraryPath = "/vendor/lib";
     46 #endif
     47 
     48 // This is not the complete list - just a small subset
     49 // of the libraries that should reside in /system/lib
     50 // (in addition to kSystemPublicLibraries)
     51 static std::vector<std::string> kSystemLibraries = {
     52     "libart.so",
     53     "libandroid_runtime.so",
     54     "libbinder.so",
     55     "libcutils.so",
     56     "libgui.so",
     57     "libmedia.so",
     58     "libnativehelper.so",
     59     "libstagefright.so",
     60     "libui.so",
     61     "libutils.so",
     62   };
     63 
     64 static bool is_directory(const std::string path) {
     65   struct stat sb;
     66   if (stat(path.c_str(), &sb) != -1) {
     67     return S_ISDIR(sb.st_mode);
     68   }
     69 
     70   return false;
     71 }
     72 
     73 static bool is_libdl(const std::string path) {
     74   return kSystemLibraryPath + "/libdl.so" == path;
     75 }
     76 
     77 static bool already_loaded(const std::string& library, const std::string& err) {
     78   if (err.find("dlopen failed: library \"" + library + "\"") != 0 ||
     79       err.find("is not accessible for the namespace \"classloader-namespace\"") == std::string::npos) {
     80     return false;
     81   }
     82   return true;
     83 }
     84 
     85 static bool check_lib(const std::string& path,
     86                       const std::string& library_path,
     87                       const std::unordered_set<std::string>& libraries,
     88                       std::vector<std::string>* errors) {
     89   if (is_libdl(path)) {
     90     // TODO (dimitry): we skip check for libdl.so because
     91     // 1. Linker will fail to check accessibility because it imposes as libdl.so (see http://b/27106625)
     92     // 2. It is impractical to dlopen libdl.so because this library already depends
     93     //    on it in order to have dlopen()
     94     return true;
     95   }
     96 
     97   std::unique_ptr<void, int (*)(void*)> handle(dlopen(path.c_str(), RTLD_NOW), dlclose);
     98 
     99   // The current restrictions on public libraries:
    100   //  - It must exist only in the top level directory of "library_path".
    101   //  - No library with the same name can be found in a sub directory.
    102   //  - Each public library does not contain any directory components.
    103 
    104   // Check if this library should be considered a public library.
    105   std::string baselib = basename(path.c_str());
    106   if (libraries.find(baselib) != libraries.end() &&
    107       library_path + "/" + baselib == path) {
    108     if (handle.get() == nullptr) {
    109       errors->push_back("The library \"" + path +
    110                         "\" is a public library but it cannot be loaded: " + dlerror());
    111       return false;
    112     }
    113   } else if (handle.get() != nullptr) {
    114     errors->push_back("The library \"" + path + "\" is not a public library but it loaded.");
    115     return false;
    116   } else { // (handle == nullptr && !shouldBeAccessible(path))
    117     // Check the error message
    118     std::string err = dlerror();
    119     if (!already_loaded(path, err)) {
    120       errors->push_back("unexpected dlerror: " + err);
    121       return false;
    122     }
    123   }
    124   return true;
    125 }
    126 
    127 static bool check_path(const std::string& library_path,
    128                        const std::unordered_set<std::string> libraries,
    129                        std::vector<std::string>* errors) {
    130   bool success = true;
    131   std::list<std::string> dirs = { library_path };
    132   while (!dirs.empty()) {
    133     std::string dir = dirs.front();
    134     dirs.pop_front();
    135 
    136     auto dir_deleter = [](DIR* handle) { closedir(handle); };
    137     std::unique_ptr<DIR, decltype(dir_deleter)> dirp(opendir(dir.c_str()), dir_deleter);
    138     if (dirp == nullptr) {
    139       errors->push_back("Failed to open " + dir + ": " + strerror(errno));
    140       success = false;
    141       continue;
    142     }
    143 
    144     dirent* dp;
    145     while ((dp = readdir(dirp.get())) != nullptr) {
    146       // skip "." and ".."
    147       if (strcmp(".", dp->d_name) == 0 || strcmp("..", dp->d_name) == 0) {
    148         continue;
    149       }
    150 
    151       std::string path = dir + "/" + dp->d_name;
    152       if (is_directory(path)) {
    153         dirs.push_back(path);
    154       } else if (!check_lib(path, library_path, libraries, errors)) {
    155         success = false;
    156       }
    157     }
    158   }
    159 
    160   return success;
    161 }
    162 
    163 static bool jobject_array_to_set(JNIEnv* env,
    164                                  jobjectArray java_libraries_array,
    165                                  std::unordered_set<std::string>* libraries,
    166                                  std::string* error_msg) {
    167   error_msg->clear();
    168   size_t size = env->GetArrayLength(java_libraries_array);
    169   bool success = true;
    170   for (size_t i = 0; i<size; ++i) {
    171     ScopedLocalRef<jstring> java_soname(
    172         env, (jstring) env->GetObjectArrayElement(java_libraries_array, i));
    173     std::string soname(ScopedUtfChars(env, java_soname.get()).c_str());
    174 
    175     // Verify that the name doesn't contain any directory components.
    176     if (soname.rfind('/') != std::string::npos) {
    177       *error_msg += "\n---Illegal value, no directories allowed: " + soname;
    178       continue;
    179     }
    180 
    181     // Check to see if the string ends in " 32" or " 64" to indicate the
    182     // library is only public for one bitness.
    183     size_t space_pos = soname.rfind(' ');
    184     if (space_pos != std::string::npos) {
    185       std::string type = soname.substr(space_pos + 1);
    186       if (type != "32" && type != "64") {
    187         *error_msg += "\n---Illegal value at end of line (only 32 or 64 allowed): " + soname;
    188         success = false;
    189         continue;
    190       }
    191 #if defined(__LP64__)
    192       if (type == "32") {
    193         // Skip this, it's a 32 bit only public library.
    194         continue;
    195       }
    196 #else
    197       if (type == "64") {
    198         // Skip this, it's a 64 bit only public library.
    199         continue;
    200       }
    201 #endif
    202       soname.resize(space_pos);
    203     }
    204 
    205     libraries->insert(soname);
    206   }
    207 
    208   return success;
    209 }
    210 
    211 extern "C" JNIEXPORT jstring JNICALL
    212     Java_android_jni_cts_LinkerNamespacesHelper_runAccessibilityTestImpl(
    213         JNIEnv* env,
    214         jclass clazz __attribute__((unused)),
    215         jobjectArray java_system_public_libraries,
    216         jobjectArray java_vendor_public_libraries) {
    217   bool success = true;
    218   std::vector<std::string> errors;
    219   std::string error_msg;
    220   std::unordered_set<std::string> vendor_public_libraries;
    221   if (!jobject_array_to_set(env, java_vendor_public_libraries, &vendor_public_libraries,
    222                             &error_msg)) {
    223     success = false;
    224     errors.push_back("Errors in vendor public library file:" + error_msg);
    225   }
    226 
    227   std::unordered_set<std::string> system_public_libraries;
    228   if (!jobject_array_to_set(env, java_system_public_libraries, &system_public_libraries,
    229                             &error_msg)) {
    230     success = false;
    231     errors.push_back("Errors in system public library file:" + error_msg);
    232   }
    233 
    234   // Check the system libraries.
    235   if (!check_path(kSystemLibraryPath, system_public_libraries, &errors)) {
    236     success = false;
    237   }
    238 
    239   // Check that the mandatory system libraries are present - the grey list
    240   for (const auto& name : kSystemLibraries) {
    241     std::string library = kSystemLibraryPath + "/" + name;
    242     void* handle = dlopen(library.c_str(), RTLD_NOW);
    243     if (handle == nullptr) {
    244       std::string err = dlerror();
    245       // If the library is already loaded, then dlopen failing is okay.
    246       if (!already_loaded(library, err)) {
    247           errors.push_back("Mandatory system library \"" + library + "\" failed to load: " + err);
    248           success = false;
    249       }
    250     } else {
    251       dlclose(handle);
    252     }
    253   }
    254 
    255   // Check the vendor libraries.
    256   if (!check_path(kVendorLibraryPath, vendor_public_libraries, &errors)) {
    257     success = false;
    258   }
    259 
    260   if (!success) {
    261     std::string error_str;
    262     for (const auto& line : errors) {
    263       error_str += line + '\n';
    264     }
    265     return env->NewStringUTF(error_str.c_str());
    266   }
    267 
    268   return nullptr;
    269 }
    270 
    271