1 /* 2 * Copyright (C) 2014 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 // Uncomment for verbose logging. 18 // #define LOG_NDEBUG 0 19 #define LOG_TAG "webviewchromiumloader" 20 21 #include <dlfcn.h> 22 #include <errno.h> 23 #include <fcntl.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <unistd.h> 28 #include <sys/mman.h> 29 #include <sys/stat.h> 30 #include <sys/types.h> 31 32 #include <jni.h> 33 #include <android/dlext.h> 34 #include <utils/Log.h> 35 36 #define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) 37 38 namespace android { 39 namespace { 40 41 void* gReservedAddress = NULL; 42 size_t gReservedSize = 0; 43 44 jboolean DoReserveAddressSpace(jlong size) { 45 size_t vsize = static_cast<size_t>(size); 46 47 void* addr = mmap(NULL, vsize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 48 if (addr == MAP_FAILED) { 49 ALOGE("Failed to reserve %zd bytes of address space for future load of " 50 "libwebviewchromium.so: %s", 51 vsize, strerror(errno)); 52 return JNI_FALSE; 53 } 54 gReservedAddress = addr; 55 gReservedSize = vsize; 56 ALOGV("Reserved %zd bytes at %p", vsize, addr); 57 return JNI_TRUE; 58 } 59 60 jboolean DoCreateRelroFile(const char* lib, const char* relro) { 61 // Try to unlink the old file, since if this is being called, the old one is 62 // obsolete. 63 if (unlink(relro) != 0 && errno != ENOENT) { 64 // If something went wrong other than the file not existing, log a warning 65 // but continue anyway in the hope that we can successfully overwrite the 66 // existing file with rename() later. 67 ALOGW("Failed to unlink old file %s: %s", relro, strerror(errno)); 68 } 69 static const char tmpsuffix[] = ".XXXXXX"; 70 char relro_tmp[strlen(relro) + sizeof(tmpsuffix)]; 71 strlcpy(relro_tmp, relro, sizeof(relro_tmp)); 72 strlcat(relro_tmp, tmpsuffix, sizeof(relro_tmp)); 73 int tmp_fd = TEMP_FAILURE_RETRY(mkstemp(relro_tmp)); 74 if (tmp_fd == -1) { 75 ALOGE("Failed to create temporary file %s: %s", relro_tmp, strerror(errno)); 76 return JNI_FALSE; 77 } 78 android_dlextinfo extinfo; 79 extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_WRITE_RELRO; 80 extinfo.reserved_addr = gReservedAddress; 81 extinfo.reserved_size = gReservedSize; 82 extinfo.relro_fd = tmp_fd; 83 void* handle = android_dlopen_ext(lib, RTLD_NOW, &extinfo); 84 int close_result = close(tmp_fd); 85 if (handle == NULL) { 86 ALOGE("Failed to load library %s: %s", lib, dlerror()); 87 unlink(relro_tmp); 88 return JNI_FALSE; 89 } 90 if (close_result != 0 || 91 chmod(relro_tmp, S_IRUSR | S_IRGRP | S_IROTH) != 0 || 92 rename(relro_tmp, relro) != 0) { 93 ALOGE("Failed to update relro file %s: %s", relro, strerror(errno)); 94 unlink(relro_tmp); 95 return JNI_FALSE; 96 } 97 ALOGV("Created relro file %s for library %s", relro, lib); 98 return JNI_TRUE; 99 } 100 101 jboolean DoLoadWithRelroFile(const char* lib, const char* relro) { 102 int relro_fd = TEMP_FAILURE_RETRY(open(relro, O_RDONLY)); 103 if (relro_fd == -1) { 104 ALOGE("Failed to open relro file %s: %s", relro, strerror(errno)); 105 return JNI_FALSE; 106 } 107 android_dlextinfo extinfo; 108 extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_USE_RELRO; 109 extinfo.reserved_addr = gReservedAddress; 110 extinfo.reserved_size = gReservedSize; 111 extinfo.relro_fd = relro_fd; 112 void* handle = android_dlopen_ext(lib, RTLD_NOW, &extinfo); 113 close(relro_fd); 114 if (handle == NULL) { 115 ALOGE("Failed to load library %s: %s", lib, dlerror()); 116 return JNI_FALSE; 117 } 118 ALOGV("Loaded library %s with relro file %s", lib, relro); 119 return JNI_TRUE; 120 } 121 122 /******************************************************************************/ 123 /* JNI wrappers - handle string lifetimes and 32/64 ABI choice */ 124 /******************************************************************************/ 125 126 jboolean ReserveAddressSpace(JNIEnv*, jclass, jlong size) { 127 return DoReserveAddressSpace(size); 128 } 129 130 jboolean CreateRelroFile(JNIEnv* env, jclass, jstring lib32, jstring lib64, 131 jstring relro32, jstring relro64) { 132 #ifdef __LP64__ 133 jstring lib = lib64; 134 jstring relro = relro64; 135 (void)lib32; (void)relro32; 136 #else 137 jstring lib = lib32; 138 jstring relro = relro32; 139 (void)lib64; (void)relro64; 140 #endif 141 jboolean ret = JNI_FALSE; 142 const char* lib_utf8 = env->GetStringUTFChars(lib, NULL); 143 if (lib_utf8 != NULL) { 144 const char* relro_utf8 = env->GetStringUTFChars(relro, NULL); 145 if (relro_utf8 != NULL) { 146 ret = DoCreateRelroFile(lib_utf8, relro_utf8); 147 env->ReleaseStringUTFChars(relro, relro_utf8); 148 } 149 env->ReleaseStringUTFChars(lib, lib_utf8); 150 } 151 return ret; 152 } 153 154 jboolean LoadWithRelroFile(JNIEnv* env, jclass, jstring lib32, jstring lib64, 155 jstring relro32, jstring relro64) { 156 #ifdef __LP64__ 157 jstring lib = lib64; 158 jstring relro = relro64; 159 (void)lib32; (void)relro32; 160 #else 161 jstring lib = lib32; 162 jstring relro = relro32; 163 (void)lib64; (void)relro64; 164 #endif 165 jboolean ret = JNI_FALSE; 166 const char* lib_utf8 = env->GetStringUTFChars(lib, NULL); 167 if (lib_utf8 != NULL) { 168 const char* relro_utf8 = env->GetStringUTFChars(relro, NULL); 169 if (relro_utf8 != NULL) { 170 ret = DoLoadWithRelroFile(lib_utf8, relro_utf8); 171 env->ReleaseStringUTFChars(relro, relro_utf8); 172 } 173 env->ReleaseStringUTFChars(lib, lib_utf8); 174 } 175 return ret; 176 } 177 178 const char kClassName[] = "android/webkit/WebViewFactory"; 179 const JNINativeMethod kJniMethods[] = { 180 { "nativeReserveAddressSpace", "(J)Z", 181 reinterpret_cast<void*>(ReserveAddressSpace) }, 182 { "nativeCreateRelroFile", 183 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z", 184 reinterpret_cast<void*>(CreateRelroFile) }, 185 { "nativeLoadWithRelroFile", 186 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z", 187 reinterpret_cast<void*>(LoadWithRelroFile) }, 188 }; 189 190 } // namespace 191 192 void RegisterWebViewFactory(JNIEnv* env) { 193 // If either of these fail, it will set an exception that will be thrown on 194 // return, so no need to handle errors here. 195 jclass clazz = env->FindClass(kClassName); 196 if (clazz) { 197 env->RegisterNatives(clazz, kJniMethods, NELEM(kJniMethods)); 198 } 199 } 200 201 } // namespace android 202 203 JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) { 204 JNIEnv* env = NULL; 205 if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { 206 ALOGE("GetEnv failed"); 207 return JNI_ERR; 208 } 209 android::RegisterWebViewFactory(env); 210 return JNI_VERSION_1_6; 211 } 212