]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
stdlib: Avoid element self-comparisons in qsort
authorFlorian Weimer <fweimer@redhat.com>
Wed, 8 Nov 2023 14:18:02 +0000 (15:18 +0100)
committerFlorian Weimer <fweimer@redhat.com>
Wed, 8 Nov 2023 14:18:02 +0000 (15:18 +0100)
This improves compatibility with applications which assume that qsort
does not invoke the comparison function with equal pointer arguments.

The newly introduced branches should be predictable, as leading to a
call to the comparison function.  If the prediction fails, we avoid
calling the function.

Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
stdlib/qsort.c

index fd32a165e7313c3a69387e7a4ac60cb773e5472f..ad110e8a892a66e1fc90f850b828e1a2d09e2ac5 100644 (file)
@@ -136,7 +136,7 @@ siftdown (void *base, size_t size, size_t k, size_t n,
       if (j < n && cmp (base + (j * size), base + ((j + 1) * size), arg) < 0)
        j++;
 
-      if (cmp (base + (k * size), base + (j * size), arg) >= 0)
+      if (j == k || cmp (base + (k * size), base + (j * size), arg) >= 0)
        break;
 
       do_swap (base + (size * j), base + (k * size), size, swap_type);
@@ -332,10 +332,12 @@ __qsort_r (void *const pbase, size_t total_elems, size_t size,
             that this algorithm runs much faster than others. */
          do
            {
-             while ((*cmp) ((void *) left_ptr, (void *) mid, arg) < 0)
+             while (left_ptr != mid
+                    && (*cmp) ((void *) left_ptr, (void *) mid, arg) < 0)
                left_ptr += size;
 
-             while ((*cmp) ((void *) mid, (void *) right_ptr, arg) < 0)
+             while (right_ptr != mid
+                    && (*cmp) ((void *) mid, (void *) right_ptr, arg) < 0)
                right_ptr -= size;
 
              if (left_ptr < right_ptr)