| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_NDEBUG 0 |
| #define LOG_TAG "AslrMallocTest" |
| |
| #if !defined(BUILD_ONLY) |
| #include <android-base/file.h> |
| #include <android-base/parseint.h> |
| #include <android-base/stringprintf.h> |
| #include <android-base/strings.h> |
| #include <linux/limits.h> |
| #include <math.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| #include <unistd.h> |
| #include <unordered_set> |
| #endif |
| |
| #include <gtest/gtest.h> |
| #include <string> |
| #include <utils/Log.h> |
| |
| /* minimum entropy for malloc return addresses */ |
| const size_t minEntropyBits = 8; |
| |
| /* test using the following allocation sizes */ |
| const size_t allocSizes[] = { |
| 1 << 8, // small |
| 1 << 16, // large |
| 1 << 23 // huge |
| }; |
| |
| /* when started using this argument followed by the allocation size, |
| * performs malloc(size) and prints out the address */ |
| static const std::string argPrint = "--print-malloc-address"; |
| |
| #if !defined(BUILD_ONLY) |
| class AslrMallocTest : public ::testing::Test |
| { |
| protected: |
| std::string self_; |
| |
| AslrMallocTest() {} |
| virtual ~AslrMallocTest() {} |
| |
| virtual void SetUp() |
| { |
| /* path to self for exec */ |
| char path[PATH_MAX]; |
| auto size = readlink("/proc/self/exe", path, sizeof(path)); |
| ASSERT_TRUE(size > 0 && size < PATH_MAX); |
| path[size] = '\0'; |
| self_ = path; |
| } |
| |
| void GetAddress(size_t allocSize, uintptr_t& address) |
| { |
| int fds[2]; |
| ASSERT_TRUE(pipe(fds) != -1); |
| |
| auto pid = fork(); |
| ASSERT_TRUE(pid != -1); |
| |
| if (pid == 0) { |
| /* child process */ |
| ASSERT_TRUE(TEMP_FAILURE_RETRY(dup2(fds[1], STDOUT_FILENO)) != -1); |
| |
| for (auto fd : fds) { |
| TEMP_FAILURE_RETRY(close(fd)); |
| } |
| |
| /* exec self to print malloc output */ |
| ASSERT_TRUE(execl(self_.c_str(), self_.c_str(), argPrint.c_str(), |
| android::base::StringPrintf("%zu", allocSize).c_str(), |
| nullptr) != -1); |
| } |
| |
| /* parent process */ |
| TEMP_FAILURE_RETRY(close(fds[1])); |
| |
| std::string output; |
| ASSERT_TRUE(android::base::ReadFdToString(fds[0], &output)); |
| TEMP_FAILURE_RETRY(close(fds[0])); |
| |
| int status; |
| ASSERT_TRUE(waitpid(pid, &status, 0) != -1); |
| ASSERT_TRUE(WEXITSTATUS(status) == EXIT_SUCCESS); |
| |
| ASSERT_TRUE(android::base::ParseUint(output.c_str(), &address)); |
| } |
| |
| void TestRandomization() |
| { |
| /* should be sufficient to see minEntropyBits when rounded up */ |
| size_t iterations = 2 * (1 << minEntropyBits); |
| |
| for (auto size : allocSizes) { |
| ALOGV("running %zu iterations for allocation size %zu", |
| iterations, size); |
| |
| /* collect unique return addresses */ |
| std::unordered_set<uintptr_t> addresses; |
| |
| for (size_t i = 0; i < iterations; ++i) { |
| uintptr_t address; |
| GetAddress(size, address); |
| |
| addresses.emplace(address); |
| } |
| |
| size_t entropy = static_cast<size_t>(0.5 + |
| log2(static_cast<double>(addresses.size()))); |
| |
| ALOGV("%zu bits of entropy for allocation size %zu (minimum %zu)", |
| entropy, size, minEntropyBits); |
| ALOGE_IF(entropy < minEntropyBits, |
| "insufficient entropy for malloc(%zu)", size); |
| ASSERT_TRUE(entropy >= minEntropyBits); |
| } |
| } |
| }; |
| #else /* defined(BUILD_ONLY) */ |
| class AslrMallocTest : public ::testing::Test |
| { |
| protected: |
| void TestRandomization() {} |
| }; |
| #endif |
| |
| TEST_F(AslrMallocTest, testMallocRandomization) { |
| TestRandomization(); |
| } |
| |
| int main(int argc, char **argv) |
| { |
| #if !defined(BUILD_ONLY) |
| if (argc == 3 && argPrint == argv[1]) { |
| size_t size; |
| |
| if (!android::base::ParseUint(argv[2], &size)) { |
| return EXIT_FAILURE; |
| } |
| |
| printf("%p", malloc(size)); |
| return EXIT_SUCCESS; |
| } |
| #endif |
| |
| testing::InitGoogleTest(&argc, argv); |
| return RUN_ALL_TESTS(); |
| } |