1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // This file implements the native methods of 6 // org.content.chromium.app.LinkerTests 7 // Unlike the content of linker_jni.cc, it is part of the content library and 8 // can 9 // thus use base/ and the C++ STL. 10 11 #include "content/shell/android/linker_test_apk/chromium_linker_test_linker_tests.h" 12 13 #include <errno.h> 14 #include <sys/mman.h> 15 #include <stdio.h> 16 #include <string> 17 18 #include "base/basictypes.h" 19 #include "base/debug/proc_maps_linux.h" 20 #include "base/logging.h" 21 #include "base/strings/stringprintf.h" 22 23 #include "jni/LinkerTests_jni.h" 24 25 namespace content { 26 27 namespace { 28 29 using base::debug::MappedMemoryRegion; 30 31 jboolean RunChecks(bool in_browser_process, bool need_relros) { 32 33 // IMPORTANT NOTE: The Python test control script reads the logcat for 34 // lines like: 35 // BROWSER_LINKER_TEST: <status> 36 // RENDERER_LINKER_TEST: <status> 37 // 38 // Where <status> can be either SUCCESS or FAIL. Other lines starting 39 // with the same prefixes, but not using SUCCESS or FAIL are ignored. 40 const char* prefix = 41 in_browser_process ? "BROWSER_LINKER_TEST: " : "RENDERER_LINKER_TEST: "; 42 43 // The RELRO section(s), after being copied into an ashmem region, will 44 // appear in /proc/self/maps as a mapped memory region for a file name 45 // that begins with the following prefix. 46 // 47 // Note that the full name will be something like: 48 // "/dev/ashmem/RELRO:<libname> (deleted)" 49 // 50 // Where <libname> is the library name and '(deleted)' is actually 51 // added by the kernel to indicate there is no corresponding file 52 // on the filesystem. 53 // 54 // For regular builds, there is only one library, and thus one RELRO 55 // section, but for the component build, there are several libraries, 56 // each one with its own RELRO. 57 static const char kRelroSectionPrefix[] = "/dev/ashmem/RELRO:"; 58 59 // Parse /proc/self/maps and builds a list of region mappings in this 60 // process. 61 std::string maps; 62 base::debug::ReadProcMaps(&maps); 63 if (maps.empty()) { 64 LOG(ERROR) << prefix << "FAIL Cannot parse /proc/self/maps"; 65 return false; 66 } 67 68 std::vector<MappedMemoryRegion> regions; 69 base::debug::ParseProcMaps(maps, ®ions); 70 if (regions.empty()) { 71 LOG(ERROR) << prefix << "FAIL Cannot read memory mappings in this process"; 72 return false; 73 } 74 75 size_t num_shared_relros = 0; 76 size_t num_bad_shared_relros = 0; 77 78 for (size_t n = 0; n < regions.size(); ++n) { 79 MappedMemoryRegion& region = regions[n]; 80 81 if (region.path.find(kRelroSectionPrefix) != 0) { 82 // Ignore any mapping that isn't a shared RELRO. 83 continue; 84 } 85 86 num_shared_relros++; 87 88 void* region_start = reinterpret_cast<void*>(region.start); 89 void* region_end = reinterpret_cast<void*>(region.end); 90 91 // Check that it is mapped read-only. 92 const uint8 expected_flags = MappedMemoryRegion::READ; 93 const uint8 expected_mask = MappedMemoryRegion::READ | 94 MappedMemoryRegion::WRITE | 95 MappedMemoryRegion::EXECUTE; 96 97 uint8 region_flags = region.permissions & expected_mask; 98 if (region_flags != expected_flags) { 99 LOG(ERROR) 100 << prefix 101 << base::StringPrintf( 102 "Shared RELRO section at %p-%p is not mapped read-only. " 103 "Protection flags are %d (%d expected)!", 104 region_start, 105 region_end, 106 region_flags, 107 expected_flags); 108 num_bad_shared_relros++; 109 continue; 110 } 111 112 // Check that trying to remap it read-write fails with EACCES 113 size_t region_size = region.end - region.start; 114 int ret = ::mprotect(region_start, region_size, PROT_READ | PROT_WRITE); 115 if (ret != -1) { 116 LOG(ERROR) 117 << prefix 118 << base::StringPrintf( 119 "Shared RELRO section at %p-%p could be remapped read-write!?", 120 region_start, 121 region_end); 122 num_bad_shared_relros++; 123 // Just in case. 124 ::mprotect(region_start, region_size, PROT_READ); 125 } else if (errno != EACCES) { 126 LOG(ERROR) << prefix << base::StringPrintf( 127 "Shared RELRO section at %p-%p failed " 128 "read-write mprotect with " 129 "unexpected error %d (EACCES:%d wanted): %s", 130 region_start, 131 region_end, 132 errno, 133 EACCES, 134 strerror(errno)); 135 num_bad_shared_relros++; 136 } 137 } 138 139 VLOG(0) 140 << prefix 141 << base::StringPrintf( 142 "There are %d shared RELRO sections in this process, %d are bad", 143 num_shared_relros, 144 num_bad_shared_relros); 145 146 if (num_bad_shared_relros > 0) { 147 LOG(ERROR) << prefix << "FAIL Bad Relros sections in this process"; 148 return false; 149 } 150 151 if (need_relros) { 152 if (num_shared_relros == 0) { 153 LOG(ERROR) << prefix 154 << "FAIL Missing shared RELRO sections in this process!"; 155 return false; 156 } 157 } else { 158 if (num_shared_relros > 0) { 159 LOG(ERROR) << prefix << "FAIL Unexpected " << num_shared_relros 160 << " shared RELRO sections in this process!"; 161 return false; 162 } 163 } 164 165 VLOG(0) << prefix << "SUCCESS"; 166 return true; 167 } 168 169 } // namespace 170 171 jboolean CheckForSharedRelros(JNIEnv* env, 172 jclass clazz, 173 jboolean in_browser_process) { 174 return RunChecks(in_browser_process, true); 175 } 176 177 jboolean CheckForNoSharedRelros(JNIEnv* env, 178 jclass clazz, 179 jboolean in_browser_process) { 180 return RunChecks(in_browser_process, false); 181 } 182 183 bool RegisterLinkerTestsJni(JNIEnv* env) { return RegisterNativesImpl(env); } 184 185 } // namespace content 186