Merge pull request #14482 from MathiasVP/additional-call-targets-for-cpp (#23)

C++: Add an abstract class that can be used to extend `viableCallable`

Co-authored-by: Mathias Vorreiter Pedersen <mathiasvp@github.com>
This commit is contained in:
Ben Rodes
2023-10-23 10:12:11 -04:00
committed by GitHub
parent 1cdf4aafb0
commit d9364c060e
6 changed files with 98 additions and 1 deletions

View File

@@ -0,0 +1,4 @@
---
category: feature
---
* Added a new class `AdditionalCallTarget` for specifying additional call targets.

View File

@@ -7,9 +7,12 @@ private import DataFlowImplCommon as DataFlowImplCommon
/** /**
* Gets a function that might be called by `call`. * Gets a function that might be called by `call`.
*
* This predicate does not take additional call targets
* from `AdditionalCallTarget` into account.
*/ */
cached cached
DataFlowCallable viableCallable(DataFlowCall call) { DataFlowCallable defaultViableCallable(DataFlowCall call) {
DataFlowImplCommon::forceCachingInSameStage() and DataFlowImplCommon::forceCachingInSameStage() and
result = call.getStaticCallTarget() result = call.getStaticCallTarget()
or or
@@ -29,6 +32,17 @@ DataFlowCallable viableCallable(DataFlowCall call) {
result = call.(VirtualDispatch::DataSensitiveCall).resolve() result = call.(VirtualDispatch::DataSensitiveCall).resolve()
} }
/**
* Gets a function that might be called by `call`.
*/
cached
DataFlowCallable viableCallable(DataFlowCall call) {
result = defaultViableCallable(call)
or
// Additional call targets
result = any(AdditionalCallTarget additional).viableTarget(call.getUnconvertedResultExpression())
}
/** /**
* Provides virtual dispatch support compatible with the original * Provides virtual dispatch support compatible with the original
* implementation of `semmle.code.cpp.security.TaintTracking`. * implementation of `semmle.code.cpp.security.TaintTracking`.

View File

@@ -14,6 +14,7 @@ private import DataFlowPrivate
private import ModelUtil private import ModelUtil
private import SsaInternals as Ssa private import SsaInternals as Ssa
private import DataFlowImplCommon as DataFlowImplCommon private import DataFlowImplCommon as DataFlowImplCommon
private import codeql.util.Unit
/** /**
* The IR dataflow graph consists of the following nodes: * The IR dataflow graph consists of the following nodes:
@@ -2237,3 +2238,43 @@ module InstructionBarrierGuard<instructionGuardChecksSig/3 instructionGuardCheck
) )
} }
} }
/**
* A unit class for adding additional call steps.
*
* Extend this class to add additional call steps to the data flow graph.
*
* For example, if the following subclass is added:
* ```ql
* class MyAdditionalCallTarget extends DataFlow::AdditionalCallTarget {
* override Function viableTarget(Call call) {
* call.getTarget().hasName("f") and
* result.hasName("g")
* }
* }
* ```
* then flow from `source()` to `x` in `sink(x)` is reported in the following example:
* ```cpp
* void sink(int);
* int source();
* void f(int);
*
* void g(int x) {
* sink(x);
* }
*
* void test() {
* int x = source();
* f(x);
* }
* ```
*
* Note: To prevent reevaluation of cached dataflow-related predicates any
* subclass of `AdditionalCallTarget` must be imported in all dataflow queries.
*/
class AdditionalCallTarget extends Unit {
/**
* Gets a viable target for `call`.
*/
abstract DataFlowCallable viableTarget(Call call);
}

View File

@@ -1,6 +1,19 @@
private import semmle.code.cpp.ir.dataflow.DataFlow private import semmle.code.cpp.ir.dataflow.DataFlow
private import DataFlow private import DataFlow
private class TestAdditionalCallTarget extends AdditionalCallTarget {
override Function viableTarget(Call call) {
// To test that call targets specified by `AdditionalCallTarget` are
// resolved correctly this subclass resolves all calls to
// `call_template_argument<f>(x)` as if the user had written `f(x)`.
exists(FunctionTemplateInstantiation inst |
inst.getTemplate().hasName("call_template_argument") and
call.getTarget() = inst and
result = inst.getTemplateArgument(0).(FunctionAccess).getTarget()
)
}
}
module IRConfig implements ConfigSig { module IRConfig implements ConfigSig {
predicate isSource(Node src) { predicate isSource(Node src) {
src.asExpr() instanceof NewExpr src.asExpr() instanceof NewExpr

View File

@@ -770,6 +770,9 @@ edges
| simple.cpp:92:7:92:7 | a indirection [post update] [i] | simple.cpp:94:10:94:11 | a2 indirection [i] | | simple.cpp:92:7:92:7 | a indirection [post update] [i] | simple.cpp:94:10:94:11 | a2 indirection [i] |
| simple.cpp:92:11:92:20 | call to user_input | simple.cpp:92:5:92:22 | ... = ... | | simple.cpp:92:11:92:20 | call to user_input | simple.cpp:92:5:92:22 | ... = ... |
| simple.cpp:94:10:94:11 | a2 indirection [i] | simple.cpp:94:13:94:13 | i | | simple.cpp:94:10:94:11 | a2 indirection [i] | simple.cpp:94:13:94:13 | i |
| simple.cpp:103:24:103:24 | x | simple.cpp:104:14:104:14 | x |
| simple.cpp:108:17:108:26 | call to user_input | simple.cpp:109:43:109:43 | x |
| simple.cpp:109:43:109:43 | x | simple.cpp:103:24:103:24 | x |
| struct_init.c:14:24:14:25 | ab indirection [a] | struct_init.c:15:8:15:9 | ab indirection [a] | | struct_init.c:14:24:14:25 | ab indirection [a] | struct_init.c:15:8:15:9 | ab indirection [a] |
| struct_init.c:15:8:15:9 | ab indirection [a] | struct_init.c:15:12:15:12 | a | | struct_init.c:15:8:15:9 | ab indirection [a] | struct_init.c:15:12:15:12 | a |
| struct_init.c:20:13:20:14 | definition of ab indirection [a] | struct_init.c:22:8:22:9 | ab indirection [a] | | 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
| simple.cpp:92:11:92:20 | call to user_input | semmle.label | call to user_input | | simple.cpp:92:11:92:20 | call to user_input | semmle.label | call to user_input |
| simple.cpp:94:10:94:11 | a2 indirection [i] | semmle.label | a2 indirection [i] | | simple.cpp:94:10:94:11 | a2 indirection [i] | semmle.label | a2 indirection [i] |
| simple.cpp:94:13:94:13 | i | semmle.label | i | | simple.cpp:94:13:94:13 | i | semmle.label | i |
| simple.cpp:103:24:103:24 | x | semmle.label | x |
| simple.cpp:104:14:104:14 | x | semmle.label | x |
| simple.cpp:108:17:108:26 | call to user_input | semmle.label | call to user_input |
| simple.cpp:109:43:109:43 | x | semmle.label | x |
| struct_init.c:14:24:14:25 | ab indirection [a] | semmle.label | ab indirection [a] | | struct_init.c:14:24:14:25 | ab indirection [a] | semmle.label | ab indirection [a] |
| struct_init.c:15:8:15:9 | ab indirection [a] | semmle.label | ab indirection [a] | | struct_init.c:15:8:15:9 | ab indirection [a] | semmle.label | ab indirection [a] |
| struct_init.c:15:12:15:12 | a | semmle.label | a | | struct_init.c:15:12:15:12 | a | semmle.label | a |
@@ -1782,6 +1789,7 @@ subpaths
| 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 | | 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 |
| 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 | | 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 |
| 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 | | 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 |
| 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 |
| 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 | | 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 |
| 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 | | 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 |
| 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 | | 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 |

View File

@@ -94,4 +94,21 @@ void single_field_test_typedef(A_typedef a)
sink(a2.i); //$ ast,ir sink(a2.i); //$ ast,ir
} }
namespace TestAdditionalCallTargets {
using TakesIntReturnsVoid = void(*)(int);
template<TakesIntReturnsVoid F>
void call_template_argument(int);
void call_sink(int x) {
sink(x); // $ ir
}
void test_additional_call_targets() {
int x = user_input();
call_template_argument<call_sink>(x);
}
}
} // namespace Simple } // namespace Simple