Fix off-by-one in arenas_bin_i_index and arenas_lextent_i_index bounds checks

The index validation used > instead of >=, allowing access at index
SC_NBINS (for bins) and SC_NSIZES-SC_NBINS (for lextents), which are
one past the valid range. This caused out-of-bounds reads in bin_infos[]
and sz_index2size_unsafe().

Add unit tests that verify the boundary indices return ENOENT.
This commit is contained in:
Slobodan Predolac 2026-03-27 08:42:54 -07:00
parent 176ea0a801
commit 513778bcb1
2 changed files with 49 additions and 2 deletions

View file

@ -3250,7 +3250,7 @@ CTL_RO_NL_GEN(arenas_bin_i_slab_size, bin_infos[mib[2]].slab_size, size_t)
CTL_RO_NL_GEN(arenas_bin_i_nshards, bin_infos[mib[2]].n_shards, uint32_t)
static const ctl_named_node_t *
arenas_bin_i_index(tsdn_t *tsdn, const size_t *mib, size_t miblen, size_t i) {
if (i > SC_NBINS) {
if (i >= SC_NBINS) {
return NULL;
}
return super_arenas_bin_i_node;
@ -3262,7 +3262,7 @@ CTL_RO_NL_GEN(arenas_lextent_i_size,
static const ctl_named_node_t *
arenas_lextent_i_index(
tsdn_t *tsdn, const size_t *mib, size_t miblen, size_t i) {
if (i > SC_NSIZES - SC_NBINS) {
if (i >= SC_NSIZES - SC_NBINS) {
return NULL;
}
return super_arenas_lextent_i_node;

View file

@ -956,6 +956,52 @@ TEST_BEGIN(test_arenas_bin_constants) {
}
TEST_END
TEST_BEGIN(test_arenas_bin_oob) {
size_t sz;
size_t result;
char buf[128];
/*
* Querying the bin at index SC_NBINS should fail because valid
* indices are [0, SC_NBINS).
*/
sz = sizeof(result);
malloc_snprintf(
buf, sizeof(buf), "arenas.bin.%u.size", (unsigned)SC_NBINS);
expect_d_eq(mallctl(buf, (void *)&result, &sz, NULL, 0), ENOENT,
"mallctl() should fail for out-of-bounds bin index SC_NBINS");
/* One below the boundary should succeed. */
malloc_snprintf(
buf, sizeof(buf), "arenas.bin.%u.size", (unsigned)(SC_NBINS - 1));
expect_d_eq(mallctl(buf, (void *)&result, &sz, NULL, 0), 0,
"mallctl() should succeed for valid bin index SC_NBINS-1");
}
TEST_END
TEST_BEGIN(test_arenas_lextent_oob) {
size_t sz;
size_t result;
char buf[128];
unsigned nlextents = SC_NSIZES - SC_NBINS;
/*
* Querying the lextent at index nlextents should fail because valid
* indices are [0, nlextents).
*/
sz = sizeof(result);
malloc_snprintf(buf, sizeof(buf), "arenas.lextent.%u.size", nlextents);
expect_d_eq(mallctl(buf, (void *)&result, &sz, NULL, 0), ENOENT,
"mallctl() should fail for out-of-bounds lextent index");
/* Querying the last element (nlextents - 1) should succeed. */
malloc_snprintf(
buf, sizeof(buf), "arenas.lextent.%u.size", nlextents - 1);
expect_d_eq(mallctl(buf, (void *)&result, &sz, NULL, 0), 0,
"mallctl() should succeed for valid lextent index");
}
TEST_END
TEST_BEGIN(test_arenas_lextent_constants) {
#define TEST_ARENAS_LEXTENT_CONSTANT(t, name, expected) \
do { \
@ -1379,6 +1425,7 @@ main(void) {
test_arena_i_dss, test_arena_i_name, test_arena_i_retain_grow_limit,
test_arenas_dirty_decay_ms, test_arenas_muzzy_decay_ms,
test_arenas_constants, test_arenas_bin_constants,
test_arenas_bin_oob, test_arenas_lextent_oob,
test_arenas_lextent_constants, test_arenas_create,
test_arenas_lookup, test_prof_active, test_stats_arenas,
test_stats_arenas_hpa_shard_counters,