Home | History | Annotate | Download | only in kernel.config
      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