summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Cashman <dcashman@google.com>2016-03-14 17:38:58 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2016-03-14 17:38:59 +0000
commitb098491ac69154f8633d11c0fc4a50429d374a4a (patch)
tree9a66fe158756dce427c5b3c9ebb970ea0402f5a1
parentad64c38f5652c8557c3f61953a92c457da3e82d4 (diff)
parentd661642651a6689c76d3f575b4b5dbf04e6b75bd (diff)
downloadextras-b098491ac69154f8633d11c0fc4a50429d374a4a.tar.gz
Merge "Migrate aslr test to gtest." into nyc-dev
-rw-r--r--tests/kernel.config/Android.mk9
-rw-r--r--tests/kernel.config/aslr_rec_test.cpp135
-rw-r--r--tests/kernel.config/aslr_test.cpp231
-rw-r--r--tests/kernel.config/aslr_test.h77
-rw-r--r--tests/kernel.config/scrape_mmap_addr.cpp32
5 files changed, 484 insertions, 0 deletions
diff --git a/tests/kernel.config/Android.mk b/tests/kernel.config/Android.mk
index 7a02521a..fc90f669 100644
--- a/tests/kernel.config/Android.mk
+++ b/tests/kernel.config/Android.mk
@@ -16,6 +16,7 @@ test_c_flags := \
# Required Tests
cts_src_files := \
+ aslr_test.cpp \
multicast_test.cpp \
pstore_test.cpp \
sysvipc_test.cpp \
@@ -24,6 +25,7 @@ cts_src_files := \
# Required plus Recommended Tests
test_src_files := \
$(cts_src_files) \
+ aslr_rec_test.cpp \
mmc_max_speed_test.cpp \
cts_executable := CtsKernelConfigTestCases
@@ -68,3 +70,10 @@ LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
include $(BUILD_HOST_NATIVE_TEST)
endif # ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ scrape_mmap_addr.cpp
+
+LOCAL_MODULE := scrape_mmap_addr
+include $(BUILD_NATIVE_TEST)
diff --git a/tests/kernel.config/aslr_rec_test.cpp b/tests/kernel.config/aslr_rec_test.cpp
new file mode 100644
index 00000000..3dc61e6e
--- /dev/null
+++ b/tests/kernel.config/aslr_rec_test.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "aslr_test.h"
+
+/* run tests if on supported arch */
+#if defined(__x86__64__) || defined(__i386__) || defined(__aarch64__) || defined(__arm__)
+
+/* make sure the default entropy values matches what we expect */
+TEST_F(AslrMmapTest, match_default) {
+ if (user32) {
+ // running 32-bit userspace on 64-bit kernel, only compat used.
+ return;
+ } else {
+ EXPECT_EQ(def, get_mmap_rnd_bits(false));
+ }
+}
+
+/* make sure the default compat entropy values matches what we expect */
+TEST_F(AslrMmapTest, match_compat_default) {
+ if (compat || user32)
+ EXPECT_EQ(def_cmpt, get_mmap_rnd_bits(true));
+}
+
+/* make sure we can't set entropy below a minimum threshold */
+TEST_F(AslrMmapTest, match_min) {
+ if (user32) {
+ // running 32-bit userspace on 64-bit kernel, only compat used.
+ return;
+ } else {
+ EXPECT_FALSE(set_mmap_rnd_bits(min - 1, false));
+ EXPECT_TRUE(set_mmap_rnd_bits(min, false));
+ EXPECT_EQ(min, get_mmap_rnd_bits(false));
+ }
+}
+
+/* make sure we can't set compat entropy below a minimum threshold */
+TEST_F(AslrMmapTest, match_compat_min) {
+ if (compat || user32) {
+ EXPECT_FALSE(set_mmap_rnd_bits(min_cmpt - 1, true));
+ EXPECT_TRUE(set_mmap_rnd_bits(min_cmpt, true));
+ EXPECT_EQ(min_cmpt, get_mmap_rnd_bits(true));
+ }
+}
+
+/* make sure we can't set entropy above a maximum threshold */
+TEST_F(AslrMmapTest, match_max) {
+ if (user32) {
+ // running 32-bit userspace on 64-bit kernel, only compat used.
+ return;
+ } else {
+ EXPECT_FALSE(set_mmap_rnd_bits(max + 1, false));
+ EXPECT_TRUE(set_mmap_rnd_bits(max, false));
+ EXPECT_EQ(max, get_mmap_rnd_bits(false));
+ }
+}
+
+/* make sure we can't set compat entropy above a maximum threshold */
+TEST_F(AslrMmapTest, match_compat_max) {
+ if (compat || user32) {
+ EXPECT_FALSE(set_mmap_rnd_bits(max_cmpt + 1, true));
+ EXPECT_TRUE(set_mmap_rnd_bits(max_cmpt, true));
+ EXPECT_EQ(max_cmpt, get_mmap_rnd_bits(true));
+ }
+}
+
+/* make sure observed entropy is what we expect when we set min value */
+TEST_F(AslrMmapTest, entropy_min) {
+ if (user32) {
+ // running 32-bit userspace on 64-bit kernel, only compat used.
+ return;
+ } else {
+ EXPECT_TRUE(set_mmap_rnd_bits(min, false));
+ EXPECT_EQ(min, calc_mmap_entropy(path, lib, 16));
+ }
+}
+
+/* make sure observed compat entropy is what we expect when we set min value */
+TEST_F(AslrMmapTest, entropy_cmpt_min) {
+ if (compat || user32) {
+ EXPECT_TRUE(set_mmap_rnd_bits(min_cmpt, true));
+ EXPECT_EQ(min_cmpt, calc_mmap_entropy(SCRAPE_PATH_32, SCRAPE_LIB_32, 16));
+ }
+}
+
+/* make sure observed entropy is what we expect when we set max value */
+TEST_F(AslrMmapTest, entropy_max) {
+ if (user32) {
+ // running 32-bit userspace on 64-bit kernel, only compat used.
+ return;
+ } else {
+ EXPECT_TRUE(set_mmap_rnd_bits(max, false));
+ EXPECT_EQ(max, calc_mmap_entropy(path, lib, 16));
+ }
+}
+
+/* make sure observed compat entropy is what we expect when we set max value */
+TEST_F(AslrMmapTest, entropy_cmpt_max) {
+ if (compat || user32) {
+ EXPECT_TRUE(set_mmap_rnd_bits(max_cmpt, true));
+ EXPECT_EQ(max_cmpt, calc_mmap_entropy(SCRAPE_PATH_32, SCRAPE_LIB_32, 16));
+ }
+}
+
+/* make sure observed entropy is what we expect for default value */
+TEST_F(AslrMmapTest, entropy_def) {
+ if (user32) {
+ // running 32-bit userspace on 64-bit kernel, only compat used.
+ return;
+ } else {
+ EXPECT_EQ(def, calc_mmap_entropy(path, lib, 16));
+ }
+}
+
+/* make sure observed entropy is what we expect for default compat value */
+TEST_F(AslrMmapTest, entropy_cmpt_def) {
+ if (compat || user32) {
+ EXPECT_EQ(def_cmpt, calc_mmap_entropy(SCRAPE_PATH_32, SCRAPE_LIB_32, 16));
+ }
+}
+
+#endif /* supported arch */
diff --git a/tests/kernel.config/aslr_test.cpp b/tests/kernel.config/aslr_test.cpp
new file mode 100644
index 00000000..cda121c6
--- /dev/null
+++ b/tests/kernel.config/aslr_test.cpp
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "aslr_test.h"
+
+unsigned int get_mmap_rnd_bits(bool compat) {
+ std::string path;
+
+ if (compat)
+ path = PROCFS_COMPAT_PATH;
+ else
+ path = PROCFS_PATH;
+
+ std::ifstream bi_file(path);
+ if (!bi_file)
+ return false;
+ std::string str_rec;
+ bi_file >> str_rec;
+
+ return stoi(str_rec);
+}
+
+bool set_mmap_rnd_bits(unsigned int new_val, bool compat) {
+ std::string path;
+
+ if (compat)
+ path = "/proc/sys/vm/mmap_rnd_compat_bits";
+ else
+ path = "/proc/sys/vm/mmap_rnd_bits";
+
+ std::ofstream bo_file(path, std::ios::out);
+ if (!bo_file)
+ return false;
+
+ std::string str_val = std::to_string(new_val);
+ bo_file << str_val << std::flush;
+ bo_file.close();
+
+ // check to make sure it was recorded
+ std::ifstream bi_file(path);
+ if (!bi_file)
+ return false;
+ std::string str_rec;
+ bi_file >> str_rec;
+ bi_file.close();
+ if (str_val.compare(str_rec) != 0)
+ return false;
+ return true;
+}
+
+std::string scrape_addr(const char *exec_name, const char *lib_match) {
+ pid_t pid;
+ int fd[2];
+ char buff[MAX_ADDR_LEN];
+ int len, status;
+ if(pipe(fd)) {
+ std::cerr << "Error creating pipe:" << strerror(errno) << "\n";
+ return std::string();
+ }
+
+ if ((pid = fork()) < 0) {
+ std::cerr << "Error creating new process: " << strerror(errno) << "\n";
+ close(fd[0]);
+ close(fd[1]);
+ return std::string();
+ } else if (pid > 0) {
+ // parent
+ close(fd[1]);
+ wait(&status);
+ if (status == -1) {
+ std::cerr << "Unable to find starting address of mmapp'd libc. Aborting.\n";
+ close(fd[0]);
+ return std::string();
+ }
+ len = read(fd[0], buff, MAX_ADDR_LEN - 1);
+ if (len < 0) {
+ std::cerr << "Error reading pipe from child: " << strerror(errno) << "\n";
+ close(fd[0]);
+ return std::string();
+ }
+ buff[len] = '\0';
+ close(fd[0]);
+ } else {
+ // child, dup 'n' exec
+ close(fd[0]);
+ if(dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO) {
+ std::cerr << "Error dup'n pipe to STDOUT of child: " << strerror(errno) << "\n";
+ close(fd[1]);
+ return std::string();
+ }
+ if(execlp(exec_name, exec_name, lib_match, (char *) NULL)) {
+ std::cerr << "Error exec'ing mmap_scraper: " << strerror(errno) << "\n";
+ close(fd[1]);
+ return std::string();
+ }
+ }
+ return std::string(buff, strlen(buff));
+}
+
+unsigned int calc_mmap_entropy(const char *exec_name, const char *lib_match, size_t samp_sz) {
+ uint64_t min_addr = 0, max_addr = 0;
+
+ std::unordered_set<uint64_t> addrs = { };
+
+ // get our first value
+ uint64_t addr = min_addr = max_addr = std::stoll(scrape_addr(exec_name, lib_match), 0, 16);
+ addrs.insert(addr);
+ for (unsigned int i = 0; i < samp_sz - 1; ++i) {
+ std::string addr_str = scrape_addr(exec_name, lib_match);
+ if (addr_str.empty())
+ return 0;
+ addr = std::stoll(addr_str, 0, 16);
+ if (addr < min_addr)
+ min_addr = addr;
+ if (addr >= max_addr)
+ max_addr = addr;
+ addrs.insert(addr);
+ }
+ if (addrs.size() < (samp_sz >> 1)) {
+ std::cerr << "> 50% collisions in mmap addresses, entropy appears to be rigged!";
+ return 0;
+ }
+ unsigned int e_bits = (int) (std::ceil(std::log2(max_addr - min_addr)) - std::log2(getpagesize()));
+ return e_bits;
+}
+
+const char *AslrMmapTest::path;
+const char *AslrMmapTest::lib;
+unsigned int AslrMmapTest::def, AslrMmapTest::min, AslrMmapTest::max;
+bool AslrMmapTest::compat = false, AslrMmapTest::user32 = false;
+unsigned int AslrMmapTest::def_cmpt, AslrMmapTest::min_cmpt, AslrMmapTest::max_cmpt;
+
+void AslrMmapTest::SetUpTestCase() {
+ /* set up per-arch values */
+#if defined(__x86__64__)
+ def = 32;
+ min = 28;
+ max = 32;
+ path = SCRAPE_PATH_64;
+ lib = SCRAPE_LIB_64;
+
+ compat = true;
+ def_cmpt = 16;
+ min_cmpt = 8;
+ max_cmpt = 16;
+
+#elif defined(__i386__)
+ def = 16;
+ min = 8;
+ max = 16;
+ path = SCRAPE_PATH_32;
+ lib = SCRAPE_LIB_32;
+
+ if (!access(PROCFS_COMPAT_PATH, F_OK)) {
+ // running 32 bit userspace over 64-bit kernel
+ user32 = true;
+ def_cmpt = 16;
+ min_cmpt = 8;
+ max_cmpt = 16;
+ }
+
+#elif defined(__aarch64__)
+ unsigned int pgbits = std::log2(getpagesize());
+ def = 24;
+ min = 18 - (pgbits - 12);
+ max = 24;
+ path = SCRAPE_PATH_64;
+ lib = SCRAPE_LIB_64;
+
+ compat = true;
+ def_cmpt = 16;
+ min_cmpt = 11 - (pgbits - 12);
+ max_cmpt = 16;
+
+#elif defined(__arm__)
+ unsigned int pgbits = std::log2(getpagesize());
+ def = 8;
+ min = 8;
+ max = 16;
+ path = SCRAPE_PATH_32;
+ lib = SCRAPE_LIB_32;
+
+ if (!access(PROCFS_COMPAT_PATH, F_OK)) {
+ // running 32 bit userspace over 64-bit kernel
+ user32 = true;
+ def_cmpt = 16;
+ min_cmpt = 11 - (pgbits - 12);;
+ max_cmpt = 16;
+ }
+#endif
+}
+
+void AslrMmapTest::TearDown() {
+ if (!user32)
+ set_mmap_rnd_bits(def, false);
+ if (user32 || compat)
+ set_mmap_rnd_bits(def_cmpt, true);
+}
+
+/* run tests only if on supported arch */
+#if defined(__x86__64__) || defined(__i386__) || defined(__aarch64__) || defined(__arm__)
+
+TEST_F(AslrMmapTest, entropy_min_def) {
+ if (user32) {
+ // running 32-bit userspace on 64-bit kernel, only compat used.
+ return;
+ } else {
+ EXPECT_GE(def, calc_mmap_entropy(path, lib, 16));
+ }
+}
+
+TEST_F(AslrMmapTest, entropy_min_cmpt_def) {
+ if (compat || user32) {
+ EXPECT_GE(def_cmpt, calc_mmap_entropy(SCRAPE_PATH_32, SCRAPE_LIB_32, 16));
+ }
+}
+
+#endif /* supported arch */
diff --git a/tests/kernel.config/aslr_test.h b/tests/kernel.config/aslr_test.h
new file mode 100644
index 00000000..355a62a5
--- /dev/null
+++ b/tests/kernel.config/aslr_test.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef ASLR_TEST_H
+#define ASLR_TEST_H
+
+#include <cmath>
+#include <errno.h>
+#include <fstream>
+#include <iostream>
+#include <stdint.h>
+#include <string>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <unordered_set>
+
+#include <gtest/gtest.h>
+
+#define MAX_ADDR_LEN 256
+
+#define PROCFS_PATH "/proc/sys/vm/mmap_rnd_bits"
+#define PROCFS_COMPAT_PATH "/proc/sys/vm/mmap_rnd_compat_bits"
+
+#define SCRAPE_PATH_64 "/data/nativetest64/scrape_mmap_addr/scrape_mmap_addr"
+#define SCRAPE_PATH_32 "/data/nativetest/scrape_mmap_addr/scrape_mmap_addr"
+#define SCRAPE_LIB_64 "/system/bin/linker64"
+#define SCRAPE_LIB_32 "/system/bin/linker"
+
+class AslrMmapTest : public ::testing::Test {
+ protected:
+ static void SetUpTestCase();
+ static const char *path;
+ static const char *lib;
+ static unsigned int def, min, max;
+ static bool compat, user32;
+ static unsigned int def_cmpt, min_cmpt, max_cmpt;
+
+ void TearDown();
+};
+
+/*
+ * gets the current mmap_rnd_bits value. requires root.
+ */
+unsigned int get_mmap_rnd_bits(bool compat);
+
+/*
+ * sets the corresponding mmap_rnd_bits variable, returns false if couldn't
+ * change. requires root.
+ */
+bool set_mmap_rnd_bits(unsigned int new_val, bool compat);
+
+/*
+ * scrape_addr - get the raw starting address from /proc/child_pid/mmaps
+ */
+std::string scrape_addr(const char *exec_name, const char *lib_match);
+
+/*
+ * forks off sample_size processes and records the starting address of the
+ * indicated library as reported by exec_name. Reports entropy observed among
+ * recorded samples.
+ */
+unsigned int calc_mmap_entropy(const char *exec_name, const char *lib_match, size_t samp_sz);
+
+#endif //ASLR_TEST_H
diff --git a/tests/kernel.config/scrape_mmap_addr.cpp b/tests/kernel.config/scrape_mmap_addr.cpp
new file mode 100644
index 00000000..be5995f1
--- /dev/null
+++ b/tests/kernel.config/scrape_mmap_addr.cpp
@@ -0,0 +1,32 @@
+#include <errno.h>
+#include <fstream>
+#include <iostream>
+#include <regex>
+#include <string>
+#include <string.h>
+
+int main(int argc, char * argv[]) {
+ if (argc != 2) {
+ std::cerr << "usage: " << argv[0] << ": libname\n";
+ return -1;
+ }
+ std::regex reg(std::string("^([a-f0-9]+)\\-[0-9a-f]+\\s+.+\\s+(\\d+)\\s+.+\\s+\\d+\\s+") + std::string(argv[1]) + std::string("\\s*$"));
+
+ /* open /proc/self/maps */
+ std::string ln;
+ std::ifstream m_file("/proc/self/maps");
+ if (!m_file) {
+ std::cerr << "Unable to open /proc/self/maps " << strerror(errno) << "\n";
+ return -1;
+ }
+ while (getline(m_file, ln)) {
+ std::smatch sm;
+ if (std::regex_match (ln,sm, reg)) {
+ if (std::stoi(sm[2]) == 0) {
+ std::cout << sm[1];
+ return 0;
+ }
+ }
+ }
+ return -1;
+}