Skip to content

protect evaluated lazy call in new_error_subscript_type() - #2174

Open
kevinushey wants to merge 1 commit into
r-lib:mainfrom
kevinushey:fix/subscript-protect-lazy-call
Open

protect evaluated lazy call in new_error_subscript_type()#2174
kevinushey wants to merge 1 commit into
r-lib:mainfrom
kevinushey:fix/subscript-protect-lazy-call

Conversation

@kevinushey

Copy link
Copy Markdown

Fixes #2173.

new_error_subscript_type() was the only r_lazy_eval_protect() call site in the package that did not KEEP() its result. When the lazy call evaluates to a language object, r_lazy_eval_protect() returns a freshly allocated quote() wrapper, which sat unprotected in the args array while r_call_n() allocated the cons cells of the argument pairlist. A GC during those allocations collects the wrapper; the freed node is typically reused by the next Rf_cons(), so the condition's call field ends up as a cyclic pairlist pointing into the condition call's own arguments (and this can just as well crash R).

This PR KEEP()s the result (matching the call sites in conditions.c, match.c, and list-combine.c) and bumps the matching FREE(3) to FREE(4).

Verified with the gctorture() repro from #2173:

  • before: conditionCall(cnd) becomes a self-referential dotted pair list
  • after: conditionCall(cnd) is foo(bar) as expected

testthat::test_local(filter = "subscript") passes (262 tests).

The result of r_lazy_eval_protect() is a freshly allocated quote()
wrapper whenever the lazy call evaluates to a language object. It was
left unprotected while r_call_n() allocated the argument pairlist, so
a GC during those allocations could collect it, corrupting the call
field of the condition (or crashing R).

KEEP() it like every other r_lazy_eval_protect() call site and bump
the matching FREE().

Fixes r-lib#2173.
@kevinushey kevinushey closed this Aug 5, 2026
@kevinushey kevinushey reopened this Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

unprotected lazy call in new_error_subscript_type() can be corrupted by GC

1 participant