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 #include <ctype.h> 18 19 #include "aslr_test.h" 20 21 unsigned int get_mmap_rnd_bits(bool compat) { 22 std::string path; 23 24 if (compat) 25 path = PROCFS_COMPAT_PATH; 26 else 27 path = PROCFS_PATH; 28 29 std::ifstream bi_file(path); 30 if (!bi_file) 31 return false; 32 std::string str_rec; 33 bi_file >> str_rec; 34 35 return stoi(str_rec); 36 } 37 38 bool set_mmap_rnd_bits(unsigned int new_val, bool compat) { 39 std::string path; 40 41 if (compat) 42 path = "/proc/sys/vm/mmap_rnd_compat_bits"; 43 else 44 path = "/proc/sys/vm/mmap_rnd_bits"; 45 46 std::ofstream bo_file(path, std::ios::out); 47 if (!bo_file) 48 return false; 49 50 std::string str_val = std::to_string(new_val); 51 bo_file << str_val << std::flush; 52 bo_file.close(); 53 54 // check to make sure it was recorded 55 std::ifstream bi_file(path); 56 if (!bi_file) 57 return false; 58 std::string str_rec; 59 bi_file >> str_rec; 60 bi_file.close(); 61 if (str_val.compare(str_rec) != 0) 62 return false; 63 return true; 64 } 65 66 std::string scrape_addr(const char *exec_name, const char *lib_match) { 67 pid_t pid; 68 int fd[2]; 69 char buff[MAX_ADDR_LEN]; 70 int len, status; 71 if(pipe(fd)) { 72 std::cerr << "Error creating pipe:" << strerror(errno) << "\n"; 73 return std::string(); 74 } 75 76 if ((pid = fork()) < 0) { 77 std::cerr << "Error creating new process: " << strerror(errno) << "\n"; 78 close(fd[0]); 79 close(fd[1]); 80 return std::string(); 81 } else if (pid > 0) { 82 // parent 83 close(fd[1]); 84 wait(&status); 85 if (status == -1) { 86 std::cerr << "Unable to find starting address of mmapp'd libc. Aborting.\n"; 87 close(fd[0]); 88 return std::string(); 89 } 90 len = read(fd[0], buff, MAX_ADDR_LEN - 1); 91 if (len < 0) { 92 std::cerr << "Error reading pipe from child: " << strerror(errno) << "\n"; 93 close(fd[0]); 94 return std::string(); 95 } 96 buff[len] = '\0'; 97 close(fd[0]); 98 } else { 99 // child, dup 'n' exec 100 close(fd[0]); 101 if(dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO) { 102 std::cerr << "Error dup'n pipe to STDOUT of child: " << strerror(errno) << "\n"; 103 close(fd[1]); 104 return std::string(); 105 } 106 if(execlp(exec_name, exec_name, lib_match, (char *) NULL)) { 107 std::cerr << "Error exec'ing mmap_scraper: " << strerror(errno) << "\n"; 108 close(fd[1]); 109 return std::string(); 110 } 111 } 112 return std::string(buff, strlen(buff)); 113 } 114 115 unsigned int calc_mmap_entropy(const char *exec_name, const char *lib_match, size_t samp_sz) { 116 uint64_t addr, min_addr, max_addr; 117 118 std::unordered_set<uint64_t> addrs = { }; 119 120 // get our first value 121 std::string addr_str = scrape_addr(exec_name, lib_match); 122 if (addr_str.empty()) { 123 std::cerr << "empty first address"; 124 return 0; 125 } 126 if (!isxdigit(addr_str[0])) { 127 std::cerr << "invalid address: " << addr_str; 128 return 0; 129 } 130 addr = min_addr = max_addr = std::stoll(addr_str, 0, 16); 131 addrs.insert(addr); 132 for (unsigned int i = 0; i < samp_sz - 1; ++i) { 133 addr_str = scrape_addr(exec_name, lib_match); 134 if (addr_str.empty()) { 135 std::cerr << "empty address"; 136 return 0; 137 } 138 if (!isxdigit(addr_str[0])) { 139 std::cerr << "invalid address: " << addr_str; 140 return 0; 141 } 142 addr = std::stoll(addr_str, 0, 16); 143 if (addr < min_addr) 144 min_addr = addr; 145 if (addr >= max_addr) 146 max_addr = addr; 147 addrs.insert(addr); 148 } 149 if (addrs.size() < (samp_sz >> 1)) { 150 std::cerr << "> 50% collisions in mmap addresses, entropy appears to be rigged!"; 151 return 0; 152 } 153 unsigned int e_bits = (int) (std::ceil(std::log2(max_addr - min_addr)) - std::log2(getpagesize())); 154 return e_bits; 155 } 156 157 const char *AslrMmapTest::path; 158 const char *AslrMmapTest::lib; 159 unsigned int AslrMmapTest::def, AslrMmapTest::min, AslrMmapTest::max; 160 bool AslrMmapTest::compat = false, AslrMmapTest::user32 = false; 161 unsigned int AslrMmapTest::def_cmpt, AslrMmapTest::min_cmpt, AslrMmapTest::max_cmpt; 162 163 void AslrMmapTest::SetUpTestCase() { 164 /* set up per-arch values */ 165 #if defined(__x86_64__) 166 def = 32; 167 min = 28; 168 max = 32; 169 path = SCRAPE_PATH_64; 170 lib = SCRAPE_LIB_64; 171 172 compat = true; 173 def_cmpt = 16; 174 min_cmpt = 8; 175 max_cmpt = 16; 176 177 #elif defined(__i386__) 178 def = 16; 179 min = 8; 180 max = 16; 181 path = SCRAPE_PATH_32; 182 lib = SCRAPE_LIB_32; 183 184 if (!access(PROCFS_COMPAT_PATH, F_OK)) { 185 // running 32 bit userspace over 64-bit kernel 186 user32 = true; 187 def_cmpt = 16; 188 min_cmpt = 8; 189 max_cmpt = 16; 190 } 191 192 #elif defined(__aarch64__) 193 unsigned int pgbits = std::log2(getpagesize()); 194 def = 24; 195 min = 18 - (pgbits - 12); 196 max = 24; 197 path = SCRAPE_PATH_64; 198 lib = SCRAPE_LIB_64; 199 200 compat = true; 201 def_cmpt = 16; 202 min_cmpt = 11 - (pgbits - 12); 203 max_cmpt = 16; 204 205 #elif defined(__arm__) 206 unsigned int pgbits = std::log2(getpagesize()); 207 def = 16; 208 min = 8; 209 max = 16; 210 path = SCRAPE_PATH_32; 211 lib = SCRAPE_LIB_32; 212 213 if (!access(PROCFS_COMPAT_PATH, F_OK)) { 214 // running 32 bit userspace over 64-bit kernel 215 user32 = true; 216 def_cmpt = 16; 217 min_cmpt = 11 - (pgbits - 12);; 218 max_cmpt = 16; 219 } 220 #endif 221 } 222 223 void AslrMmapTest::TearDown() { 224 if (!user32) 225 set_mmap_rnd_bits(def, false); 226 if (user32 || compat) 227 set_mmap_rnd_bits(def_cmpt, true); 228 } 229 230 /* run tests only if on supported arch */ 231 #if defined(__x86_64__) || defined(__i386__) || defined(__aarch64__) || defined(__arm__) 232 233 TEST_F(AslrMmapTest, entropy_min_def) { 234 if (user32) { 235 // running 32-bit userspace on 64-bit kernel, only compat used. 236 return; 237 } else { 238 EXPECT_GE(def, calc_mmap_entropy(path, lib, 16)); 239 } 240 } 241 242 TEST_F(AslrMmapTest, entropy_min_cmpt_def) { 243 if (compat || user32) { 244 EXPECT_GE(def_cmpt, calc_mmap_entropy(SCRAPE_PATH_32, SCRAPE_LIB_32, 16)); 245 } 246 } 247 248 #endif /* supported arch */ 249