[sanitizer] No THREADLOCAL in qsort and bsearch

qsort can reuse qsort_r if available.
bsearch always passes key as the first comparator argument, so we
can use it to wrap the original comparator.

Differential Revision: https://reviews.llvm.org/D108751
This commit is contained in:
Vitaly Buka 2021-08-26 10:25:09 -07:00
parent 04da89e652
commit f1bb30a495
1 changed files with 32 additions and 37 deletions

View File

@ -9951,13 +9951,17 @@ INTERCEPTOR(int, getentropy, void *buf, SIZE_T buflen) {
#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,
@ -9973,26 +9977,8 @@ INTERCEPTOR(void, qsort_r, void *base, SIZE_T nmemb, SIZE_T size,
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, &params);
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, base, nmemb * size);
}
# define INIT_QSORT_R COMMON_INTERCEPT_FUNCTION(qsort_r)
@ -10000,7 +9986,15 @@ INTERCEPTOR(void, qsort_r, void *base, SIZE_T nmemb, SIZE_T size,
# define INIT_QSORT_R
#endif
#if SANITIZER_INTERCEPT_QSORT
#if SANITIZER_INTERCEPT_QSORT && SANITIZER_INTERCEPT_QSORT_R
INTERCEPTOR(void, qsort, void *base, SIZE_T nmemb, SIZE_T size,
qsort_r_compar_f compar) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, qsort, base, nmemb, size, compar);
WRAP(qsort_r)(base, nmemb, size, compar, nullptr);
}
# define INIT_QSORT COMMON_INTERCEPT_FUNCTION(qsort)
#elif SANITIZER_INTERCEPT_QSORT && !SANITIZER_INTERCEPT_QSORT_R
// Glibc qsort uses a temporary buffer allocated either on stack or on heap.
// Poisoned memory from there may get copied into the comparator arguments,
// where it needs to be dealt with. But even that is not enough - the results of
@ -10057,29 +10051,30 @@ INTERCEPTOR(void, qsort, void *base, SIZE_T nmemb, SIZE_T size,
}
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, base, nmemb * size);
}
#define INIT_QSORT COMMON_INTERCEPT_FUNCTION(qsort)
# define INIT_QSORT COMMON_INTERCEPT_FUNCTION(qsort)
#else
#define INIT_QSORT
# define INIT_QSORT
#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)(&params, base, nmemb, size, wrapped_bsearch_compar);
}
# define INIT_BSEARCH COMMON_INTERCEPT_FUNCTION(bsearch)
#else