Skip to content

Commit bbf9bcd

Browse files
authored
Merge pull request github#14482 from MathiasVP/additional-call-targets-for-cpp
C++: Add an abstract class that can be used to extend `viableCallable`
2 parents 401639e + 9a2c1da commit bbf9bcd

File tree

6 files changed

+98
-1
lines changed

6 files changed

+98
-1
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: feature
3+
---
4+
* Added a new class `AdditionalCallTarget` for specifying additional call targets.

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@ private import DataFlowImplCommon as DataFlowImplCommon
77

88
/**
99
* Gets a function that might be called by `call`.
10+
*
11+
* This predicate does not take additional call targets
12+
* from `AdditionalCallTarget` into account.
1013
*/
1114
cached
12-
DataFlowCallable viableCallable(DataFlowCall call) {
15+
DataFlowCallable defaultViableCallable(DataFlowCall call) {
1316
DataFlowImplCommon::forceCachingInSameStage() and
1417
result = call.getStaticCallTarget()
1518
or
@@ -29,6 +32,17 @@ DataFlowCallable viableCallable(DataFlowCall call) {
2932
result = call.(VirtualDispatch::DataSensitiveCall).resolve()
3033
}
3134

35+
/**
36+
* Gets a function that might be called by `call`.
37+
*/
38+
cached
39+
DataFlowCallable viableCallable(DataFlowCall call) {
40+
result = defaultViableCallable(call)
41+
or
42+
// Additional call targets
43+
result = any(AdditionalCallTarget additional).viableTarget(call.getUnconvertedResultExpression())
44+
}
45+
3246
/**
3347
* Provides virtual dispatch support compatible with the original
3448
* implementation of `semmle.code.cpp.security.TaintTracking`.

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ private import DataFlowPrivate
1414
private import ModelUtil
1515
private import SsaInternals as Ssa
1616
private import DataFlowImplCommon as DataFlowImplCommon
17+
private import codeql.util.Unit
1718

1819
/**
1920
* The IR dataflow graph consists of the following nodes:
@@ -2237,3 +2238,43 @@ module InstructionBarrierGuard<instructionGuardChecksSig/3 instructionGuardCheck
22372238
)
22382239
}
22392240
}
2241+
2242+
/**
2243+
* A unit class for adding additional call steps.
2244+
*
2245+
* Extend this class to add additional call steps to the data flow graph.
2246+
*
2247+
* For example, if the following subclass is added:
2248+
* ```ql
2249+
* class MyAdditionalCallTarget extends DataFlow::AdditionalCallTarget {
2250+
* override Function viableTarget(Call call) {
2251+
* call.getTarget().hasName("f") and
2252+
* result.hasName("g")
2253+
* }
2254+
* }
2255+
* ```
2256+
* then flow from `source()` to `x` in `sink(x)` is reported in the following example:
2257+
* ```cpp
2258+
* void sink(int);
2259+
* int source();
2260+
* void f(int);
2261+
*
2262+
* void g(int x) {
2263+
* sink(x);
2264+
* }
2265+
*
2266+
* void test() {
2267+
* int x = source();
2268+
* f(x);
2269+
* }
2270+
* ```
2271+
*
2272+
* Note: To prevent reevaluation of cached dataflow-related predicates any
2273+
* subclass of `AdditionalCallTarget` must be imported in all dataflow queries.
2274+
*/
2275+
class AdditionalCallTarget extends Unit {
2276+
/**
2277+
* Gets a viable target for `call`.
2278+
*/
2279+
abstract DataFlowCallable viableTarget(Call call);
2280+
}

cpp/ql/test/library-tests/dataflow/fields/IRConfiguration.qll

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
private import semmle.code.cpp.ir.dataflow.DataFlow
22
private import DataFlow
33

4+
private class TestAdditionalCallTarget extends AdditionalCallTarget {
5+
override Function viableTarget(Call call) {
6+
// To test that call targets specified by `AdditionalCallTarget` are
7+
// resolved correctly this subclass resolves all calls to
8+
// `call_template_argument<f>(x)` as if the user had written `f(x)`.
9+
exists(FunctionTemplateInstantiation inst |
10+
inst.getTemplate().hasName("call_template_argument") and
11+
call.getTarget() = inst and
12+
result = inst.getTemplateArgument(0).(FunctionAccess).getTarget()
13+
)
14+
}
15+
}
16+
417
module IRConfig implements ConfigSig {
518
predicate isSource(Node src) {
619
src.asExpr() instanceof NewExpr

cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,9 @@ edges
770770
| simple.cpp:92:7:92:7 | a indirection [post update] [i] | simple.cpp:94:10:94:11 | a2 indirection [i] |
771771
| simple.cpp:92:11:92:20 | call to user_input | simple.cpp:92:5:92:22 | ... = ... |
772772
| simple.cpp:94:10:94:11 | a2 indirection [i] | simple.cpp:94:13:94:13 | i |
773+
| simple.cpp:103:24:103:24 | x | simple.cpp:104:14:104:14 | x |
774+
| simple.cpp:108:17:108:26 | call to user_input | simple.cpp:109:43:109:43 | x |
775+
| simple.cpp:109:43:109:43 | x | simple.cpp:103:24:103:24 | x |
773776
| struct_init.c:14:24:14:25 | ab indirection [a] | struct_init.c:15:8:15:9 | ab indirection [a] |
774777
| struct_init.c:15:8:15:9 | ab indirection [a] | struct_init.c:15:12:15:12 | a |
775778
| struct_init.c:20:13:20:14 | definition of ab indirection [a] | struct_init.c:22:8:22:9 | ab indirection [a] |
@@ -1576,6 +1579,10 @@ nodes
15761579
| simple.cpp:92:11:92:20 | call to user_input | semmle.label | call to user_input |
15771580
| simple.cpp:94:10:94:11 | a2 indirection [i] | semmle.label | a2 indirection [i] |
15781581
| simple.cpp:94:13:94:13 | i | semmle.label | i |
1582+
| simple.cpp:103:24:103:24 | x | semmle.label | x |
1583+
| simple.cpp:104:14:104:14 | x | semmle.label | x |
1584+
| simple.cpp:108:17:108:26 | call to user_input | semmle.label | call to user_input |
1585+
| simple.cpp:109:43:109:43 | x | semmle.label | x |
15791586
| struct_init.c:14:24:14:25 | ab indirection [a] | semmle.label | ab indirection [a] |
15801587
| struct_init.c:15:8:15:9 | ab indirection [a] | semmle.label | ab indirection [a] |
15811588
| struct_init.c:15:12:15:12 | a | semmle.label | a |
@@ -1782,6 +1789,7 @@ subpaths
17821789
| simple.cpp:67:13:67:13 | i | simple.cpp:65:11:65:20 | call to user_input | simple.cpp:67:13:67:13 | i | i flows from $@ | simple.cpp:65:11:65:20 | call to user_input | call to user_input |
17831790
| simple.cpp:84:14:84:20 | call to getf2f1 | simple.cpp:83:17:83:26 | call to user_input | simple.cpp:84:14:84:20 | call to getf2f1 | call to getf2f1 flows from $@ | simple.cpp:83:17:83:26 | call to user_input | call to user_input |
17841791
| simple.cpp:94:13:94:13 | i | simple.cpp:92:11:92:20 | call to user_input | simple.cpp:94:13:94:13 | i | i flows from $@ | simple.cpp:92:11:92:20 | call to user_input | call to user_input |
1792+
| simple.cpp:104:14:104:14 | x | simple.cpp:108:17:108:26 | call to user_input | simple.cpp:104:14:104:14 | x | x flows from $@ | simple.cpp:108:17:108:26 | call to user_input | call to user_input |
17851793
| struct_init.c:15:12:15:12 | a | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:20:20:20:29 | call to user_input | call to user_input |
17861794
| struct_init.c:15:12:15:12 | a | struct_init.c:27:7:27:16 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:27:7:27:16 | call to user_input | call to user_input |
17871795
| struct_init.c:15:12:15:12 | a | struct_init.c:40:20:40:29 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:40:20:40:29 | call to user_input | call to user_input |

cpp/ql/test/library-tests/dataflow/fields/simple.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,21 @@ void single_field_test_typedef(A_typedef a)
9494
sink(a2.i); //$ ast,ir
9595
}
9696

97+
namespace TestAdditionalCallTargets {
98+
99+
using TakesIntReturnsVoid = void(*)(int);
100+
template<TakesIntReturnsVoid F>
101+
void call_template_argument(int);
102+
103+
void call_sink(int x) {
104+
sink(x); // $ ir
105+
}
106+
107+
void test_additional_call_targets() {
108+
int x = user_input();
109+
call_template_argument<call_sink>(x);
110+
}
111+
112+
}
113+
97114
} // namespace Simple

0 commit comments

Comments
 (0)