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