Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rust: Allow SSA and some data flow for mutable borrows #18872

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

paldepind
Copy link
Contributor

@paldepind paldepind commented Feb 26, 2025

As seen in the tests this gets us some true flow but also some false flow. If there's an "easy" fix to the false flow (without loosing true flow) then that would be great, otherwise I think it's ok as is.

@github-actions github-actions bot added the Rust Pull requests that update Rust code label Feb 26, 2025
@paldepind paldepind marked this pull request as ready for review February 26, 2025 15:22
@Copilot Copilot bot review requested due to automatic review settings February 26, 2025 15:22
@paldepind paldepind assigned paldepind and hvitved and unassigned paldepind Feb 26, 2025

Choose a reason for hiding this comment

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

PR Overview

This PR expands the data flow testing in the Rust queries to support SSA and mutable borrows while refining the expected test annotations. Key changes include:

  • Splitting intraprocedural tests into immutable and mutable borrow modules with updated function signatures.
  • Modifying methods on types such as MyFlag and MyNumber to take ownership for more precise flow tracking.
  • Updating expected flow and SQL injection annotation markers in several test files.

Reviewed Changes

File Description
rust/ql/test/library-tests/dataflow/pointers/main.rs Added new intraprocedural modules and updated test annotations for pointer and borrow flows.
rust/ql/test/library-tests/dataflow/global/main.rs Changed method receivers from &self to self and updated related test call sites.
rust/ql/test/library-tests/dataflow/local/main.rs Corrected expected flow annotation on iterator tests.
rust/ql/test/library-tests/dataflow/sources/test.rs Adjusted taint flow annotation to the correct expected value.
rust/ql/test/query-tests/security/CWE-089/sqlx.rs Fixed SQL injection alert annotations to correctly reflect expected test markers.

Copilot reviewed 20 out of 20 changed files in this pull request and generated no comments.

Comments suppressed due to low confidence (11)

rust/ql/test/library-tests/dataflow/pointers/main.rs:47

  • The expected value flow annotation for sink(c) in ref_nested_pattern_match is missing; please add '$ hasValueFlow=23' to align the test.
sink(c); // $ MISSING: hasValueFlow=23

rust/ql/test/library-tests/dataflow/pointers/main.rs:84

  • Missing expected value flow annotation for sink(a) in write_through_borrow; add '$ hasValueFlow=39' to meet the test expectations.
sink(a); // $ MISSING: hasValueFlow=39

rust/ql/test/library-tests/dataflow/pointers/main.rs:107

  • The sink(a) call in write_through_borrow_in_match is missing its value flow annotation; please add '$ hasValueFlow=24'.
sink(a); // $ MISSING: hasValueFlow=24

rust/ql/test/library-tests/dataflow/pointers/main.rs:108

  • The sink(b) call in write_through_borrow_in_match is missing its expected annotation; please add '$ hasValueFlow=24'.
sink(b); // $ MISSING: hasValueFlow=24

rust/ql/test/library-tests/dataflow/pointers/main.rs:123

  • The expected annotation for sink(t.1) in mutate_tuple is missing; add '$ hasValueFlow=48' to satisfy the test conditions.
sink(t.1); // $ MISSING: hasValueFlow=48

rust/ql/test/library-tests/dataflow/pointers/main.rs:139

  • The call to sink(a.0) in tuple_match_mut is missing its expected flow annotation; please add '$ hasValueFlow=71'.
sink(a.0); // $ MISSING: hasValueFlow=71

rust/ql/test/library-tests/dataflow/global/main.rs:68

  • [nitpick] Multiple value flow annotations are present on sink(n); please use a single, consistent annotation to avoid confusion.
sink(n); // $ hasValueFlow=1 hasValueFlow=8

rust/ql/test/query-tests/security/CWE-089/sqlx.rs:63

  • The SQL injection alert annotation for unsafe_query_1 is missing; please add 'Alert[rust/sql-injection]=args1'.
let _ = conn.execute(unsafe_query_1.as_str()).await?; // $ sql-sink MISSING: Alert[rust/sql-injection]=args1

rust/ql/test/query-tests/security/CWE-089/sqlx.rs:66

  • Missing SQL injection alert annotation for unsafe_query_3; please add 'Alert[rust/sql-injection]=remote1'.
let _ = conn.execute(unsafe_query_3.as_str()).await?; // $ sql-sink MISSING: Alert[rust/sql-injection]=remote1

rust/ql/test/query-tests/security/CWE-089/sqlx.rs:75

  • The SQL query for unsafe_query_1 is missing the expected alert annotation; please update it with 'Alert[rust/sql-injection]=args1'.
let _ = sqlx::query(unsafe_query_1.as_str()).execute(&pool).await?; // $ sql-sink MISSING: Alert[rust/sql-injection][rust/sql-injection]=args1

rust/ql/test/query-tests/security/CWE-089/sqlx.rs:77

  • Please add the missing SQL injection alert annotation 'Alert[rust/sql-injection]=remote1' for unsafe_query_3 in this SQL query.
let _ = sqlx::query(unsafe_query_3.as_str()).execute(&pool).await?; // $ sql-sink MISSING: Alert[rust/sql-injection]=remote1

Tip: Copilot code review supports C#, Go, Java, JavaScript, Markdown, Python, Ruby and TypeScript, with more languages coming soon. Learn more

| Unexpected summary found: repo::test;<crate::option::MyOption>::get_or_insert_default;Argument[self].Field[crate::option::MyOption::MySome(0)];ReturnValue.Reference;value;dfc-generated |
| Unexpected summary found: repo::test;<crate::option::MyOption>::get_or_insert_with;Argument[self].Field[crate::option::MyOption::MySome(0)];ReturnValue.Reference;value;dfc-generated |
| Unexpected summary found: repo::test;<crate::option::MyOption>::insert;Argument[self].Field[crate::option::MyOption::MySome(0)];ReturnValue.Reference;value;dfc-generated |
| Unexpected summary found: repo::test;<crate::option::MyOption>::take_if;Argument[self].Field[crate::option::MyOption::MySome(0)];Argument[0].Parameter[0].Reference;value;dfc-generated |
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These summaries are incorrect and show up since we now insert implicit borrow/deref in some places where there actually isn't any. Once we can place them more precisely using type information, these should disappear again.

@paldepind
Copy link
Contributor Author

CI is failing, but I think it's only in the way in which it seems to fail for everything right now.

Copy link
Contributor

@hvitved hvitved left a comment

Choose a reason for hiding this comment

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

Really nice work, great to see that it worked out in the end. A few comments.

)
}
}
class SourceVariable = Variable;
Copy link
Contributor

Choose a reason for hiding this comment

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

The QL doc needs to be updated now (or perhaps simply be removed).

(&mut my_number).set(0);
sink(to_number(my_number)); // now cleared
sink(to_number(my_number)); // SPURIOUS: hasValueFlow=99
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing the $ marker.

Comment on lines 383 to 387
predicate allowFlowIntoUncertainDef(UncertainWriteDefinition def) {
exists(Variable v, BasicBlock bb, int i |
def.definesAt(v, bb, i) and mutablyBorrows(bb.getNode(i).getAstNode(), v)
)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

As discussed offline, perhaps this should be restricted to borrows passed into calls, i.e.

Suggested change
predicate allowFlowIntoUncertainDef(UncertainWriteDefinition def) {
exists(Variable v, BasicBlock bb, int i |
def.definesAt(v, bb, i) and mutablyBorrows(bb.getNode(i).getAstNode(), v)
)
}
predicate allowFlowIntoUncertainDef(UncertainWriteDefinition def) {
exists(CfgNodes::CallExprBaseCfgNode call, Variable v, BasicBlock bb, int i |
def.definesAt(v, bb, i) and
mutablyBorrows(bb.getNode(i).getAstNode(), v)
|
call.getArgument(_) = bb.getNode(i)
or
call.(CfgNodes::MethodCallExprCfgNode).getReceiver() = bb.getNode(i)
)
}

ExprArgumentNode() {
isArgumentForCall(n, call_, pos_) and
// For receivers in method calls the `ReceiverNode` is the argument.
not call_.(MethodCallExprCfgNode).getReceiver() = n
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps instead remove the arg = call.(MethodCallExprCfgNode).getReceiver() and pos.isSelf() case inside isArgumentForCall, and then remove this restriction and add a e = any(MethodCallExprCfgNode mc).getReceiver() case to TExprPostUpdateNode.


override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) {
call.asCallBaseExprCfgNode() = call_ and pos = pos_
}
}

/**
* The receiver of a method call _after_ any implicit borrow or dereferences
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* The receiver of a method call _after_ any implicit borrow or dereferences
* The receiver of a method call _after_ any implicit borrow or dereferencing


override CfgScope getCfgScope() { result = n.getAstNode().getEnclosingCfgScope() }

override Location getLocation() { result = n.getLocation() }
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it would be better to use this.getReceiver().getLocation().


override CfgScope getCfgScope() { result = n.getAstNode().getEnclosingCfgScope() }

override Location getLocation() { result = n.getLocation() }
Copy link
Contributor

Choose a reason for hiding this comment

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

Same

@@ -998,6 +1049,23 @@ predicate lambdaCallExpr(CallExprCfgNode call, LambdaCallKind kind, ExprCfgNode
exists(kind)
}

/** Holds if `mc` implicitly borrows its receiver. */
predicate implicitBorrow(MethodCallExpr mc) {
Copy link
Contributor

Choose a reason for hiding this comment

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

private

}

/** Holds if `mc` implicitly dereferences its receiver. */
predicate implicitDeref(MethodCallExpr mc) {
Copy link
Contributor

Choose a reason for hiding this comment

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

private

@paldepind paldepind force-pushed the rust-ref-mut branch 2 times, most recently from 14b1114 to cc2e13e Compare February 28, 2025 10:32
@paldepind
Copy link
Contributor Author

I've addressed the comments :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Rust Pull requests that update Rust code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants