@@ -9639,6 +9639,75 @@ INTERCEPTOR(int, getentropy, void *buf, SIZE_T buflen) {
9639
9639
#define INIT_GETENTROPY
9640
9640
#endif
9641
9641
9642
+ #if SANITIZER_INTERCEPT_QSORT
9643
+ // Glibc qsort uses a temporary buffer allocated either on stack or on heap.
9644
+ // Poisoned memory from there may get copied into the comparator arguments,
9645
+ // where it needs to be dealt with. But even that is not enough - the results of
9646
+ // the sort may be copied into the input/output array based on the results of
9647
+ // the comparator calls, but directly from the temp memory, bypassing the
9648
+ // unpoisoning done in wrapped_qsort_compar. We deal with this by, again,
9649
+ // unpoisoning the entire array after the sort is done.
9650
+ //
9651
+ // We can not check that the entire array is initialized at the beginning. IMHO,
9652
+ // it's fine for parts of the sorted objects to contain uninitialized memory,
9653
+ // ex. as padding in structs.
9654
+ typedef int (*qsort_compar_f)(const void *, const void *);
9655
+ static THREADLOCAL qsort_compar_f qsort_compar;
9656
+ static THREADLOCAL SIZE_T qsort_size;
9657
+ int wrapped_qsort_compar (const void *a, const void *b) {
9658
+ COMMON_INTERCEPTOR_UNPOISON_PARAM (2 );
9659
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE (a, qsort_size);
9660
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE (b, qsort_size);
9661
+ return qsort_compar (a, b);
9662
+ }
9663
+
9664
+ INTERCEPTOR (void , qsort, void *base, SIZE_T nmemb, SIZE_T size,
9665
+ qsort_compar_f compar) {
9666
+ void *ctx;
9667
+ COMMON_INTERCEPTOR_ENTER (ctx, qsort, base, nmemb, size, compar);
9668
+ qsort_compar_f old_compar = qsort_compar;
9669
+ qsort_compar = compar;
9670
+ SIZE_T old_size = qsort_size;
9671
+ qsort_size = size;
9672
+ REAL (qsort)(base, nmemb, size, wrapped_qsort_compar);
9673
+ qsort_compar = old_compar;
9674
+ qsort_size = old_size;
9675
+ COMMON_INTERCEPTOR_WRITE_RANGE (ctx, base, nmemb * size);
9676
+ }
9677
+ #define INIT_QSORT COMMON_INTERCEPT_FUNCTION (qsort)
9678
+ #else
9679
+ #define INIT_QSORT
9680
+ #endif
9681
+
9682
+ #if SANITIZER_INTERCEPT_QSORT_R
9683
+ typedef int (*qsort_r_compar_f)(const void *, const void *, void *);
9684
+ static THREADLOCAL qsort_r_compar_f qsort_r_compar;
9685
+ static THREADLOCAL SIZE_T qsort_r_size;
9686
+ int wrapped_qsort_r_compar (const void *a, const void *b, void *arg) {
9687
+ COMMON_INTERCEPTOR_UNPOISON_PARAM (3 );
9688
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE (a, qsort_r_size);
9689
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE (b, qsort_r_size);
9690
+ return qsort_r_compar (a, b, arg);
9691
+ }
9692
+
9693
+ INTERCEPTOR (void , qsort_r, void *base, SIZE_T nmemb, SIZE_T size,
9694
+ qsort_r_compar_f compar, void *arg) {
9695
+ void *ctx;
9696
+ COMMON_INTERCEPTOR_ENTER (ctx, qsort_r, base, nmemb, size, compar, arg);
9697
+ qsort_r_compar_f old_compar = qsort_r_compar;
9698
+ qsort_r_compar = compar;
9699
+ SIZE_T old_size = qsort_r_size;
9700
+ qsort_r_size = size;
9701
+ REAL (qsort_r)(base, nmemb, size, wrapped_qsort_r_compar, arg);
9702
+ qsort_r_compar = old_compar;
9703
+ qsort_r_size = old_size;
9704
+ COMMON_INTERCEPTOR_WRITE_RANGE (ctx, base, nmemb * size);
9705
+ }
9706
+ #define INIT_QSORT_R COMMON_INTERCEPT_FUNCTION (qsort_r)
9707
+ #else
9708
+ #define INIT_QSORT_R
9709
+ #endif
9710
+
9642
9711
static void InitializeCommonInterceptors () {
9643
9712
#if SI_POSIX
9644
9713
static u64 metadata_mem[sizeof (MetadataHashMap) / sizeof (u64 ) + 1 ];
@@ -9940,6 +10009,8 @@ static void InitializeCommonInterceptors() {
9940
10009
INIT_CRYPT;
9941
10010
INIT_CRYPT_R;
9942
10011
INIT_GETENTROPY;
10012
+ INIT_QSORT;
10013
+ INIT_QSORT_R;
9943
10014
9944
10015
INIT___PRINTF_CHK;
9945
10016
}
0 commit comments