diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc --- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -3416,7 +3416,8 @@ // its metadata. See // https://github.com/google/sanitizers/issues/321. __sanitizer_dirent *res = REAL(readdir)(dirp); - if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, res->d_reclen); + if (res) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, SANITIZER_DIRSIZ(res)); return res; } @@ -3431,7 +3432,7 @@ if (!res) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); if (*result) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *result, (*result)->d_reclen); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *result, SANITIZER_DIRSIZ(*result)); } return res; } @@ -3452,7 +3453,8 @@ // its metadata. See // https://github.com/google/sanitizers/issues/321. __sanitizer_dirent64 *res = REAL(readdir64)(dirp); - if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, res->d_reclen); + if (res) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, SANITIZER_DIRSIZ(res)); return res; } @@ -3467,7 +3469,7 @@ if (!res) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); if (*result) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *result, (*result)->d_reclen); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *result, SANITIZER_DIRSIZ(*result)); } return res; } @@ -4039,7 +4041,7 @@ static int wrapped_scandir_filter(const struct __sanitizer_dirent *dir) { COMMON_INTERCEPTOR_UNPOISON_PARAM(1); - COMMON_INTERCEPTOR_INITIALIZE_RANGE(dir, dir->d_reclen); + COMMON_INTERCEPTOR_INITIALIZE_RANGE(dir, SANITIZER_DIRSIZ(dir)); return scandir_filter(dir); } @@ -4047,9 +4049,9 @@ const struct __sanitizer_dirent **b) { COMMON_INTERCEPTOR_UNPOISON_PARAM(2); COMMON_INTERCEPTOR_INITIALIZE_RANGE(a, sizeof(*a)); - COMMON_INTERCEPTOR_INITIALIZE_RANGE(*a, (*a)->d_reclen); + COMMON_INTERCEPTOR_INITIALIZE_RANGE(*a, SANITIZER_DIRSIZ(*a)); COMMON_INTERCEPTOR_INITIALIZE_RANGE(b, sizeof(*b)); - COMMON_INTERCEPTOR_INITIALIZE_RANGE(*b, (*b)->d_reclen); + COMMON_INTERCEPTOR_INITIALIZE_RANGE(*b, SANITIZER_DIRSIZ(*b)); return scandir_compar(a, b); } @@ -4073,7 +4075,7 @@ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *namelist, sizeof(**namelist) * res); for (int i = 0; i < res; ++i) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (*namelist)[i], - (*namelist)[i]->d_reclen); + SANITIZER_DIRSIZ((*namelist)[i])); } return res; } @@ -4092,7 +4094,7 @@ static int wrapped_scandir64_filter(const struct __sanitizer_dirent64 *dir) { COMMON_INTERCEPTOR_UNPOISON_PARAM(1); - COMMON_INTERCEPTOR_INITIALIZE_RANGE(dir, dir->d_reclen); + COMMON_INTERCEPTOR_INITIALIZE_RANGE(dir, SANITIZER_DIRSIZ(dir)); return scandir64_filter(dir); } @@ -4100,9 +4102,9 @@ const struct __sanitizer_dirent64 **b) { COMMON_INTERCEPTOR_UNPOISON_PARAM(2); COMMON_INTERCEPTOR_INITIALIZE_RANGE(a, sizeof(*a)); - COMMON_INTERCEPTOR_INITIALIZE_RANGE(*a, (*a)->d_reclen); + COMMON_INTERCEPTOR_INITIALIZE_RANGE(*a, SANITIZER_DIRSIZ(*a)); COMMON_INTERCEPTOR_INITIALIZE_RANGE(b, sizeof(*b)); - COMMON_INTERCEPTOR_INITIALIZE_RANGE(*b, (*b)->d_reclen); + COMMON_INTERCEPTOR_INITIALIZE_RANGE(*b, SANITIZER_DIRSIZ(*b)); return scandir64_compar(a, b); } @@ -4127,7 +4129,7 @@ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *namelist, sizeof(**namelist) * res); for (int i = 0; i < res; ++i) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (*namelist)[i], - (*namelist)[i]->d_reclen); + SANITIZER_DIRSIZ((*namelist)[i])); } return res; } diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_platform.h b/compiler-rt/lib/sanitizer_common/sanitizer_platform.h --- a/compiler-rt/lib/sanitizer_common/sanitizer_platform.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_platform.h @@ -445,4 +445,14 @@ # define SANITIZER_START_BACKGROUND_THREAD_IN_ASAN_INTERNAL 0 #endif +#if SANITIZER_FREEBSD +// For FreeBSD the actual size of a directory entry is not always in d_reclen +# define SANITIZER_DIRSIZ(dp) \ + ((__offsetof(struct __sanitizer_dirent, d_name) + ((dp)->d_namlen) + 1 + \ + 7) & \ + ~7) +#else +# define SANITIZER_DIRSIZ(dp) ((dp)->d_reclen) +#endif + #endif // SANITIZER_PLATFORM_H diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_freebsd.h b/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_freebsd.h --- a/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_freebsd.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_freebsd.h @@ -249,7 +249,11 @@ unsigned int d_fileno; # endif unsigned short d_reclen; - // more fields that we don't care about + u8 d_type; + u8 d_pad0; + unsigned short d_namlen; + unsigned short d_pad1; + char d_name[256]; }; // 'clock_t' is 32 bits wide on x64 FreeBSD diff --git a/compiler-rt/test/asan/TestCases/scandir.c b/compiler-rt/test/asan/TestCases/scandir.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/scandir.c @@ -0,0 +1,27 @@ +// 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_asan %s -DTEMP_DIR='"'"%t-dir"'"' -o %t && %run %t 2>&1 | FileCheck %s + +#include +#include +#include +#include + +int main(int argc, char **argv) { + struct dirent **dirpp = NULL; + int count = scandir(TEMP_DIR, &dirpp, NULL, NULL); + // CHECK-NOT: heap-buffer-overflow + 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; +}