• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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