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