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.
This commit is contained in:
Tony Printezis 2026-08-03 14:36:35 -07:00 committed by Tony P
parent 1e92317014
commit e36a0fa5bc
5 changed files with 38 additions and 3 deletions

View file

@ -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 \

View file

@ -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;

View file

@ -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);
}

29
test/unit/san_prof.c Normal file
View file

@ -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);
}

5
test/unit/san_prof.sh Normal file
View file

@ -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