Skip to content

Commit

Permalink
Add unit tests for invalid string accesses
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
danw committed Jun 30, 2016
1 parent dbd6b57 commit cd29d6c
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
5 changes: 4 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
48 changes: 48 additions & 0 deletions strutil_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include "strutil.h"

#include <assert.h>
#include <sys/mman.h>
#include <unistd.h>

#include <string>
#include <vector>
Expand Down Expand Up @@ -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<StringPiece> 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() {
Expand All @@ -150,5 +196,7 @@ int main() {
TestNormalizePath();
TestEscapeShell();
TestFindEndOfLine();
TestWordScannerInvalidAccess();
TestFindEndOfLineInvalidAccess();
assert(!g_failed);
}

0 comments on commit cd29d6c

Please sign in to comment.