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 @@ -10013,15 +10013,18 @@ #if SANITIZER_INTERCEPT_QSORT_R typedef int (*qsort_r_compar_f)(const void *, const void *, void *); -static THREADLOCAL qsort_r_compar_f qsort_r_compar; -static THREADLOCAL SIZE_T qsort_r_size; +struct qsort_r_compar_params { + SIZE_T size; + qsort_r_compar_f compar; + void *arg; +}; static int wrapped_qsort_r_compar(const void *a, const void *b, void *arg) { + qsort_r_compar_params *params = (qsort_r_compar_params *)arg; COMMON_INTERCEPTOR_UNPOISON_PARAM(3); - COMMON_INTERCEPTOR_INITIALIZE_RANGE(a, qsort_r_size); - COMMON_INTERCEPTOR_INITIALIZE_RANGE(b, qsort_r_size); - return qsort_r_compar(a, b, arg); + COMMON_INTERCEPTOR_INITIALIZE_RANGE(a, params->size); + COMMON_INTERCEPTOR_INITIALIZE_RANGE(b, params->size); + return params->compar(a, b, params->arg); } - INTERCEPTOR(void, qsort_r, void *base, SIZE_T nmemb, SIZE_T size, qsort_r_compar_f compar, void *arg) { void *ctx; @@ -10035,51 +10038,68 @@ compar(p, q, arg); } } - qsort_r_compar_f old_compar = qsort_r_compar; - SIZE_T old_size = qsort_r_size; - // Handle qsort_r() implementations that recurse using an - // interposable function call: - bool already_wrapped = compar == wrapped_qsort_r_compar; - if (already_wrapped) { - // This case should only happen if the qsort() implementation calls itself - // using a preemptible function call (e.g. the FreeBSD libc version). - // Check that the size and comparator arguments are as expected. - CHECK_NE(compar, qsort_r_compar); - CHECK_EQ(qsort_r_size, size); - } else { - qsort_r_compar = compar; - qsort_r_size = size; - } - REAL(qsort_r)(base, nmemb, size, wrapped_qsort_r_compar, arg); - if (!already_wrapped) { - qsort_r_compar = old_compar; - qsort_r_size = old_size; + qsort_r_compar_params params = {size, compar, arg}; + REAL(qsort_r)(base, nmemb, size, wrapped_qsort_r_compar, ¶ms); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, base, nmemb * size); +} + +// Re-use qsort_r if it's intercepted. +typedef int (*qsort_compar_f)(const void *, const void *); +struct qsort_compar_params { + SIZE_T size; + qsort_compar_f compar; +}; +static int wrapped_qsort_compar(const void *a, const void *b, void *arg) { + qsort_compar_params *params = (qsort_compar_params *)arg; + COMMON_INTERCEPTOR_UNPOISON_PARAM(2); + COMMON_INTERCEPTOR_INITIALIZE_RANGE(a, params->size); + COMMON_INTERCEPTOR_INITIALIZE_RANGE(b, params->size); + return params->compar(a, b); +} +INTERCEPTOR(void, qsort, void *base, SIZE_T nmemb, SIZE_T size, + qsort_compar_f compar) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, qsort, base, nmemb, size, compar); + // Run the comparator over all array elements to detect any memory issues. + if (nmemb > 1) { + for (SIZE_T i = 0; i < nmemb - 1; ++i) { + void *p = (void *)((char *)base + i * size); + void *q = (void *)((char *)base + (i + 1) * size); + COMMON_INTERCEPTOR_UNPOISON_PARAM(2); + compar(p, q); + } } + qsort_compar_params params = {size, compar}; + REAL(qsort_r)(base, nmemb, size, wrapped_qsort_compar, ¶ms); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, base, nmemb * size); } -#define INIT_QSORT_R COMMON_INTERCEPT_FUNCTION(qsort_r) + +# define INIT_QSORT_R \ + COMMON_INTERCEPT_FUNCTION(qsort_r); \ + COMMON_INTERCEPT_FUNCTION(qsort) #else #define INIT_QSORT_R #endif #if SANITIZER_INTERCEPT_BSEARCH typedef int (*bsearch_compar_f)(const void *, const void *); -static THREADLOCAL bsearch_compar_f bsearch_compar; -static int wrapped_bsearch_compar(const void *a, const void *b) { +struct bsearch_compar_params { + const void *key; + bsearch_compar_f compar; +}; + +static int wrapped_bsearch_compar(const void *key, const void *b) { + const bsearch_compar_params *params = (const bsearch_compar_params *)key; COMMON_INTERCEPTOR_UNPOISON_PARAM(2); - return bsearch_compar(a, b); + return params->compar(params->key, b); } INTERCEPTOR(void *, bsearch, const void *key, const void *base, SIZE_T nmemb, SIZE_T size, bsearch_compar_f compar) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, bsearch, key, base, nmemb, size, compar); - // Unlike qsort, don't expect recursive implementation of bsearch. - CHECK_NE(compar, wrapped_bsearch_compar); - Swap(bsearch_compar, compar); - void *r = REAL(bsearch)(key, base, nmemb, size, wrapped_bsearch_compar); - bsearch_compar = compar; - return r; + bsearch_compar_params params = {key, compar}; + return REAL(bsearch)(¶ms, base, nmemb, size, wrapped_bsearch_compar); } # define INIT_BSEARCH COMMON_INTERCEPT_FUNCTION(bsearch) #else diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h b/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h --- a/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h @@ -568,8 +568,9 @@ #define SANITIZER_INTERCEPT_ATEXIT SI_NETBSD #define SANITIZER_INTERCEPT_PTHREAD_ATFORK SI_NETBSD #define SANITIZER_INTERCEPT_GETENTROPY SI_FREEBSD -#define SANITIZER_INTERCEPT_QSORT \ - (SI_POSIX && !SI_IOSSIM && !SI_WATCHOS && !SI_TVOS && !SI_ANDROID) +#define SANITIZER_INTERCEPT_QSORT \ + (SI_POSIX && !SI_IOSSIM && !SI_WATCHOS && !SI_TVOS && !SI_ANDROID && \ + !SI_GLIBC) #define SANITIZER_INTERCEPT_QSORT_R SI_GLIBC #define SANITIZER_INTERCEPT_BSEARCH \ (SI_POSIX && !SI_IOSSIM && !SI_WATCHOS && !SI_TVOS && !SI_ANDROID)