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 "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