Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright (C) 2015 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 #define LOG_NDEBUG 0
     18 #define LOG_TAG "AslrMallocTest"
     19 
     20 #include <android-base/file.h>
     21 #include <android-base/parseint.h>
     22 #include <android-base/stringprintf.h>
     23 #include <android-base/strings.h>
     24 #include <linux/limits.h>
     25 #include <math.h>
     26 #include <stdint.h>
     27 #include <stdio.h>
     28 #include <sys/types.h>
     29 #include <sys/wait.h>
     30 #include <unistd.h>
     31 #include <unordered_set>
     32 
     33 #include <gtest/gtest.h>
     34 #include <string>
     35 #include <utils/Log.h>
     36 
     37 /* minimum entropy for malloc return addresses */
     38 const size_t minEntropyBits = 8;
     39 
     40 /* test using the following allocation sizes */
     41 const size_t allocSizes[] = {
     42     1 << 8,     // small
     43     1 << 16,    // large
     44     1 << 23     // huge
     45 };
     46 
     47 /* when started using this argument followed by the allocation size,
     48  * performs malloc(size) and prints out the address */
     49 static const std::string argPrint = "--print-malloc-address";
     50 
     51 class AslrMallocTest : public ::testing::Test
     52 {
     53 protected:
     54     std::string self_;
     55 
     56     AslrMallocTest() {}
     57     virtual ~AslrMallocTest() {}
     58 
     59     virtual void SetUp()
     60     {
     61         /* path to self for exec */
     62         char path[PATH_MAX];
     63         auto size = readlink("/proc/self/exe", path, sizeof(path));
     64         ASSERT_TRUE(size > 0 && size < PATH_MAX);
     65         path[size] = '\0';
     66         self_ = path;
     67     }
     68 
     69     void GetAddress(size_t allocSize, uintptr_t& address)
     70     {
     71         int fds[2];
     72         ASSERT_TRUE(pipe(fds) != -1);
     73 
     74         auto pid = fork();
     75         ASSERT_TRUE(pid != -1);
     76 
     77         if (pid == 0) {
     78             /* child process */
     79             ASSERT_TRUE(TEMP_FAILURE_RETRY(dup2(fds[1], STDOUT_FILENO)) != -1);
     80 
     81             for (auto fd : fds) {
     82                 TEMP_FAILURE_RETRY(close(fd));
     83             }
     84 
     85             /* exec self to print malloc output */
     86             ASSERT_TRUE(execl(self_.c_str(), self_.c_str(), argPrint.c_str(),
     87                 android::base::StringPrintf("%zu", allocSize).c_str(),
     88                 nullptr) != -1);
     89         }
     90 
     91         /* parent process */
     92         TEMP_FAILURE_RETRY(close(fds[1]));
     93 
     94         std::string output;
     95         ASSERT_TRUE(android::base::ReadFdToString(fds[0], &output));
     96         TEMP_FAILURE_RETRY(close(fds[0]));
     97 
     98         int status;
     99         ASSERT_TRUE(waitpid(pid, &status, 0) != -1);
    100         ASSERT_TRUE(WEXITSTATUS(status) == EXIT_SUCCESS);
    101 
    102         ASSERT_TRUE(android::base::ParseUint(output.c_str(), &address));
    103     }
    104 
    105     void TestRandomization()
    106     {
    107         /* should be sufficient to see minEntropyBits when rounded up */
    108         size_t iterations = 2 * (1 << minEntropyBits);
    109 
    110         for (auto size : allocSizes) {
    111             ALOGV("running %zu iterations for allocation size %zu",
    112                 iterations, size);
    113 
    114             /* collect unique return addresses */
    115             std::unordered_set<uintptr_t> addresses;
    116 
    117             for (size_t i = 0; i < iterations; ++i) {
    118                 uintptr_t address;
    119                 GetAddress(size, address);
    120 
    121                 addresses.emplace(address);
    122             }
    123 
    124             size_t entropy = static_cast<size_t>(0.5 +
    125                                 log2(static_cast<double>(addresses.size())));
    126 
    127             ALOGV("%zu bits of entropy for allocation size %zu (minimum %zu)",
    128                 entropy, size, minEntropyBits);
    129             ALOGE_IF(entropy < minEntropyBits,
    130                 "insufficient entropy for malloc(%zu)", size);
    131             ASSERT_TRUE(entropy >= minEntropyBits);
    132         }
    133     }
    134 };
    135 
    136 TEST_F(AslrMallocTest, testMallocRandomization) {
    137     TestRandomization();
    138 }
    139 
    140 int main(int argc, char **argv)
    141 {
    142     if (argc == 3 && argPrint == argv[1]) {
    143         size_t size;
    144 
    145         if (!android::base::ParseUint(argv[2], &size)) {
    146             return EXIT_FAILURE;
    147         }
    148 
    149         void* p = malloc(size);
    150         printf("%p", p);
    151         free(p);
    152         return EXIT_SUCCESS;
    153     }
    154 
    155     testing::InitGoogleTest(&argc, argv);
    156     return RUN_ALL_TESTS();
    157 }
    158