@@ -354,6 +354,12 @@ tests := \
tst-xpg-basename \
# tests
+tests-cxx = \
+ tst-qsort7 \
+ # tests-cxx
+
+tests += $(if $(CXX),$(tests-cxx))
+
tests-internal := \
tst-qsort4 \
tst-strtod1i \
@@ -539,7 +545,17 @@ tests-special += $(objpfx)isomac.out
ifeq ($(run-built-tests),yes)
tests-special += $(objpfx)tst-fmtmsg.out
-endif
+ifeq ($(build-shared),yes)
+ifneq ($(PERL),no)
+generated += \
+ tst-qsort7.mtrace \
+ # generated
+tests-special += \
+ $(objpfx)tst-qsort7-mem.out \
+ # tests-special
+endif # $(build-shared) == yes
+endif # $(PERL) == yes
+endif # $(run-built-tests) == yes
include ../Rules
@@ -627,3 +643,13 @@ $(objpfx)tst-setcontext3.out: tst-setcontext3.sh $(objpfx)tst-setcontext3
$(objpfx)tst-qsort5: $(libm)
$(objpfx)tst-concurrent-exit: $(shared-thread-library)
$(objpfx)tst-concurrent-quick_exit: $(shared-thread-library)
+
+CFLAGS-tst-qsort7.o = -std=c++11
+LDLIBS-tst-qsort7 = -lstdc++
+
+tst-qsort7-ENV = MALLOC_TRACE=$(objpfx)tst-qsort7.mtrace \
+ LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
+
+$(objpfx)tst-qsort7-mem.out: $(objpfx)tst-qsort7.out
+ $(common-objpfx)malloc/mtrace $(objpfx)tst-qsort7.mtrace > $@; \
+ $(evaluate-test)
@@ -25,6 +25,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
+#include <libc-lock.h>
/* Swap SIZE bytes between addresses A and B. These helpers are provided
along the generic one as an optimization. */
@@ -338,6 +339,21 @@ indirect_msort_with_tmp (const struct msort_param *p, void *b, size_t n,
}
}
+struct cleanup_arg
+{
+ char *tmp;
+ char *buf;
+};
+
+static void
+cancel_handler (void *ptr)
+{
+ /* Restore the old signal handler. */
+ struct cleanup_arg *clarg = (struct cleanup_arg *) ptr;
+ if (clarg->tmp != clarg->buf)
+ free (clarg->buf);
+}
+
void
__qsort_r (void *const pbase, size_t total_elems, size_t size,
__compar_d_fn_t cmp, void *arg)
@@ -348,19 +364,21 @@ __qsort_r (void *const pbase, size_t total_elems, size_t size,
/* Align to the maximum size used by the swap optimization. */
_Alignas (uint64_t) char tmp[QSORT_STACK_SIZE];
size_t total_size = total_elems * size;
- char *buf;
+
+ struct cleanup_arg clarg = { tmp, NULL };
+ __libc_cleanup_push (cancel_handler, &clarg);
if (size > INDIRECT_SORT_SIZE_THRES)
total_size = 2 * total_elems * sizeof (void *) + size;
if (total_size <= sizeof tmp)
- buf = tmp;
+ clarg.buf = tmp;
else
{
int save = errno;
- buf = malloc (total_size);
+ clarg.buf = malloc (total_size);
__set_errno (save);
- if (buf == NULL)
+ if (clarg.buf == NULL)
{
/* Fallback to heapsort in case of memory failure. */
heapsort_r (pbase, total_elems - 1, size, cmp, arg);
@@ -376,7 +394,7 @@ __qsort_r (void *const pbase, size_t total_elems, size_t size,
.cmp = cmp,
.arg = arg,
.var = SWAP_VOID_ARG,
- .t = buf,
+ .t = clarg.buf,
};
indirect_msort_with_tmp (&msort_param, pbase, total_elems, size);
}
@@ -388,13 +406,15 @@ __qsort_r (void *const pbase, size_t total_elems, size_t size,
.cmp = cmp,
.arg = arg,
.var = get_swap_type (pbase, size),
- .t = buf,
+ .t = clarg.buf,
};
msort_with_tmp (&msort_param, pbase, total_elems);
}
- if (buf != tmp)
- free (buf);
+ __libc_cleanup_pop (0);
+
+ if (clarg.tmp != clarg.buf)
+ free (clarg.buf);
}
libc_hidden_def (__qsort_r)
weak_alias (__qsort_r, qsort_r)
@@ -16,6 +16,10 @@
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
+#include <pthread.h>
+
+#define __libc_cleanup_push(a, b) pthread_cleanup_push (a, b)
+#define __libc_cleanup_pop(a) pthread_cleanup_pop (a)
#include "qsort.c"
#include <stdio.h>
new file mode 100644
@@ -0,0 +1,74 @@
+/* Test if qsort cleanup memory allocation if the comparison function
+ throws (BZ 32058)
+ Copyright (C) 2024 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <array>
+#include <cstdlib>
+#include <stdexcept>
+#include <mcheck.h>
+
+static int
+compar_func (const void *a1, const void *a2)
+{
+ throw std::logic_error (__func__);
+}
+
+static int
+do_test (void)
+{
+ mtrace ();
+
+ {
+ /* An array smaller than QSORT_STACK_SIZE, check if cleanup handler
+ handles the stack buffer correctly. */
+ typedef std::array<int, 32> input_t;
+ input_t input = { 0 };
+
+ try
+ {
+ qsort (input.data(),
+ input.size(),
+ sizeof (input_t::value_type),
+ compar_func);
+ }
+ catch (...)
+ {
+ }
+ }
+
+ {
+ /* An array larger than QSORT_STACK_SIZE to force memory allocation. */
+ typedef std::array<int, 1024> input_t;
+ input_t input = { 0 };
+
+ try
+ {
+ qsort (input.data(),
+ input.size(),
+ sizeof (input_t::value_type),
+ compar_func);
+ }
+ catch (...)
+ {
+ }
+ }
+
+ return 0;
+}
+
+#include <support/test-driver.c>