map: fix uint32 overflow in sc_map_alloc when init cap is huge - #139
Merged
Conversation
sc_map_alloc_##name() rounds the requested capacity up to the next
power of two. When cap is large enough (roughly > 2^31), the round-up
wraps uint32_t back to 0 instead of failing.
The existing overflow guard 'if (*cap > SC_MAP_MAX / factor)' cannot
catch this on the sc_map_init_*() path because factor is 1 there, so
the check degenerates to 'cap > UINT32_MAX', which is never true for
a uint32_t value.
The result: sc_map_init_*() returns true with m->cap silently set to
0 (while still allocating a 1-item buffer). Any later
sc_map_get_*()/sc_map_del_*() call then computes
'mod = m->cap - 1', which underflows to UINT32_MAX, and indexes
m->mem[pos] with pos derived from a full 32-bit hash - an
out-of-bounds access far past the tiny actual allocation.
Reproduced with:
struct sc_map_str map;
sc_map_init_str(&map, 2147483649U, 0); // returns true, map.cap == 0
sc_map_get_str(&map, "somekey"); // segfaults
Fix: check for the wrap directly after computing the rounded-up
value, regardless of which factor produced it.
tezc
approved these changes
Jul 17, 2026
Owner
|
@94xhn thank you very much! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
sc_map_alloc_##name()(used bysc_map_init_*()withfactor == 1)rounds the requested capacity up to the next power of two. When
capis large enough (roughly
> 2^31), that round-up wrapsuint32_tback to
0instead of failing.The existing overflow guard
can't catch this on the
sc_map_init_*()path becausefactoris1there, so the check degenerates tocap > UINT32_MAX, which cannever be true for a
uint32_tvalue.Impact
sc_map_init_*()returnstruewithm->capsilently set to0(while still allocating a real, tiny 1-item buffer). Any later
sc_map_get_*()/sc_map_del_*()call computesmod = m->cap - 1, which underflows toUINT32_MAX, and thenindexes
m->mem[pos]withposderived from a full 32-bit hash -an out-of-bounds access far past the actual allocation.
Reproduced (unmodified
map/sc_map.c, gcc, x86_64):sc_map_put_*()is not affected the same way in practice, because itcalls
sc_map_remap_##name()first, andremap(which is0inthis corrupted state) immediately forces a real grow-to-8 before any
indexing happens -
get/delhave no such self-healing step.Fix
Check for the wrap directly on the rounded-up value, right after
computing it, instead of relying on the pre-multiply guard. This
covers the
factor == 1(init) path that the existing check misses,and is a no-op for the
factor == 2(remap/grow) path, where theexisting guard already works correctly.
Testing
the unmodified code, and confirming
sc_map_init_str()now returnsfalsecleanly for the same input instead of corrupting state.map/map_test.cagainst the patched file with-Wall -Wextra -pedantic -std=c99- builds clean, passes.sc_map_32against anaive reference map across ~8M randomized put/get/del/foreach
operations (small keyspace to force heavy collisions/wraparound,
plus a larger keyspace that forces multiple organic
remapgrows,including key
0) - zero mismatches before and after the patch, sothe fix doesn't change behavior for any valid capacity.
No functional change for any
capvalue that doesn't already triggerthe overflow.