Skip to content

Commit cd6429a

Browse files
authored
Merge pull request #20969 from paldepind/rust/dispath-default-trait
Rust: Do not dispatch to all implementations when trait target is accurate
2 parents 24852c6 + f200dba commit cd6429a

File tree

7 files changed

+170
-21
lines changed

7 files changed

+170
-21
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* The call graph is now more precise for calls that target a trait function with a default implemention. This reduces the number of false positives for data flow queries.

rust/ql/lib/codeql/rust/elements/internal/CallImpl.qll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ module Impl {
6363
}
6464

6565
/** Gets the resolved target of this call, if any. */
66-
Function getStaticTarget() { result = TypeInference::resolveCallTarget(this) }
66+
Function getStaticTarget() { result = TypeInference::resolveCallTarget(this, _) }
6767

6868
/** Gets the name of the function called, if any. */
6969
string getTargetName() { result = this.getStaticTarget().getName().getText() }
@@ -73,9 +73,9 @@ module Impl {
7373
Function getARuntimeTarget() {
7474
result.hasImplementation() and
7575
(
76-
result = this.getStaticTarget()
76+
result = TypeInference::resolveCallTarget(this, _)
7777
or
78-
result.implements(this.getStaticTarget())
78+
result.implements(TypeInference::resolveCallTarget(this, true))
7979
)
8080
}
8181
}

rust/ql/lib/codeql/rust/elements/internal/InvocationExprImpl.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,6 @@ module Impl {
9494
}
9595

9696
/** Gets the resolved target (function or tuple struct/variant), if any. */
97-
Addressable getResolvedTarget() { result = TypeInference::resolveCallTarget(this) }
97+
Addressable getResolvedTarget() { result = TypeInference::resolveCallTarget(this, _) }
9898
}
9999
}

rust/ql/lib/codeql/rust/internal/TypeInference.qll

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3522,12 +3522,27 @@ private module Cached {
35223522
any(MethodResolution::MethodCall mc).argumentHasImplicitBorrow(n)
35233523
}
35243524

3525-
/** Gets an item (function or tuple struct/variant) that `call` resolves to, if any. */
3525+
/**
3526+
* Gets an item (function or tuple struct/variant) that `call` resolves to, if
3527+
* any.
3528+
*
3529+
* The parameter `dispatch` is `true` if and only if the resolved target is a
3530+
* trait item because a precise target could not be determined from the
3531+
* types (for instance in the presence of generics or `dyn` types)
3532+
*/
35263533
cached
3527-
Addressable resolveCallTarget(Expr call) {
3528-
result = call.(MethodResolution::MethodCall).resolveCallTarget(_, _, _)
3534+
Addressable resolveCallTarget(InvocationExpr call, boolean dispatch) {
3535+
dispatch = false and
3536+
result = call.(NonMethodResolution::NonMethodCall).resolveCallTargetViaPathResolution()
35293537
or
3530-
result = call.(NonMethodResolution::NonMethodCall).resolveCallTarget()
3538+
exists(ImplOrTraitItemNode i |
3539+
i instanceof TraitItemNode and dispatch = true
3540+
or
3541+
i instanceof ImplItemNode and dispatch = false
3542+
|
3543+
result = call.(MethodResolution::MethodCall).resolveCallTarget(i, _, _) or
3544+
result = call.(NonMethodResolution::NonMethodCall).resolveCallTargetViaTypeInference(i)
3545+
)
35313546
}
35323547

35333548
/**
@@ -3668,9 +3683,9 @@ private module Debug {
36683683
result = inferType(n, path)
36693684
}
36703685

3671-
Addressable debugResolveCallTarget(InvocationExpr c) {
3686+
Addressable debugResolveCallTarget(InvocationExpr c, boolean dispatch) {
36723687
c = getRelevantLocatable() and
3673-
result = resolveCallTarget(c)
3688+
result = resolveCallTarget(c, dispatch)
36743689
}
36753690

36763691
predicate debugConditionSatisfiesConstraint(

rust/ql/test/library-tests/dataflow/global/inline-flow.expected

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,27 @@ edges
187187
| main.rs:334:9:334:9 | a | main.rs:335:10:335:10 | a | provenance | |
188188
| main.rs:334:13:334:55 | ...::block_on(...) | main.rs:334:9:334:9 | a | provenance | |
189189
| main.rs:334:41:334:54 | async_source(...) | main.rs:334:13:334:55 | ...::block_on(...) | provenance | MaD:2 |
190+
| main.rs:346:44:348:9 | { ... } | main.rs:383:18:383:38 | t.get_double_number() | provenance | |
191+
| main.rs:346:44:348:9 | { ... } | main.rs:387:18:387:50 | ...::get_double_number(...) | provenance | |
192+
| main.rs:347:13:347:29 | self.get_number() | main.rs:346:44:348:9 | { ... } | provenance | |
193+
| main.rs:350:33:352:9 | { ... } | main.rs:391:18:391:37 | ...::get_default(...) | provenance | |
194+
| main.rs:351:13:351:21 | source(...) | main.rs:350:33:352:9 | { ... } | provenance | |
195+
| main.rs:358:37:360:9 | { ... } | main.rs:347:13:347:29 | self.get_number() | provenance | |
196+
| main.rs:359:13:359:21 | source(...) | main.rs:358:37:360:9 | { ... } | provenance | |
197+
| main.rs:370:44:372:9 | { ... } | main.rs:395:18:395:38 | i.get_double_number() | provenance | |
198+
| main.rs:371:13:371:22 | source(...) | main.rs:370:44:372:9 | { ... } | provenance | |
199+
| main.rs:374:33:376:9 | { ... } | main.rs:398:18:398:41 | ...::get_default(...) | provenance | |
200+
| main.rs:375:13:375:21 | source(...) | main.rs:374:33:376:9 | { ... } | provenance | |
201+
| main.rs:383:13:383:14 | n1 | main.rs:384:14:384:15 | n1 | provenance | |
202+
| main.rs:383:18:383:38 | t.get_double_number() | main.rs:383:13:383:14 | n1 | provenance | |
203+
| main.rs:387:13:387:14 | n2 | main.rs:388:14:388:15 | n2 | provenance | |
204+
| main.rs:387:18:387:50 | ...::get_double_number(...) | main.rs:387:13:387:14 | n2 | provenance | |
205+
| main.rs:391:13:391:14 | n3 | main.rs:392:14:392:15 | n3 | provenance | |
206+
| main.rs:391:18:391:37 | ...::get_default(...) | main.rs:391:13:391:14 | n3 | provenance | |
207+
| main.rs:395:13:395:14 | n4 | main.rs:396:14:396:15 | n4 | provenance | |
208+
| main.rs:395:18:395:38 | i.get_double_number() | main.rs:395:13:395:14 | n4 | provenance | |
209+
| main.rs:398:13:398:14 | n5 | main.rs:399:14:399:15 | n5 | provenance | |
210+
| main.rs:398:18:398:41 | ...::get_default(...) | main.rs:398:13:398:14 | n5 | provenance | |
190211
nodes
191212
| main.rs:12:28:14:1 | { ... } | semmle.label | { ... } |
192213
| main.rs:13:5:13:13 | source(...) | semmle.label | source(...) |
@@ -391,6 +412,31 @@ nodes
391412
| main.rs:334:13:334:55 | ...::block_on(...) | semmle.label | ...::block_on(...) |
392413
| main.rs:334:41:334:54 | async_source(...) | semmle.label | async_source(...) |
393414
| main.rs:335:10:335:10 | a | semmle.label | a |
415+
| main.rs:346:44:348:9 | { ... } | semmle.label | { ... } |
416+
| main.rs:347:13:347:29 | self.get_number() | semmle.label | self.get_number() |
417+
| main.rs:350:33:352:9 | { ... } | semmle.label | { ... } |
418+
| main.rs:351:13:351:21 | source(...) | semmle.label | source(...) |
419+
| main.rs:358:37:360:9 | { ... } | semmle.label | { ... } |
420+
| main.rs:359:13:359:21 | source(...) | semmle.label | source(...) |
421+
| main.rs:370:44:372:9 | { ... } | semmle.label | { ... } |
422+
| main.rs:371:13:371:22 | source(...) | semmle.label | source(...) |
423+
| main.rs:374:33:376:9 | { ... } | semmle.label | { ... } |
424+
| main.rs:375:13:375:21 | source(...) | semmle.label | source(...) |
425+
| main.rs:383:13:383:14 | n1 | semmle.label | n1 |
426+
| main.rs:383:18:383:38 | t.get_double_number() | semmle.label | t.get_double_number() |
427+
| main.rs:384:14:384:15 | n1 | semmle.label | n1 |
428+
| main.rs:387:13:387:14 | n2 | semmle.label | n2 |
429+
| main.rs:387:18:387:50 | ...::get_double_number(...) | semmle.label | ...::get_double_number(...) |
430+
| main.rs:388:14:388:15 | n2 | semmle.label | n2 |
431+
| main.rs:391:13:391:14 | n3 | semmle.label | n3 |
432+
| main.rs:391:18:391:37 | ...::get_default(...) | semmle.label | ...::get_default(...) |
433+
| main.rs:392:14:392:15 | n3 | semmle.label | n3 |
434+
| main.rs:395:13:395:14 | n4 | semmle.label | n4 |
435+
| main.rs:395:18:395:38 | i.get_double_number() | semmle.label | i.get_double_number() |
436+
| main.rs:396:14:396:15 | n4 | semmle.label | n4 |
437+
| main.rs:398:13:398:14 | n5 | semmle.label | n5 |
438+
| main.rs:398:18:398:41 | ...::get_default(...) | semmle.label | ...::get_default(...) |
439+
| main.rs:399:14:399:15 | n5 | semmle.label | n5 |
394440
subpaths
395441
| main.rs:38:23:38:31 | source(...) | main.rs:26:28:26:33 | ...: i64 | main.rs:26:17:26:25 | SelfParam [Return] [&ref, MyStruct] | main.rs:38:6:38:11 | [post] &mut a [&ref, MyStruct] |
396442
| main.rs:39:10:39:10 | a [MyStruct] | main.rs:30:17:30:21 | SelfParam [&ref, MyStruct] | main.rs:30:31:32:5 | { ... } | main.rs:39:10:39:21 | a.get_data() |
@@ -442,3 +488,8 @@ testFailures
442488
| main.rs:317:10:317:10 | a | main.rs:316:13:316:21 | source(...) | main.rs:317:10:317:10 | a | $@ | main.rs:316:13:316:21 | source(...) | source(...) |
443489
| main.rs:327:14:327:14 | c | main.rs:326:17:326:25 | source(...) | main.rs:327:14:327:14 | c | $@ | main.rs:326:17:326:25 | source(...) | source(...) |
444490
| main.rs:335:10:335:10 | a | main.rs:316:13:316:21 | source(...) | main.rs:335:10:335:10 | a | $@ | main.rs:316:13:316:21 | source(...) | source(...) |
491+
| main.rs:384:14:384:15 | n1 | main.rs:359:13:359:21 | source(...) | main.rs:384:14:384:15 | n1 | $@ | main.rs:359:13:359:21 | source(...) | source(...) |
492+
| main.rs:388:14:388:15 | n2 | main.rs:359:13:359:21 | source(...) | main.rs:388:14:388:15 | n2 | $@ | main.rs:359:13:359:21 | source(...) | source(...) |
493+
| main.rs:392:14:392:15 | n3 | main.rs:351:13:351:21 | source(...) | main.rs:392:14:392:15 | n3 | $@ | main.rs:351:13:351:21 | source(...) | source(...) |
494+
| main.rs:396:14:396:15 | n4 | main.rs:371:13:371:22 | source(...) | main.rs:396:14:396:15 | n4 | $@ | main.rs:371:13:371:22 | source(...) | source(...) |
495+
| main.rs:399:14:399:15 | n5 | main.rs:375:13:375:21 | source(...) | main.rs:399:14:399:15 | n5 | $@ | main.rs:375:13:375:21 | source(...) | source(...) |

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

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,69 @@ fn test_async_await() {
337337
futures::executor::block_on(test_async_await_async_part());
338338
}
339339

340+
mod not_trait_dispatch {
341+
use super::{sink, source};
342+
343+
trait HasNumbers {
344+
fn get_number(&self) -> i64;
345+
346+
fn get_double_number(&self) -> i64 {
347+
self.get_number() * 2
348+
}
349+
350+
fn get_default() -> i64 {
351+
source(0)
352+
}
353+
}
354+
355+
struct Three;
356+
357+
impl HasNumbers for Three {
358+
fn get_number(&self) -> i64 {
359+
source(3)
360+
}
361+
}
362+
363+
struct TwentyTwo;
364+
365+
impl HasNumbers for TwentyTwo {
366+
fn get_number(&self) -> i64 {
367+
22
368+
}
369+
370+
fn get_double_number(&self) -> i64 {
371+
source(44)
372+
}
373+
374+
fn get_default() -> i64 {
375+
source(1)
376+
}
377+
}
378+
379+
fn test_non_trait_dispatch() {
380+
let t = Three;
381+
382+
// This call is to the default method implementation.
383+
let n1 = t.get_double_number();
384+
sink(n1); // $ hasTaintFlow=3
385+
386+
// This call is to the default method implementation.
387+
let n2 = HasNumbers::get_double_number(&t);
388+
sink(n2); // $ hasTaintFlow=3
389+
390+
// This call is to the default function implementation.
391+
let n3 = Three::get_default();
392+
sink(n3); // $ hasValueFlow=0
393+
394+
let i = TwentyTwo;
395+
let n4 = i.get_double_number();
396+
sink(n4); // $ hasValueFlow=44
397+
398+
let n5 = TwentyTwo::get_default();
399+
sink(n5); // $ hasValueFlow=1
400+
}
401+
}
402+
340403
fn main() {
341404
data_out_of_call();
342405
data_out_of_call_side_effect1();

rust/ql/test/library-tests/dataflow/global/viableCallable.expected

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -105,14 +105,30 @@
105105
| main.rs:335:5:335:11 | sink(...) | main.rs:5:1:7:1 | fn sink |
106106
| main.rs:337:5:337:62 | ...::block_on(...) | {EXTERNAL LOCATION} | fn block_on |
107107
| main.rs:337:33:337:61 | test_async_await_async_part(...) | main.rs:321:1:331:1 | fn test_async_await_async_part |
108-
| main.rs:341:5:341:22 | data_out_of_call(...) | main.rs:16:1:19:1 | fn data_out_of_call |
109-
| main.rs:342:5:342:35 | data_out_of_call_side_effect1(...) | main.rs:35:1:40:1 | fn data_out_of_call_side_effect1 |
110-
| main.rs:343:5:343:35 | data_out_of_call_side_effect2(...) | main.rs:42:1:50:1 | fn data_out_of_call_side_effect2 |
111-
| main.rs:344:5:344:21 | data_in_to_call(...) | main.rs:56:1:59:1 | fn data_in_to_call |
112-
| main.rs:345:5:345:23 | data_through_call(...) | main.rs:65:1:69:1 | fn data_through_call |
113-
| main.rs:346:5:346:34 | data_through_nested_function(...) | main.rs:79:1:88:1 | fn data_through_nested_function |
114-
| main.rs:348:5:348:24 | data_out_of_method(...) | main.rs:136:1:146:1 | fn data_out_of_method |
115-
| main.rs:349:5:349:28 | data_in_to_method_call(...) | main.rs:153:1:163:1 | fn data_in_to_method_call |
116-
| main.rs:350:5:350:25 | data_through_method(...) | main.rs:171:1:183:1 | fn data_through_method |
117-
| main.rs:352:5:352:31 | test_operator_overloading(...) | main.rs:240:1:278:1 | fn test_operator_overloading |
118-
| main.rs:353:5:353:22 | test_async_await(...) | main.rs:333:1:338:1 | fn test_async_await |
108+
| main.rs:347:13:347:29 | self.get_number() | main.rs:358:9:360:9 | fn get_number |
109+
| main.rs:347:13:347:29 | self.get_number() | main.rs:366:9:368:9 | fn get_number |
110+
| main.rs:351:13:351:21 | source(...) | main.rs:1:1:3:1 | fn source |
111+
| main.rs:359:13:359:21 | source(...) | main.rs:1:1:3:1 | fn source |
112+
| main.rs:371:13:371:22 | source(...) | main.rs:1:1:3:1 | fn source |
113+
| main.rs:375:13:375:21 | source(...) | main.rs:1:1:3:1 | fn source |
114+
| main.rs:383:18:383:38 | t.get_double_number() | main.rs:346:9:348:9 | fn get_double_number |
115+
| main.rs:384:9:384:16 | sink(...) | main.rs:5:1:7:1 | fn sink |
116+
| main.rs:387:18:387:50 | ...::get_double_number(...) | main.rs:346:9:348:9 | fn get_double_number |
117+
| main.rs:388:9:388:16 | sink(...) | main.rs:5:1:7:1 | fn sink |
118+
| main.rs:391:18:391:37 | ...::get_default(...) | main.rs:350:9:352:9 | fn get_default |
119+
| main.rs:392:9:392:16 | sink(...) | main.rs:5:1:7:1 | fn sink |
120+
| main.rs:395:18:395:38 | i.get_double_number() | main.rs:370:9:372:9 | fn get_double_number |
121+
| main.rs:396:9:396:16 | sink(...) | main.rs:5:1:7:1 | fn sink |
122+
| main.rs:398:18:398:41 | ...::get_default(...) | main.rs:374:9:376:9 | fn get_default |
123+
| main.rs:399:9:399:16 | sink(...) | main.rs:5:1:7:1 | fn sink |
124+
| main.rs:404:5:404:22 | data_out_of_call(...) | main.rs:16:1:19:1 | fn data_out_of_call |
125+
| main.rs:405:5:405:35 | data_out_of_call_side_effect1(...) | main.rs:35:1:40:1 | fn data_out_of_call_side_effect1 |
126+
| main.rs:406:5:406:35 | data_out_of_call_side_effect2(...) | main.rs:42:1:50:1 | fn data_out_of_call_side_effect2 |
127+
| main.rs:407:5:407:21 | data_in_to_call(...) | main.rs:56:1:59:1 | fn data_in_to_call |
128+
| main.rs:408:5:408:23 | data_through_call(...) | main.rs:65:1:69:1 | fn data_through_call |
129+
| main.rs:409:5:409:34 | data_through_nested_function(...) | main.rs:79:1:88:1 | fn data_through_nested_function |
130+
| main.rs:411:5:411:24 | data_out_of_method(...) | main.rs:136:1:146:1 | fn data_out_of_method |
131+
| main.rs:412:5:412:28 | data_in_to_method_call(...) | main.rs:153:1:163:1 | fn data_in_to_method_call |
132+
| main.rs:413:5:413:25 | data_through_method(...) | main.rs:171:1:183:1 | fn data_through_method |
133+
| main.rs:415:5:415:31 | test_operator_overloading(...) | main.rs:240:1:278:1 | fn test_operator_overloading |
134+
| main.rs:416:5:416:22 | test_async_await(...) | main.rs:333:1:338:1 | fn test_async_await |

0 commit comments

Comments
 (0)