rax.c: add OOM guard in raxRemove for incomplete ancestor stack#580
Open
ft-ioxcs wants to merge 1 commit into
Open
rax.c: add OOM guard in raxRemove for incomplete ancestor stack#580ft-ioxcs wants to merge 1 commit into
ft-ioxcs wants to merge 1 commit into
Conversation
raxLowWalk:613 discards raxStackPush return value. If the stack heap-expansion fails (depth > 32, malloc(512)), ts.oom is set but the ancestor is silently dropped. raxRemove then calls raxRemoveCleanup before the existing ts.oom guard at line 1331 (which only suppresses recompression, not cleanup). The incomplete stack causes raxRemoveCleanup to pop wrong ancestors, potentially passing a non-child to raxRemoveChild, whose while(1) scan reads past the child array (UB, per the comment at raxFindParentLink:1128). Fix: check ts.oom immediately after raxLowWalk, matching the existing pattern at raxSeek:1966. Fault-injection test confirms: without fix, raxRemove returns 1 (false success) with corrupted tree; with fix, returns 0, tree intact.
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.
Summary
raxRemovecallsraxLowWalkwith a stack to track ancestor nodes, butraxLowWalkdiscards the return value ofraxStackPush. If a stack heap-expansion fails (tree depth > 32,malloc(512)),ts.oomis set but the ancestor is silently dropped.raxRemovethen proceeds to mutate the tree and callraxRemoveCleanupwith the incomplete stack, which pops wrong ancestors and can pass a non-child toraxRemoveChild. That function has an unboundedwhile(1)scan documented as UB. The fix adds ats.oomcheck immediately afterraxLowWalkreturns, matching the pattern already used inraxSeekat line 1966.Files changed: 1 file, +4 lines.
Causal Chain
raxLowWalk:613raxStackPush(ts,h)return value discarded. On OOM,ts->oom = 1but the ancestor node is not pushed.raxRemove:1284&tstoraxLowWalk— the only mutating caller that supplies a real stack.raxRemove:1298raxRemoveCleanup(rax, h, &ts, &trycompress)before thets.oomguard at 1331.raxRemove:1321raxRemove:1331if (trycompress && ts.oom) trycompress = 0;— only suppresses recompression; cleanup has already run.raxRemoveCleanup:~1243h = raxStackPop(ts)— with a truncated stack, pops the wrong ancestor orNULL.raxRemoveChild:1217-1226while(1) { ... cp++; }— scans past the child-array boundary whenchildis not in the wrong parent.raxFindParentLink:1128-1129Correct Pattern (already in this file)
raxSeek:1966checksts.oomimmediately after its ownraxLowWalkcall:raxRemoveat line 1284 simply omits this check.Impact
The stack static buffer holds 32 entries (
RAX_STACK_STATIC_ITEMSinrax.h:151). Trees deeper than 32 nodes trigger a heap expansion (malloc(512)on first expansion). If that allocation fails — under system OOM, custom allocator policy, container memory limits, orsetrlimit— the stack is silently incomplete. Depending on tree shape:raxRemoveCleanuppopsNULLand dereferencesh->iskeyat line 1246 (immediate segfault).raxRemovereturns 1 (success) while the tree is silently corrupted.Verification
A fault-injection test was built using
RAX_MALLOC_INCLUDE(supported atrax.c:41-45) to substitute a custom allocator that can be instructed to fail. The test:malloccall.raxRemoveon the deepest key.Without the fix:
raxRemovereturns 1 (claims success), tree size and key presence are corrupted.With the fix:
raxRemovereturns 0, tree is unchanged, all keys still present.A subsequent normal
raxRemove(no fault) on the same tree confirms the non-OOM path is unaffected.make ds4_test(which linksrax.o) builds cleanly with the patch applied — no regressions.