summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChandler Carruth <chandlerc@gmail.com>2013-07-10 09:50:29 +0000
committerChandler Carruth <chandlerc@gmail.com>2013-07-10 09:50:29 +0000
commit28c1b294b84fdb261c264525c62931ec8cbe0b8f (patch)
treea33ac4a716f0b0397092f31703e5c63084970868
parented5fe90bb87d13e3de5ebbb50d65421e5eb223d8 (diff)
downloadbcm5719-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.cc30
-rw-r--r--compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc2
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;
OpenPOWER on IntegriCloud