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 
AslrMallocTest()56     AslrMallocTest() {}
~AslrMallocTest()57     virtual ~AslrMallocTest() {}
58 
SetUp()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 
GetAddress(size_t allocSize,uintptr_t & address)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 
TestRandomization()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 
TEST_F(AslrMallocTest,testMallocRandomization)136 TEST_F(AslrMallocTest, testMallocRandomization) {
137     TestRandomization();
138 }
139 
main(int argc,char ** argv)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