Index: lib/sanitizer_common/sanitizer_common_interceptors.inc =================================================================== --- lib/sanitizer_common/sanitizer_common_interceptors.inc +++ lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -77,6 +77,11 @@ #define ctime __ctime50 #define ctime_r __ctime_r50 #define devname __devname50 +#define fts_children __fts_children60 +#define fts_close __fts_close60 +#define fts_open __fts_open60 +#define fts_read __fts_read60 +#define fts_set __fts_set60 #define getitimer __getitimer50 #define getpwent __getpwent50 #define getpwnam __getpwnam50 @@ -7252,6 +7257,74 @@ #define INIT_NETENT #endif +#if SANITIZER_INTERCEPT_FTS +INTERCEPTOR(void *, fts_open, char *const *path_argv, int options, + int (*compar)(void **, void **)) { + void *ctx; + void *fts; + COMMON_INTERCEPTOR_ENTER(ctx, fts_open, path_argv, options, compar); + if (path_argv) { + char *const *pa = path_argv; + while (*pa) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, pa, sizeof(char **)); + COMMON_INTERCEPTOR_READ_RANGE(ctx, *pa, REAL(strlen)(*pa) + 1); + pa++; + } + COMMON_INTERCEPTOR_READ_RANGE(ctx, pa, sizeof(char **)); + } + // TODO: handle compar callback + fts = REAL(fts_open)(path_argv, options, compar); + if (fts) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, fts, struct_FTS_sz); + return fts; +} + +INTERCEPTOR(void *, fts_read, void *ftsp) { + void *ctx; + void *ftsent; + COMMON_INTERCEPTOR_ENTER(ctx, fts_read, ftsp); + COMMON_INTERCEPTOR_READ_RANGE(ctx, ftsp, struct_FTS_sz); + ftsent = REAL(fts_read)(ftsp); + if (ftsent) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ftsent, struct_FTSENT_sz); + return ftsent; +} + +INTERCEPTOR(void *, fts_children, void *ftsp, int options) { + void *ctx; + void *ftsent; + COMMON_INTERCEPTOR_ENTER(ctx, fts_children, ftsp, options); + COMMON_INTERCEPTOR_READ_RANGE(ctx, ftsp, struct_FTS_sz); + ftsent = REAL(fts_children)(ftsp, options); + if (ftsent) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ftsent, struct_FTSENT_sz); + return ftsent; +} + +INTERCEPTOR(int, fts_set, void *ftsp, void *f, int options) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, fts_set, ftsp, f, options); + COMMON_INTERCEPTOR_READ_RANGE(ctx, ftsp, struct_FTS_sz); + COMMON_INTERCEPTOR_READ_RANGE(ctx, f, struct_FTSENT_sz); + return REAL(fts_set)(ftsp, f, options); +} + +INTERCEPTOR(int, fts_close, void *ftsp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, fts_close, ftsp); + COMMON_INTERCEPTOR_READ_RANGE(ctx, ftsp, struct_FTS_sz); + return REAL(fts_close)(ftsp); +} +#define INIT_FTS \ + COMMON_INTERCEPT_FUNCTION(fts_open); \ + COMMON_INTERCEPT_FUNCTION(fts_read); \ + COMMON_INTERCEPT_FUNCTION(fts_children); \ + COMMON_INTERCEPT_FUNCTION(fts_set); \ + COMMON_INTERCEPT_FUNCTION(fts_close); +#else +#define INIT_FTS +#endif + static void InitializeCommonInterceptors() { static u64 metadata_mem[sizeof(MetadataHashMap) / sizeof(u64) + 1]; interceptor_metadata_map = new((void *)&metadata_mem) MetadataHashMap(); @@ -7503,6 +7576,7 @@ INIT_TTYENT; INIT_PROTOENT; INIT_NETENT; + INIT_FTS; INIT___PRINTF_CHK; } Index: lib/sanitizer_common/sanitizer_platform_interceptors.h =================================================================== --- lib/sanitizer_common/sanitizer_platform_interceptors.h +++ lib/sanitizer_common/sanitizer_platform_interceptors.h @@ -514,5 +514,6 @@ #define SANITIZER_INTERCEPT_TTYENT SI_NETBSD #define SANITIZER_INTERCEPT_PROTOENT SI_NETBSD #define SANITIZER_INTERCEPT_NETENT SI_NETBSD +#define SANITIZER_INTERCEPT_FTS SI_NETBSD #endif // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H Index: lib/sanitizer_common/sanitizer_platform_limits_netbsd.h =================================================================== --- lib/sanitizer_common/sanitizer_platform_limits_netbsd.h +++ lib/sanitizer_common/sanitizer_platform_limits_netbsd.h @@ -60,6 +60,8 @@ extern unsigned struct_sembuf_sz; extern unsigned struct_kevent_sz; +extern unsigned struct_FTS_sz; +extern unsigned struct_FTSENT_sz; union __sanitizer_sigval { int sival_int; Index: lib/sanitizer_common/sanitizer_platform_limits_netbsd.cc =================================================================== --- lib/sanitizer_common/sanitizer_platform_limits_netbsd.cc +++ lib/sanitizer_common/sanitizer_platform_limits_netbsd.cc @@ -253,6 +253,8 @@ unsigned struct_timespec_sz = sizeof(struct timespec); unsigned struct_sembuf_sz = sizeof(struct sembuf); unsigned struct_kevent_sz = sizeof(struct kevent); +unsigned struct_FTS_sz = sizeof(FTS); +unsigned struct_FTSENT_sz = sizeof(FTSENT); unsigned struct_utimbuf_sz = sizeof(struct utimbuf); unsigned struct_itimerspec_sz = sizeof(struct itimerspec); unsigned struct_timex_sz = sizeof(struct timex); Index: test/sanitizer_common/TestCases/NetBSD/fts.cc =================================================================== --- /dev/null +++ test/sanitizer_common/TestCases/NetBSD/fts.cc @@ -0,0 +1,44 @@ +// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s + +#include +#include + +#include + +#include +#include +#include +#include + +int main() { + FTS *ftsp; + FTSENT *p, *chp; + size_t n = 0; + char *const paths[] = { (char *)"/etc", 0 }; + + ftsp = fts_open(paths, FTS_LOGICAL, NULL); + if (ftsp == NULL) + errx(EXIT_FAILURE, "fts_open"); + + chp = fts_children(ftsp, 0); + if (chp == NULL) + errx(EXIT_FAILURE, "fts_children: no files to traverse"); + + while ((p = fts_read(ftsp)) != NULL) { + /* Skip recursively subdirectories */ + if (p->fts_info == FTS_D && p->fts_level != FTS_ROOTLEVEL) /* pre-order */ + fts_set(ftsp, p, FTS_SKIP); + else if (p->fts_info == FTS_DP) /* post-order */ + continue; + else if (p->fts_info == FTS_F) /* regular file */ + n++; + } + + fts_close(ftsp); + + printf("Number of files in /etc: '%zu'\n", n); + + return EXIT_SUCCESS; + + // CHECK: Number of files in /etc: '{{.*}}' +}