Skip to content

protect rotated ast root during injection - #1912

Merged
lionel- merged 1 commit into
r-lib:mainfrom
kevinushey:fix-ast-rotate-protect
Aug 5, 2026
Merged

lionel- merged 1 commit into
r-lib:mainfrom
kevinushey:fix-ast-rotate-protect

Conversation

@kevinushey

Copy link
Copy Markdown
Contributor

Fixes #1910.

After a left rotation in maybe_rotate() (src/internal/ast-rotate.c), the new expression root is referenced only from a C local: the protect stack pins the old root, which has just become a child of the new one, and protection is only transitive downwards. The recursion that follows evaluates remaining !! operands via r_eval(), so a GC triggered by user code collects the new root's cons cells and the not-yet-expanded operands. See the issue for a deterministic reproducible example on CRAN rlang 1.3.0 and full analysis.

This PR protects the new root across the recursion. Each recursion level pins its current root, and the old root plus the unexpanded RHS chain are reachable from it. The second rotation branch is unchanged: it reattaches the pivot into the protected tree before recursing.

The regression test interpolates before calling expect_identical(): with the injection inline in the expectation, the interpolation would be performed by the rlang imported by testthat, which in load_all() sessions can be a different (installed) rlang than the package under test.

Verified: the new test fails against unfixed rlang 1.3.0 and passes with the fix; all other test-nse-inject.R expectations pass; an instrumented build confirms the rotated root survives a forced R_gc() in the previously unprotected window.

After a left rotation in `maybe_rotate()`, the new expression root is
only referenced from a C local: the protect stack pins the old root,
which is now a child of the new one, and protection is only transitive
downwards. The subsequent recursion evaluates remaining `!!` operands,
so a GC triggered by user code could collect the new root and the
not-yet-expanded operands, corrupting the injection result.

Protect the new root across the recursion.

Fixes r-lib#1910.
@lionel-
lionel- merged commit a18c5e7 into r-lib:main Aug 5, 2026
13 checks passed
@lionel-

lionel- commented Aug 5, 2026

Copy link
Copy Markdown
Member

Thanks!

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.

use-after-free in !! injection during AST rotation

2 participants