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