Skip to content

fix potential PROTECT/UNPROTECT issues in C code - #469

Merged
jeroen merged 2 commits into
jeroen:masterfrom
kevinushey:protect-fixes
Sep 2, 2026
Merged

fix potential PROTECT/UNPROTECT issues in C code#469
jeroen merged 2 commits into
jeroen:masterfrom
kevinushey:protect-fixes

Conversation

@kevinushey

@kevinushey kevinushey commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

This fixes a PROTECT-related issue in push_parser.c, found via a manual audit.

PROTECT() calls were nested inside the argument list of Rf_lang4(). Since C argument evaluation order is unspecified, a compiler may evaluate Rf_allocVector() and Rf_ScalarInteger() before either PROTECT() runs, leaving a fresh allocation unprotected while another allocation (and potentially GC) occurs. The objects are now allocated and protected in separate statements.

Also guarantees NUL-termination of the error buffer after strncpy(), which does not terminate on truncation.

This PR originally also added defensive PROTECT() calls in parse.c, r-base64.c, and row_collapse.c. As @jeroen noted in review, those windows contain no allocating calls, so the protection had no effect; they've been dropped.

Tested with R CMD INSTALL and the testthat suite, and exercised the touched paths (including the parse-error path) under gctorture(TRUE).

Comment thread src/parse.c Outdated
Rf_errorcall(R_NilValue, "%s", errbuf);
}
SEXP out = ParseValue(node, bigint);
SEXP out = PROTECT(ParseValue(node, bigint));

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really not needed, yajl_tree_free is not an R call so it won't have any effect on gc.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, you're right -- this was just meant to be defensive. The only potential issue is in push_parser.c, since (in theory) the evaluation order of the expressions is undefined. I'll trim this PR to the minimal set of changes that are worth considering here.

Per review feedback, drop the defensive-only PROTECT additions in
parse.c, r-base64.c, and row_collapse.c -- none of those windows
contain allocating calls. Also drop the UNPROTECT before the goto in
push_parser.c, since Rf_error() unwinds the protection stack itself.

Keep the evaluation-order fix in push_parser.c, and NUL-termination
of the error buffer after strncpy.
@jeroen
jeroen merged commit 8dc03d5 into jeroen:master Sep 2, 2026
11 checks passed
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.

2 participants