aboutsummaryrefslogtreecommitdiff
path: root/test/unit/double_free.c
diff options
context:
space:
mode:
authorWarner Losh <imp@FreeBSD.org>2025-02-05 23:20:13 +0000
committerWarner Losh <imp@FreeBSD.org>2025-02-05 23:20:13 +0000
commit48ec896efb0b78141df004eaa21288b84590c9da (patch)
tree33799792fd95c266d472ab1ae51d50ab4f942eb3 /test/unit/double_free.c
parentd28d7fbede216494aa3942af042cc084fcd6098a (diff)
jemalloc: Import 5.3.0 54eaed1d8b56b1aa528be3bdd1877e59c56fa90cvendor/jemalloc/5.3.0vendor/jemalloc
Import jemalloc 5.3.0. This import changes how manage the jemalloc vendor branch (which was just started anyway). Starting with 5.3.0, we import a clean tree from the upstream github, removing all the old files that are no longer upstream, or that we've kept around for some reason. We do this because we merge from this raw version of jemalloc into the FreeBSD contrib/jemalloc, then we run autogen stuff, generate all the generated .h files with gmake, then finally remove much of the generated files in contrib/jemalloc using an update script. Sponsored by: Netflix
Diffstat (limited to 'test/unit/double_free.c')
-rw-r--r--test/unit/double_free.c77
1 files changed, 77 insertions, 0 deletions
diff --git a/test/unit/double_free.c b/test/unit/double_free.c
new file mode 100644
index 000000000000..12122c1b7498
--- /dev/null
+++ b/test/unit/double_free.c
@@ -0,0 +1,77 @@
+#include "test/jemalloc_test.h"
+#include "test/san.h"
+
+#include "jemalloc/internal/safety_check.h"
+
+bool fake_abort_called;
+void fake_abort(const char *message) {
+ (void)message;
+ fake_abort_called = true;
+}
+
+void
+test_large_double_free_pre(void) {
+ safety_check_set_abort(&fake_abort);
+ fake_abort_called = false;
+}
+
+void
+test_large_double_free_post() {
+ expect_b_eq(fake_abort_called, true, "Double-free check didn't fire.");
+ safety_check_set_abort(NULL);
+}
+
+TEST_BEGIN(test_large_double_free_tcache) {
+ test_skip_if(!config_opt_safety_checks);
+ /*
+ * Skip debug builds, since too many assertions will be triggered with
+ * double-free before hitting the one we are interested in.
+ */
+ test_skip_if(config_debug);
+
+ test_large_double_free_pre();
+ char *ptr = malloc(SC_LARGE_MINCLASS);
+ bool guarded = extent_is_guarded(tsdn_fetch(), ptr);
+ free(ptr);
+ if (!guarded) {
+ free(ptr);
+ } else {
+ /*
+ * Skip because guarded extents may unguard immediately on
+ * deallocation, in which case the second free will crash before
+ * reaching the intended safety check.
+ */
+ fake_abort_called = true;
+ }
+ mallctl("thread.tcache.flush", NULL, NULL, NULL, 0);
+ test_large_double_free_post();
+}
+TEST_END
+
+TEST_BEGIN(test_large_double_free_no_tcache) {
+ test_skip_if(!config_opt_safety_checks);
+ test_skip_if(config_debug);
+
+ test_large_double_free_pre();
+ char *ptr = mallocx(SC_LARGE_MINCLASS, MALLOCX_TCACHE_NONE);
+ bool guarded = extent_is_guarded(tsdn_fetch(), ptr);
+ dallocx(ptr, MALLOCX_TCACHE_NONE);
+ if (!guarded) {
+ dallocx(ptr, MALLOCX_TCACHE_NONE);
+ } else {
+ /*
+ * Skip because guarded extents may unguard immediately on
+ * deallocation, in which case the second free will crash before
+ * reaching the intended safety check.
+ */
+ fake_abort_called = true;
+ }
+ test_large_double_free_post();
+}
+TEST_END
+
+int
+main(void) {
+ return test(test_large_double_free_no_tcache,
+ test_large_double_free_tcache);
+}