Skip to content

Conversation

@hvitved
Copy link
Contributor

@hvitved hvitved commented Dec 19, 2025

Manually pushes magic context into transitive closure calculation, and replaces the recursive calculation with doubleBoundedFastTC.

Before

Pipeline standard for AccessAfterLifetimeExtensions::AccessAfterLifetime::mayEncloseOnStack/2#3cdefece#bf@61cb32j5 was evaluated in 30 iterations totaling 44856ms (delta sizes total: 241646328).
         241404616     ~1%    {2} r1 = SCAN `AccessAfterLifetimeExtensions::AccessAfterLifetime::mayEncloseOnStack/2#3cdefece#bf#prev_delta` OUTPUT In.1, In.0
        7379161442  ~1080%    {2}    | JOIN WITH `_AstNode::AstNode.getEnclosingBlock/0#5c38e65a_AstNode::AstNode.getEnclosingCallable/0#5a548913_Bloc__#join_rhs` ON FIRST 1 OUTPUT Lhs.1, Rhs.1
         333897324    ~40%    {2}    | AND NOT `AccessAfterLifetimeExtensions::AccessAfterLifetime::mayEncloseOnStack/2#3cdefece#bf#prev`(FIRST 2)
         297961888    ~24%    {2}    | JOIN WITH `project#AccessAfterLifetimeExtensions::AccessAfterLifetime::sourceValueScope/3#d065ba16#2` ON FIRST 1 OUTPUT Lhs.0, Lhs.1
                              return r1

The query started regressing with 108db75, which effectively computed the transitive closure over the entire virtual dispatch graph.

DCA is great; a 30 % reduction in total analysis time on rust.

@github-actions github-actions bot added the Rust Pull requests that update Rust code label Dec 19, 2025
Before
```
Pipeline standard for AccessAfterLifetimeExtensions::AccessAfterLifetime::mayEncloseOnStack/2#3cdefece#bf@61cb32j5 was evaluated in 30 iterations totaling 44856ms (delta sizes total: 241646328).
         241404616     ~1%    {2} r1 = SCAN `AccessAfterLifetimeExtensions::AccessAfterLifetime::mayEncloseOnStack/2#3cdefece#bf#prev_delta` OUTPUT In.1, In.0
        7379161442  ~1080%    {2}    | JOIN WITH `_AstNode::AstNode.getEnclosingBlock/0#5c38e65a_AstNode::AstNode.getEnclosingCallable/0#5a548913_Bloc__#join_rhs` ON FIRST 1 OUTPUT Lhs.1, Rhs.1
         333897324    ~40%    {2}    | AND NOT `AccessAfterLifetimeExtensions::AccessAfterLifetime::mayEncloseOnStack/2#3cdefece#bf#prev`(FIRST 2)
         297961888    ~24%    {2}    | JOIN WITH `project#AccessAfterLifetimeExtensions::AccessAfterLifetime::sourceValueScope/3#d065ba16#2` ON FIRST 1 OUTPUT Lhs.0, Lhs.1
                              return r1
```
@hvitved hvitved force-pushed the rust/access-after-lifetime-perf branch from bfcbf51 to 2f04451 Compare December 19, 2025 12:10
@hvitved hvitved closed this Dec 19, 2025
@hvitved hvitved reopened this Dec 19, 2025
@hvitved hvitved added the no-change-note-required This PR does not need a change note label Dec 19, 2025
@hvitved hvitved marked this pull request as ready for review December 19, 2025 18:15
@hvitved hvitved requested a review from a team as a code owner December 19, 2025 18:15
Copilot AI review requested due to automatic review settings December 19, 2025 18:15
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR optimizes the AccessAfterLifetime.ql query to significantly improve performance by replacing a recursive transitive closure calculation with doubleBoundedFastTC. The optimization was motivated by a 30% reduction in total analysis time on Rust code after a previous change (PR #20966) caused performance regression by computing transitive closure over the entire virtual dispatch graph.

Key Changes

  • Introduces a parameterized module pattern using a signature for dataflow candidate pairs
  • Replaces recursive mayEncloseOnStack predicate with optimized transitive closure using doubleBoundedFastTC
  • Refactors the logic into a DereferenceAfterLifetime module that takes a flow predicate as a parameter

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
rust/ql/src/queries/security/CWE-825/AccessAfterLifetime.ql Updates the call to dereferenceAfterLifetime to use the new parameterized module, passing AccessAfterLifetimeFlow::flow/2 as the flow predicate
rust/ql/lib/codeql/rust/security/AccessAfterLifetimeExtensions.qll Refactors dereferenceAfterLifetime into a parameterized module with optimized transitive closure using doubleBoundedFastTC, introduces TcNode type to unify sources, sinks, and block expressions for efficient graph traversal

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

* Holds if block `a` contains block `b`, in the sense that a stack allocated variable in
* `a` may still be on the stack during execution of `b`. This is interprocedural,
* but is an overapproximation that doesn't accurately track call contexts
* (for example if `f` and `g` both call `b`, then then depending on the
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

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

The comment contains a duplicate word 'then'. The phrase should read "for example if f and g both call b, then depending on the caller..." with only one 'then'.

Suggested change
* (for example if `f` and `g` both call `b`, then then depending on the
* (for example if `f` and `g` both call `b`, then depending on the

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-change-note-required This PR does not need a change note Rust Pull requests that update Rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant