From e36a0fa5bc1e1090362505ac4af4408466ba5163 Mon Sep 17 00:00:00 2001 From: Tony Printezis Date: Mon, 3 Aug 2026 14:36:35 -0700 Subject: [PATCH] Fix prof-sampling / guard-page interaction bug in the SAN Due to an incorrect condition, a prof-promoted small allocation was incorrectly wrapped in large-extent guard pages. This consumed the large-guard budget, so the next genuinely large allocation that should have been guarded got skipped. --- Makefile.in | 1 + include/jemalloc/internal/san.h | 2 +- src/san_bump.c | 4 ++-- test/unit/san_prof.c | 29 +++++++++++++++++++++++++++++ test/unit/san_prof.sh | 5 +++++ 5 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 test/unit/san_prof.c create mode 100644 test/unit/san_prof.sh diff --git a/Makefile.in b/Makefile.in index 58b58330..326a4029 100644 --- a/Makefile.in +++ b/Makefile.in @@ -240,6 +240,7 @@ TESTS_UNIT := \ $(srcroot)test/unit/fork.c \ ${srcroot}test/unit/fxp.c \ ${srcroot}test/unit/san.c \ + ${srcroot}test/unit/san_prof.c \ ${srcroot}test/unit/san_bump.c \ $(srcroot)test/unit/hash.c \ $(srcroot)test/unit/hpa.c \ diff --git a/include/jemalloc/internal/san.h b/include/jemalloc/internal/san.h index 5dcae376..089b88e1 100644 --- a/include/jemalloc/internal/san.h +++ b/include/jemalloc/internal/san.h @@ -101,7 +101,7 @@ san_large_extent_decide_guard( *tsd_san_extents_until_guard_largep_get(tsd) = n - 1; } - if (n == 1 && (alignment <= PAGE) + if (n == 1 && (size >= SC_LARGE_MINCLASS) && (alignment <= PAGE) && (san_two_side_guarded_sz(size) <= SC_LARGE_MAXCLASS)) { *tsd_san_extents_until_guard_largep_get( tsd) = opt_san_guard_large; diff --git a/src/san_bump.c b/src/san_bump.c index 30b90b04..c78313b4 100644 --- a/src/san_bump.c +++ b/src/san_bump.c @@ -56,12 +56,12 @@ san_bump_alloc(tsdn_t *tsdn, san_bump_alloc_t *sba, pac_t *pac, sba->curr_reg = NULL; } - malloc_mutex_unlock(tsdn, &sba->mtx); - assert(!edata_guarded_get(edata)); assert(sba->curr_reg == NULL || !edata_guarded_get(sba->curr_reg)); assert(to_destroy == NULL || !edata_guarded_get(to_destroy)); + malloc_mutex_unlock(tsdn, &sba->mtx); + if (to_destroy != NULL) { extent_destroy_wrapper(tsdn, pac, ehooks, to_destroy); } diff --git a/test/unit/san_prof.c b/test/unit/san_prof.c new file mode 100644 index 00000000..9e1b665c --- /dev/null +++ b/test/unit/san_prof.c @@ -0,0 +1,29 @@ +#include "test/jemalloc_test.h" +#include "test/san.h" + +#include "jemalloc/internal/prof_inlines.h" + +TEST_BEGIN(test_prof_sampled_small_is_not_guarded_as_large) { + test_skip_if(!config_prof); + + tsd_t *tsd = tsd_fetch(); + void *small = malloc(PAGE / 2); + expect_ptr_not_null(small, "Unexpected small allocation failure"); + expect_true(prof_sampled(tsd, small), + "Expected every allocation to be prof sampled"); + expect_false(extent_is_guarded(tsd_tsdn(tsd), small), + "A prof-promoted small allocation is not a large extent"); + free(small); + + void *large = malloc(SC_LARGE_MINCLASS); + expect_ptr_not_null(large, "Unexpected large allocation failure"); + expect_true(extent_is_guarded(tsd_tsdn(tsd), large), + "The next eligible large extent should still be guarded"); + free(large); +} +TEST_END + +int +main(void) { + return test(test_prof_sampled_small_is_not_guarded_as_large); +} diff --git a/test/unit/san_prof.sh b/test/unit/san_prof.sh new file mode 100644 index 00000000..684bd795 --- /dev/null +++ b/test/unit/san_prof.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +if [ "x${enable_prof}" = "x1" ] ; then + export MALLOC_CONF="prof:true,lg_prof_sample:0,san_guard_large:1,san_guard_small:0" +fi