diff options
author | Chandler Carruth <chandlerc@gmail.com> | 2013-07-10 09:50:29 +0000 |
---|---|---|
committer | Chandler Carruth <chandlerc@gmail.com> | 2013-07-10 09:50:29 +0000 |
commit | 28c1b294b84fdb261c264525c62931ec8cbe0b8f (patch) | |
tree | a33ac4a716f0b0397092f31703e5c63084970868 | |
parent | ed5fe90bb87d13e3de5ebbb50d65421e5eb223d8 (diff) | |
download | bcm5719-llvm-28c1b294b84fdb261c264525c62931ec8cbe0b8f.tar.gz bcm5719-llvm-28c1b294b84fdb261c264525c62931ec8cbe0b8f.zip |
Fix a bug in the readdir_r interceptor: when we reach the end of the
directory stream, the entry is not written to, instead *result is set to
NULL and the entry is not written to at all.
I'm still somewhat suspicious of the correct instrumention here --
I feel like it should be marking the written range as the pointer in
*result and the length (*result)->d_reclen in case the implementation
decides not to use the passed-in entry (if that's even allowed).
Finally, the definition of 'struct dirent' analog used in the
interceptor is wrong in 32-bit mode with _FILE_OFFSET_BITS=64 as it hard
codes the use of a pointer-sized offset.
I've added a somewhat goofy test for the bug I fixed via ASan --
suggestions on how to better test the interceptor logic itself welcome.
llvm-svn: 185998
-rw-r--r-- | compiler-rt/lib/asan/lit_tests/TestCases/Linux/interception_readdir_r_test.cc | 30 | ||||
-rw-r--r-- | compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc | 2 |
2 files changed, 31 insertions, 1 deletions
diff --git a/compiler-rt/lib/asan/lit_tests/TestCases/Linux/interception_readdir_r_test.cc b/compiler-rt/lib/asan/lit_tests/TestCases/Linux/interception_readdir_r_test.cc new file mode 100644 index 00000000000..8adc064bcd5 --- /dev/null +++ b/compiler-rt/lib/asan/lit_tests/TestCases/Linux/interception_readdir_r_test.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_asan -O0 %s -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <dirent.h> + + +int main() { + // Ensure the readdir_r interceptor doesn't erroneously mark the entire dirent + // as written when the end of the directory pointer is reached. + fputs("reading the " TEMP_DIR " directory...\n", stderr); + DIR *d = opendir(TEMP_DIR); + struct dirent entry, *result; + unsigned count = 0; + do { + // Stamp the entry struct to try to trick the interceptor. + entry.d_reclen = 9999; + if (readdir_r(d, &entry, &result) != 0) + abort(); + ++count; + } while (result != NULL); + fprintf(stderr, "read %d entries\n", count); + // CHECK: reading the {{.*}} directory... + // CHECK-NOT: stack-buffer-overflow + // CHECK: read {{.*}} entries +} diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc index 0a9405fdad6..67864c538da 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -1374,7 +1374,7 @@ INTERCEPTOR(int, readdir_r, void *dirp, __sanitizer_dirent *entry, if (!res) { if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); - if (entry) + if (entry && *result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, entry, entry->d_reclen); } return res; |