Fix numeric overflow checks in size classes

This commit is contained in:
Slobodan Predolac 2026-05-26 12:37:32 -07:00
parent 136d342aa0
commit 6b24522545
3 changed files with 39 additions and 8 deletions

View file

@ -268,12 +268,17 @@
#define SC_LARGE_MINCLASS ((size_t)1ULL << (LG_PAGE + SC_LG_NGROUP))
#define SC_LG_LARGE_MINCLASS (LG_PAGE + SC_LG_NGROUP)
/* Internal; only used for the definition of SC_LARGE_MAXCLASS. */
#define SC_MAX_BASE ((size_t)1 << (SC_PTR_BITS - 2))
#define SC_MAX_DELTA ((size_t)1 << (SC_PTR_BITS - 2 - SC_LG_NGROUP))
/* The largest size class supported. */
#define SC_LARGE_MAXCLASS (SC_MAX_BASE + (SC_NGROUP - 1) * SC_MAX_DELTA)
/*
* The largest size class supported. Spell this out directly to avoid
* expanding subtractive arithmetic at every use site.
*/
#if LG_SIZEOF_PTR == 3
# define SC_LARGE_MAXCLASS 0x7000000000000000ULL
#elif LG_SIZEOF_PTR == 2
# define SC_LARGE_MAXCLASS 0x70000000ULL
#else
# error "Unsupported pointer size"
#endif
/* Maximum number of regions in one slab. */
#ifndef CONFIG_LG_SLAB_MAXREGS

View file

@ -314,12 +314,20 @@ sz_size2index_usize_fastpath(size_t size, szind_t *ind, size_t *usize) {
JEMALLOC_ALWAYS_INLINE size_t
sz_s2u_compute_using_delta(size_t size) {
if (unlikely(size > SC_LARGE_MAXCLASS)) {
return 0;
}
size_t x = lg_floor((size << 1) - 1);
size_t lg_delta = (x < SC_LG_NGROUP + LG_QUANTUM + 1)
? LG_QUANTUM
: x - SC_LG_NGROUP - 1;
size_t delta = ZU(1) << lg_delta;
size_t delta_mask = delta - 1;
if (unlikely(size > SIZE_T_MAX - delta_mask)) {
return 0;
}
size_t usize = (size + delta_mask) & ~delta_mask;
return usize;
}
@ -385,7 +393,11 @@ JEMALLOC_ALWAYS_INLINE size_t
sz_sa2u(size_t size, size_t alignment) {
size_t usize;
assert(alignment != 0 && ((alignment - 1) & alignment) == 0);
if (unlikely(alignment == 0)) {
return 0;
}
size_t alignment_mask = alignment - 1;
assert((alignment_mask & alignment) == 0);
/* Try for a small size class. */
if (size <= SC_SMALL_MAXCLASS && alignment <= PAGE) {
@ -403,7 +415,10 @@ sz_sa2u(size_t size, size_t alignment) {
* 144 | 10100000 | 32
* 192 | 11000000 | 64
*/
usize = sz_s2u(ALIGNMENT_CEILING(size, alignment));
if (unlikely(size > SIZE_T_MAX - alignment_mask)) {
return 0;
}
usize = sz_s2u((size + alignment_mask) & ~alignment_mask);
if (usize < SC_LARGE_MINCLASS) {
return usize;
}

View file

@ -190,6 +190,9 @@ TEST_BEGIN(test_overflow) {
max_size_class = get_max_size_class();
max_psz = max_size_class + PAGE;
expect_zu_eq(max_size_class, SC_LARGE_MAXCLASS,
"Computed max size class should match SC_LARGE_MAXCLASS");
expect_u_eq(sz_size2index(max_size_class + 1), SC_NSIZES,
"sz_size2index() should return NSIZES on overflow");
expect_u_eq(sz_size2index(ZU(PTRDIFF_MAX) + 1), SC_NSIZES,
@ -203,6 +206,14 @@ TEST_BEGIN(test_overflow) {
"sz_s2u() should return 0 for unsupported size");
expect_zu_eq(
sz_s2u(SIZE_T_MAX), 0, "sz_s2u() should return 0 on overflow");
expect_zu_eq(sz_s2u_compute_using_delta(SC_LARGE_MAXCLASS + 1), 0,
"sz_s2u_compute_using_delta() should return 0 for unsupported size");
expect_zu_eq(sz_s2u_compute_using_delta(SIZE_T_MAX), 0,
"sz_s2u_compute_using_delta() should return 0 on overflow");
expect_zu_eq(sz_sa2u(1, 0), 0,
"sz_sa2u() should return 0 for zero alignment");
expect_zu_eq(sz_sa2u(SIZE_T_MAX, PAGE), 0,
"sz_sa2u() should return 0 on overflow");
expect_u_eq(sz_psz2ind(max_size_class + 1), SC_NPSIZES,
"sz_psz2ind() should return NPSIZES on overflow");