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
get_mmap_rnd_bits(bool compat)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
set_mmap_rnd_bits(unsigned int new_val,bool compat)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
scrape_addr(const char * exec_name,const char * lib_match)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
calc_mmap_entropy(const char * exec_name,const char * lib_match,size_t samp_sz)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
SetUpTestCase()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
TearDown()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
TEST_F(AslrMmapTest,entropy_min_def)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
TEST_F(AslrMmapTest,entropy_min_cmpt_def)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