Repository navigation
Keep the expression result alive in the native ReturnHandler - #6852
SanderMuller wants to merge 1 commit into
Conversation
The native processStmt() read the throw points, impure points and dependencies of the returned expression as pointers into its ExpressionResult. The result was released at the end of the inner block, before the node callback ran. A rule that called getType() from that callback could then reuse the freed memory, and the statement result got an unrelated object as its impure points. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
The native ReturnHandler kept pointers to the throw points, impure points and dependencies of the returned expression's ExpressionResult. The accessors return a pointer into the object's property table without a reference count. The ExpressionResult was declared in an inner block, so it was freed when that block ended. The pointers were used after the node callback, and a rule calling I checked the other native handlers for the same pattern and found none. I looked for a pointer read from a result that is freed, or used after a callback while the result can change. NewHandler and StaticCallHandler have the same inner-block shape, but they copy the throw points before the block ends. Pointers into a live ExpressionResult are safe, because its properties are only set in the constructor. |
|
my feeling is that the underlying problem is something we should be able to detect in the CI pipeline (for the bigger picture; separate PR). would the underlying problem be detectable with a static code analysis tool for the cpp code? |
|
I tried both on the pre-fix ReturnHandler. Static: Apple clang 21 finds nothing, even with AddressSanitizer: a |
The native ReturnHandler took the throw points, impure points and dependencies of the returned expression as pointers into its ExpressionResult. It released that result at the end of an inner block, before the node callback ran. If a rule then calls
getType()on the returned expression, for example from a ClassMethod callback, the freed memory can be reused. The statement result then gets an unrelated object as its impure points.On 2.3.2 this crashes a project that uses
NarrowReturnObjectTypeRulefrom tomasvotruba/type-coverage on a method that returnsonce(function () { ... }):The fix keeps the result alive until the end of processStmt(). With the extension loaded, the new test fails with that error without the fix and passes with it. The other statement handlers already keep their result alive while they use it.
🤖 Generated with Claude Code