From cd29d6cc57301c7facb118077d1d059e81c3de5d Mon Sep 17 00:00:00 2001 From: Dan Willemsen Date: Wed, 29 Jun 2016 23:21:20 -0700 Subject: Add unit tests for invalid string accesses A string may be allocated at the end of a page, and the next page may not be readable, so reading beyond the null character may not be safe. We've hit this with real makefiles, but here's a directed test that can reproduce it in other environments. This also updates the travis config to run the unit tests. --- .gitignore | 2 ++ .travis.yml | 5 ++++- strutil_test.cc | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b907c3d..19d2723 100644 --- a/.gitignore +++ b/.gitignore @@ -11,8 +11,10 @@ repo/maloader/ testcase_parse_benchmark_test.go bench-old.out bench-new.out +find_test ninja_test string_piece_test +strutil_bench strutil_test go_src_stamp version.cc diff --git a/.travis.yml b/.travis.yml index f045dd3..2fd6fa8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,10 @@ before_script: - sudo apt-get install -y libstdc++-4.8-dev clang-3.5 ninja-build realpath script: - - make -j4 ckati + - make -j4 ckati ckati_tests - ruby runtest.rb -c - ruby runtest.rb -c -n - ruby runtest.rb -c -n -a + - ./ninja_test + - ./string_piece_test + - ./strutil_test diff --git a/strutil_test.cc b/strutil_test.cc index a89786f..d7290d1 100644 --- a/strutil_test.cc +++ b/strutil_test.cc @@ -17,6 +17,8 @@ #include "strutil.h" #include +#include +#include #include #include @@ -138,6 +140,50 @@ void TestFindEndOfLine() { ASSERT_EQ(FindEndOfLine(StringPiece(buf, 2), 0, &lf_cnt), 2); } +// Take a string, and copy it into an allocated buffer where +// the byte immediately after the null termination character +// is read protected. Useful for testing, but doesn't support +// freeing the allocated pages. +const char* CreateProtectedString(const char* str) { + int pagesize = sysconf(_SC_PAGE_SIZE); + void *buffer; + char *buffer_str; + + // Allocate two pages of memory + if (posix_memalign(&buffer, pagesize, pagesize * 2) != 0) { + perror("posix_memalign failed"); + assert(false); + } + + // Make the second page unreadable + buffer_str = (char*)buffer + pagesize; + if (mprotect(buffer_str, pagesize, PROT_NONE) != 0) { + perror("mprotect failed"); + assert(false); + } + + // Then move the test string into the very end of the first page + buffer_str -= strlen(str) + 1; + strcpy(buffer_str, str); + + return buffer_str; +} + +void TestWordScannerInvalidAccess() { + vector ss; + for (StringPiece tok : WordScanner(CreateProtectedString("0123 456789"))) { + ss.push_back(tok); + } + assert(ss.size() == 2LU); + ASSERT_EQ(ss[0], "0123"); + ASSERT_EQ(ss[1], "56789"); +} + +void TestFindEndOfLineInvalidAccess() { + size_t lf_cnt = 0; + ASSERT_EQ(FindEndOfLine(CreateProtectedString("a\\"), 0, &lf_cnt), 2); +} + } // namespace int main() { @@ -150,5 +196,7 @@ int main() { TestNormalizePath(); TestEscapeShell(); TestFindEndOfLine(); + TestWordScannerInvalidAccess(); + TestFindEndOfLineInvalidAccess(); assert(!g_failed); } -- cgit v1.2.3