[compiler-rt] [sanitizer] Intercept scandirat/scandirat64 (PR #163924)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Oct 17 01:17:02 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-compiler-rt-sanitizer
Author: None (alxchk)
<details>
<summary>Changes</summary>
Fixes https://github.com/llvm/llvm-project/issues/163923
---
Full diff: https://github.com/llvm/llvm-project/pull/163924.diff
6 Files Affected:
- (modified) compiler-rt/lib/hwasan/hwasan_platform_interceptors.h (+6)
- (modified) compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc (+110)
- (modified) compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h (+2)
- (added) compiler-rt/test/msan/scandirat.cpp (+56)
- (added) compiler-rt/test/msan/scandirat_null.cpp (+34)
- (added) compiler-rt/test/sanitizer_common/TestCases/scandirat.c (+28)
``````````diff
diff --git a/compiler-rt/lib/hwasan/hwasan_platform_interceptors.h b/compiler-rt/lib/hwasan/hwasan_platform_interceptors.h
index 8a653d83dec65..71bbad2d38f3b 100644
--- a/compiler-rt/lib/hwasan/hwasan_platform_interceptors.h
+++ b/compiler-rt/lib/hwasan/hwasan_platform_interceptors.h
@@ -389,9 +389,15 @@
#undef SANITIZER_INTERCEPT_SCANDIR
#define SANITIZER_INTERCEPT_SCANDIR 0
+#undef SANITIZER_INTERCEPT_SCANDIRAT
+#define SANITIZER_INTERCEPT_SCANDIRAT 0
+
#undef SANITIZER_INTERCEPT_SCANDIR64
#define SANITIZER_INTERCEPT_SCANDIR64 0
+#undef SANITIZER_INTERCEPT_SCANDIRAT64
+#define SANITIZER_INTERCEPT_SCANDIRAT64 0
+
#undef SANITIZER_INTERCEPT_GETGROUPS
#define SANITIZER_INTERCEPT_GETGROUPS 0
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
index b10ce7fa44afc..0d1455e124f1b 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
@@ -106,6 +106,7 @@
#define readdir __readdir30
#define readdir_r __readdir_r30
#define scandir __scandir30
+#define scandirat __scandirat30
#define setitimer __setitimer50
#define setlocale __setlocale50
#define shmctl __shmctl50
@@ -4227,6 +4228,59 @@ INTERCEPTOR(int, scandir, char *dirp, __sanitizer_dirent ***namelist,
#define INIT_SCANDIR
#endif
+#if SANITIZER_INTERCEPT_SCANDIRAT
+typedef int (*scandirat_filter_f)(const struct __sanitizer_dirent *);
+typedef int (*scandirat_compar_f)(const struct __sanitizer_dirent **,
+ const struct __sanitizer_dirent **);
+
+static THREADLOCAL scandirat_filter_f scandirat_filter;
+static THREADLOCAL scandirat_compar_f scandirat_compar;
+
+static int wrapped_scandirat_filter(const struct __sanitizer_dirent *dir) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(dir, __sanitizer_dirsiz(dir));
+ return scandirat_filter(dir);
+}
+
+static int wrapped_scandirat_compar(const struct __sanitizer_dirent **a,
+ const struct __sanitizer_dirent **b) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(2);
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(a, sizeof(*a));
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(*a, __sanitizer_dirsiz(*a));
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(b, sizeof(*b));
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(*b, __sanitizer_dirsiz(*b));
+ return scandirat_compar(a, b);
+}
+
+INTERCEPTOR(int, scandirat, int dirfd, char *dirp, __sanitizer_dirent ***namelist,
+ scandirat_filter_f filter, scandirat_compar_f compar) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, scandirat, dirfd, dirp, namelist, filter, compar);
+ if (dirp) COMMON_INTERCEPTOR_READ_RANGE(ctx, dirp, internal_strlen(dirp) + 1);
+ scandirat_filter = filter;
+ scandirat_compar = compar;
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://github.com/google/sanitizers/issues/321.
+ int res = REAL(scandirat)(dirfd, dirp, namelist,
+ filter ? wrapped_scandirat_filter : nullptr,
+ compar ? wrapped_scandirat_compar : nullptr);
+ scandirat_filter = nullptr;
+ scandirat_compar = nullptr;
+ if (namelist && res > 0) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, namelist, sizeof(*namelist));
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *namelist, sizeof(**namelist) * res);
+ for (int i = 0; i < res; ++i)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (*namelist)[i],
+ __sanitizer_dirsiz((*namelist)[i]));
+ }
+ return res;
+}
+#define INIT_SCANDIRAT COMMON_INTERCEPT_FUNCTION(scandirat);
+#else
+#define INIT_SCANDIRAT
+#endif
+
#if SANITIZER_INTERCEPT_SCANDIR64
typedef int (*scandir64_filter_f)(const struct __sanitizer_dirent64 *);
typedef int (*scandir64_compar_f)(const struct __sanitizer_dirent64 **,
@@ -4281,6 +4335,60 @@ INTERCEPTOR(int, scandir64, char *dirp, __sanitizer_dirent64 ***namelist,
#define INIT_SCANDIR64
#endif
+#if SANITIZER_INTERCEPT_SCANDIRAT64
+typedef int (*scandirat64_filter_f)(const struct __sanitizer_dirent64 *);
+typedef int (*scandirat64_compar_f)(const struct __sanitizer_dirent64 **,
+ const struct __sanitizer_dirent64 **);
+
+static THREADLOCAL scandirat64_filter_f scandirat64_filter;
+static THREADLOCAL scandirat64_compar_f scandirat64_compar;
+
+static int wrapped_scandirat64_filter(const struct __sanitizer_dirent64 *dir) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(dir, __sanitizer_dirsiz(dir));
+ return scandirat64_filter(dir);
+}
+
+static int wrapped_scandirat64_compar(const struct __sanitizer_dirent64 **a,
+ const struct __sanitizer_dirent64 **b) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(2);
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(a, sizeof(*a));
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(*a, __sanitizer_dirsiz(*a));
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(b, sizeof(*b));
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(*b, __sanitizer_dirsiz(*b));
+ return scandirat64_compar(a, b);
+}
+
+INTERCEPTOR(int, scandirat64, int dirfd, char *dirp, __sanitizer_dirent64 ***namelist,
+ scandirat64_filter_f filter, scandirat64_compar_f compar) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, scandirat64, dirfd, dirp, namelist, filter, compar);
+ if (dirp) COMMON_INTERCEPTOR_READ_RANGE(ctx, dirp, internal_strlen(dirp) + 1);
+ scandirat64_filter = filter;
+ scandirat64_compar = compar;
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://github.com/google/sanitizers/issues/321.
+ int res =
+ REAL(scandirat64)(dirfd, dirp, namelist,
+ filter ? wrapped_scandirat64_filter : nullptr,
+ compar ? wrapped_scandirat64_compar : nullptr);
+ scandirat64_filter = nullptr;
+ scandirat64_compar = nullptr;
+ if (namelist && res > 0) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, namelist, sizeof(*namelist));
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *namelist, sizeof(**namelist) * res);
+ for (int i = 0; i < res; ++i)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (*namelist)[i],
+ __sanitizer_dirsiz((*namelist)[i]));
+ }
+ return res;
+}
+#define INIT_SCANDIRAT64 COMMON_INTERCEPT_FUNCTION(scandirat64);
+#else
+#define INIT_SCANDIRAT64
+#endif
+
#if SANITIZER_INTERCEPT_GETGROUPS
INTERCEPTOR(int, getgroups, int size, u32 *lst) {
void *ctx;
@@ -10526,7 +10634,9 @@ static void InitializeCommonInterceptors() {
INIT_STRERROR_R;
INIT_XPG_STRERROR_R;
INIT_SCANDIR;
+ INIT_SCANDIRAT;
INIT_SCANDIR64;
+ INIT_SCANDIRAT64;
INIT_GETGROUPS;
INIT_POLL;
INIT_PPOLL;
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h b/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h
index 88ecd7e16306a..dfe518609c673 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h
@@ -356,7 +356,9 @@ SANITIZER_WEAK_IMPORT void *aligned_alloc(__sanitizer::usize __alignment,
#define SANITIZER_INTERCEPT_XPG_STRERROR_R SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_SCANDIR \
(SI_FREEBSD || SI_NETBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
+#define SANITIZER_INTERCEPT_SCANDIRAT SI_GLIBC
#define SANITIZER_INTERCEPT_SCANDIR64 SI_GLIBC || SI_SOLARIS32
+#define SANITIZER_INTERCEPT_SCANDIRAT64 SI_GLIBC
#define SANITIZER_INTERCEPT_GETGROUPS SI_POSIX
#define SANITIZER_INTERCEPT_POLL SI_POSIX
#define SANITIZER_INTERCEPT_PPOLL SI_LINUX_NOT_ANDROID || SI_SOLARIS
diff --git a/compiler-rt/test/msan/scandirat.cpp b/compiler-rt/test/msan/scandirat.cpp
new file mode 100644
index 0000000000000..d538de1057b04
--- /dev/null
+++ b/compiler-rt/test/msan/scandirat.cpp
@@ -0,0 +1,56 @@
+// RUN: %clangxx_msan -O0 %s -o %t && %run %t %p
+// RUN: %clangxx_msan -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p
+// RUN: %clangxx_msan -O3 %s -o %t && %run %t %p
+
+#include <assert.h>
+#include <glob.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+
+#include <sanitizer/msan_interface.h>
+
+
+static int my_filter(const struct dirent *a) {
+ assert(__msan_test_shadow(&a, sizeof(a)) == (size_t)-1);
+ printf("%s\n", a->d_name);
+ __msan_print_shadow(a, a->d_reclen);
+ assert(__msan_test_shadow(a, a->d_reclen) == (size_t)-1);
+ printf("%s\n", a->d_name);
+ return strlen(a->d_name) == 3 && a->d_name[2] == 'b';
+}
+
+static int my_compar(const struct dirent **a, const struct dirent **b) {
+ assert(__msan_test_shadow(a, sizeof(*a)) == (size_t)-1);
+ assert(__msan_test_shadow(*a, (*a)->d_reclen) == (size_t)-1);
+ assert(__msan_test_shadow(b, sizeof(*b)) == (size_t)-1);
+ assert(__msan_test_shadow(*b, (*b)->d_reclen) == (size_t)-1);
+ if ((*a)->d_name[1] == (*b)->d_name[1])
+ return 0;
+ return ((*a)->d_name[1] < (*b)->d_name[1]) ? 1 : -1;
+}
+
+int main(int argc, char *argv[]) {
+ assert(argc == 2);
+ char buf[1024];
+ snprintf(buf, sizeof(buf), "%s/%s", argv[1], "scandir_test_root/");
+
+ struct dirent **d;
+ int res = scandirat(AT_FDCWD, buf, &d, my_filter, my_compar);
+ assert(res == 2);
+ assert(__msan_test_shadow(&d, sizeof(*d)) == (size_t)-1);
+ for (int i = 0; i < res; ++i) {
+ assert(__msan_test_shadow(&d[i], sizeof(d[i])) == (size_t)-1);
+ assert(__msan_test_shadow(d[i], d[i]->d_reclen) == (size_t)-1);
+ }
+
+ assert(strcmp(d[0]->d_name, "bbb") == 0);
+ assert(strcmp(d[1]->d_name, "aab") == 0);
+ return 0;
+}
diff --git a/compiler-rt/test/msan/scandirat_null.cpp b/compiler-rt/test/msan/scandirat_null.cpp
new file mode 100644
index 0000000000000..ced2041417347
--- /dev/null
+++ b/compiler-rt/test/msan/scandirat_null.cpp
@@ -0,0 +1,34 @@
+// RUN: %clangxx_msan -O0 %s -o %t && %run %t %p
+// RUN: %clangxx_msan -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p
+// RUN: %clangxx_msan -O3 %s -o %t && %run %t %p
+
+#include <assert.h>
+#include <glob.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+
+#include <sanitizer/msan_interface.h>
+
+
+int main(int argc, char *argv[]) {
+ assert(argc == 2);
+ char buf[1024];
+ snprintf(buf, sizeof(buf), "%s/%s", argv[1], "scandir_test_root/");
+
+ struct dirent **d;
+ int res = scandirat(AT_FDCWD, buf, &d, NULL, NULL);
+ assert(res >= 3);
+ assert(__msan_test_shadow(&d, sizeof(*d)) == (size_t)-1);
+ for (int i = 0; i < res; ++i) {
+ assert(__msan_test_shadow(&d[i], sizeof(d[i])) == (size_t)-1);
+ assert(__msan_test_shadow(d[i], d[i]->d_reclen) == (size_t)-1);
+ }
+ return 0;
+}
diff --git a/compiler-rt/test/sanitizer_common/TestCases/scandirat.c b/compiler-rt/test/sanitizer_common/TestCases/scandirat.c
new file mode 100644
index 0000000000000..6d889cfe212be
--- /dev/null
+++ b/compiler-rt/test/sanitizer_common/TestCases/scandirat.c
@@ -0,0 +1,28 @@
+// REQUIRES: (linux && !android) || freebsd
+
+// RUN: rm -rf %t-dir
+// RUN: mkdir -p %t-dir
+// RUN: touch %t-dir/a %t-dir/b %t-dir/c
+
+// RUN: %clang %s -DTEMP_DIR='"'"%t-dir"'"' -o %t && %run %t 2>&1
+
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int main(int argc, char **argv) {
+ struct dirent **dirpp = NULL;
+ int count = scandir(AT_FDCWD, TEMP_DIR, &dirpp, NULL, NULL);
+ fprintf(stderr, "count is %d\n", count);
+ if (count >= 0) {
+ for (int i = 0; i < count; ++i) {
+ fprintf(stderr, "found %s\n", dirpp[i]->d_name);
+ free(dirpp[i]);
+ }
+ free(dirpp);
+ }
+ return 0;
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/163924
More information about the llvm-commits
mailing list