Merge pull request #5052 from tamasvajk/feature/fnptr-df

C#: Add data flow 'getARuntimeTarget' predicate to 'FunctionPointerCall'
This commit is contained in:
Tamás Vajk
2021-02-04 08:51:03 +01:00
committed by GitHub
18 changed files with 243 additions and 74 deletions

View File

@@ -63,7 +63,7 @@ predicate mayEscape(LocalVariable v) {
exists(Callable c, Expr e, Expr succ | c = getACapturingCallableAncestor(v) |
e = getADelegateExpr(c) and
DataFlow::localExprFlow(e, succ) and
not succ = any(DelegateCall dc).getDelegateExpr() and
not succ = any(DelegateCall dc).getExpr() and
not succ = any(Cast cast).getExpr() and
not succ = any(Call call | nonEscapingCall(call)).getAnArgument() and
not succ = any(AssignableDefinition ad | ad.getTarget() instanceof LocalVariable).getSource()

View File

@@ -97,9 +97,7 @@ private class TranslatedDelegateInvokeCall extends TranslatedCompilerGeneratedCa
)
}
override TranslatedExprBase getQualifier() {
result = getTranslatedExpr(generatedBy.getDelegateExpr())
}
override TranslatedExprBase getQualifier() { result = getTranslatedExpr(generatedBy.getExpr()) }
override Instruction getQualifierResult() { result = getQualifier().getResult() }

View File

@@ -11,7 +11,8 @@ cached
private newtype TCallContext =
TEmptyCallContext() or
TArgNonDelegateCallContext(Expr arg) { exists(DispatchCall dc | arg = dc.getArgument(_)) } or
TArgDelegateCallContext(DelegateCall dc, int i) { exists(dc.getArgument(i)) }
TArgDelegateCallContext(DelegateCall dc, int i) { exists(dc.getArgument(i)) } or
TArgFunctionPointerCallContext(FunctionPointerCall fptrc, int i) { exists(fptrc.getArgument(i)) }
/**
* A call context.
@@ -60,12 +61,14 @@ class NonDelegateCallArgumentCallContext extends ArgumentCallContext, TArgNonDel
override Location getLocation() { result = arg.getLocation() }
}
/** An argument of a delegate call. */
class DelegateCallArgumentCallContext extends ArgumentCallContext, TArgDelegateCallContext {
DelegateCall dc;
/** An argument of a delegate or function pointer call. */
class DelegateLikeCallArgumentCallContext extends ArgumentCallContext {
DelegateLikeCall dc;
int arg;
DelegateCallArgumentCallContext() { this = TArgDelegateCallContext(dc, arg) }
DelegateLikeCallArgumentCallContext() {
this = TArgDelegateCallContext(dc, arg) or this = TArgFunctionPointerCallContext(dc, arg)
}
override predicate isArgument(Expr call, int i) {
call = dc and
@@ -76,3 +79,11 @@ class DelegateCallArgumentCallContext extends ArgumentCallContext, TArgDelegateC
override Location getLocation() { result = dc.getArgument(arg).getLocation() }
}
/** An argument of a delegate call. */
class DelegateCallArgumentCallContext extends DelegateLikeCallArgumentCallContext,
TArgDelegateCallContext { }
/** An argument of a function pointer call. */
class FunctionPointerCallArgumentCallContext extends DelegateLikeCallArgumentCallContext,
TArgFunctionPointerCallContext { }

View File

@@ -101,7 +101,7 @@ private module Cached {
TNonDelegateCall(ControlFlow::Nodes::ElementNode cfn, DispatchCall dc) {
cfn.getElement() = dc.getCall()
} or
TExplicitDelegateCall(ControlFlow::Nodes::ElementNode cfn, DelegateCall dc) {
TExplicitDelegateLikeCall(ControlFlow::Nodes::ElementNode cfn, DelegateLikeCall dc) {
cfn.getElement() = dc
} or
TTransitiveCapturedCall(ControlFlow::Nodes::ElementNode cfn, Callable target) {
@@ -308,12 +308,12 @@ abstract class DelegateDataFlowCall extends DataFlowCall {
override DataFlowCallable getARuntimeTarget() { result = this.getARuntimeTarget(_) }
}
/** An explicit delegate call relevant for data flow. */
class ExplicitDelegateDataFlowCall extends DelegateDataFlowCall, TExplicitDelegateCall {
/** An explicit delegate or function pointer call relevant for data flow. */
class ExplicitDelegateLikeDataFlowCall extends DelegateDataFlowCall, TExplicitDelegateLikeCall {
private ControlFlow::Nodes::ElementNode cfn;
private DelegateCall dc;
private DelegateLikeCall dc;
ExplicitDelegateDataFlowCall() { this = TExplicitDelegateCall(cfn, dc) }
ExplicitDelegateLikeDataFlowCall() { this = TExplicitDelegateLikeCall(cfn, dc) }
override DataFlowCallable getARuntimeTarget(CallContext::CallContext cc) {
result = getCallableForDataFlow(dc.getARuntimeTarget(cc))

View File

@@ -1394,7 +1394,7 @@ private module OutNodes {
private DataFlowCall csharpCall(Expr e, ControlFlow::Node cfn) {
e = any(DispatchCall dc | result = TNonDelegateCall(cfn, dc)).getCall() or
result = TExplicitDelegateCall(cfn, e)
result = TExplicitDelegateLikeCall(cfn, e)
}
/** A valid return type for a method that uses `yield return`. */

View File

@@ -14,8 +14,14 @@ private import semmle.code.csharp.dataflow.FlowSummary
private import semmle.code.csharp.dispatch.Dispatch
private import semmle.code.csharp.frameworks.system.linq.Expressions
/** A source of flow for a delegate or function pointer expression. */
abstract private class DelegateLikeFlowSource extends DataFlow::ExprNode {
/** Gets the callable that is referenced in this delegate or function pointer flow source. */
abstract Callable getCallable();
}
/** A source of flow for a delegate expression. */
private class DelegateFlowSource extends DataFlow::ExprNode {
private class DelegateFlowSource extends DelegateLikeFlowSource {
Callable c;
DelegateFlowSource() {
@@ -27,11 +33,29 @@ private class DelegateFlowSource extends DataFlow::ExprNode {
}
/** Gets the callable that is referenced in this delegate flow source. */
Callable getCallable() { result = c }
override Callable getCallable() { result = c }
}
/** A sink of flow for a delegate expression. */
abstract private class DelegateFlowSink extends DataFlow::Node {
/** A source of flow for a function pointer expression. */
private class FunctionPointerFlowSource extends DelegateLikeFlowSource {
Callable c;
FunctionPointerFlowSource() {
c =
this.getExpr()
.(AddressOfExpr)
.getOperand()
.(CallableAccess)
.getTarget()
.getUnboundDeclaration()
}
/** Gets the callable that is referenced in this function pointer flow source. */
override Callable getCallable() { result = c }
}
/** A sink of flow for a delegate or function pointer expression. */
abstract private class DelegateLikeFlowSink extends DataFlow::Node {
/**
* Gets an actual run-time target of this delegate call in the given call
* context, if any. The call context records the *last* call required to
@@ -85,25 +109,25 @@ abstract private class DelegateFlowSink extends DataFlow::Node {
*/
cached
Callable getARuntimeTarget(CallContext context) {
exists(DelegateFlowSource dfs |
exists(DelegateLikeFlowSource dfs |
flowsFrom(this, dfs, _, context) and
result = dfs.getCallable()
)
}
}
/** A delegate call expression. */
class DelegateCallExpr extends DelegateFlowSink, DataFlow::ExprNode {
DelegateCall dc;
/** A delegate or function pointer call expression. */
class DelegateLikeCallExpr extends DelegateLikeFlowSink, DataFlow::ExprNode {
DelegateLikeCall dc;
DelegateCallExpr() { this.getExpr() = dc.getDelegateExpr() }
DelegateLikeCallExpr() { this.getExpr() = dc.getExpr() }
/** Gets the delegate call that this expression belongs to. */
DelegateCall getDelegateCall() { result = dc }
/** Gets the delegate or function pointer call that this expression belongs to. */
DelegateLikeCall getCall() { result = dc }
}
/** A parameter of delegate type belonging to a callable with a flow summary. */
class SummaryDelegateParameterSink extends DelegateFlowSink, ParameterNode {
class SummaryDelegateParameterSink extends DelegateLikeFlowSink, ParameterNode {
SummaryDelegateParameterSink() {
this.getType() instanceof SystemLinqExpressions::DelegateExtType and
this.isParameterOf(any(SummarizedCallable c), _)
@@ -111,7 +135,7 @@ class SummaryDelegateParameterSink extends DelegateFlowSink, ParameterNode {
}
/** A delegate expression that is added to an event. */
class AddEventSource extends DelegateFlowSink, DataFlow::ExprNode {
class AddEventSource extends DelegateLikeFlowSink, DataFlow::ExprNode {
AddEventExpr ae;
AddEventSource() { this.getExpr() = ae.getRValue() }
@@ -150,7 +174,7 @@ private class NormalReturnNode extends Node {
* records the last call on the path from `node` to `sink`, if any.
*/
private predicate flowsFrom(
DelegateFlowSink sink, DataFlow::Node node, boolean isReturned, CallContext lastCall
DelegateLikeFlowSink sink, DataFlow::Node node, boolean isReturned, CallContext lastCall
) {
// Base case
sink = node and
@@ -188,7 +212,8 @@ private predicate flowsFrom(
or
// Flow into a callable (delegate call)
exists(
ParameterNode mid, CallContext prevLastCall, DelegateCall call, Callable c, Parameter p, int i
ParameterNode mid, CallContext prevLastCall, DelegateLikeCall call, Callable c, Parameter p,
int i
|
flowsFrom(sink, mid, isReturned, prevLastCall) and
isReturned = false and
@@ -238,14 +263,14 @@ private predicate flowIntoNonDelegateCall(NonDelegateCall call, Expr arg, DotNet
}
pragma[noinline]
private predicate flowIntoDelegateCall(DelegateCall call, Callable c, Expr arg, int i) {
exists(DelegateFlowSource dfs, DelegateCallExpr dce |
private predicate flowIntoDelegateCall(DelegateLikeCall call, Callable c, Expr arg, int i) {
exists(DelegateLikeFlowSource dfs, DelegateLikeCallExpr dce |
// the call context is irrelevant because the delegate call
// itself will be the context
flowsFrom(dce, dfs, _, _) and
arg = call.getArgument(i) and
c = dfs.getCallable() and
call = dce.getDelegateCall()
call = dce.getCall()
)
}
@@ -255,11 +280,13 @@ private predicate flowOutOfNonDelegateCall(NonDelegateCall call, NormalReturnNod
}
pragma[noinline]
private predicate flowOutOfDelegateCall(DelegateCall dc, NormalReturnNode ret, CallContext lastCall) {
exists(DelegateFlowSource dfs, DelegateCallExpr dce, Callable c |
private predicate flowOutOfDelegateCall(
DelegateLikeCall dc, NormalReturnNode ret, CallContext lastCall
) {
exists(DelegateLikeFlowSource dfs, DelegateLikeCallExpr dce, Callable c |
flowsFrom(dce, dfs, _, lastCall) and
ret.getEnclosingCallable() = c and
c = dfs.getCallable() and
dc = dce.getDelegateCall()
dc = dce.getCall()
)
}

View File

@@ -245,7 +245,7 @@ private module CallGraph {
* a library callable and `e` is a delegate argument.
*/
private predicate delegateCall(Call c, Expr e, boolean libraryDelegateCall) {
c = any(DelegateCall dc | e = dc.getDelegateExpr()) and
c = any(DelegateCall dc | e = dc.getExpr()) and
libraryDelegateCall = false
or
c.getTarget().fromLibrary() and

View File

@@ -527,6 +527,46 @@ class MutatorOperatorCall extends OperatorCall {
predicate isPostfix() { mutator_invocation_mode(this, 2) }
}
private class DelegateLikeCall_ = @delegate_invocation_expr or @function_pointer_invocation_expr;
/**
* A function pointer or delegate call.
*/
class DelegateLikeCall extends Call, DelegateLikeCall_ {
override Callable getTarget() { none() }
/**
* Gets a potential run-time target of this delegate or function pointer call in the given
* call context `cc`.
*/
Callable getARuntimeTarget(CallContext::CallContext cc) {
exists(DelegateLikeCallExpr call |
this = call.getCall() and
result = call.getARuntimeTarget(cc)
)
}
/**
* Gets the delegate or function pointer expression of this call. For example, the
* delegate expression of `X()` on line 5 is the access to the field `X` in
*
* ```csharp
* class A {
* Action X = () => { };
*
* void CallX() {
* X();
* }
* }
* ```
*/
Expr getExpr() { result = this.getChild(-1) }
override Callable getARuntimeTarget() { result = getARuntimeTarget(_) }
override Expr getRuntimeArgument(int i) { result = getArgument(i) }
}
/**
* A delegate call, for example `x()` on line 5 in
*
@@ -540,18 +580,13 @@ class MutatorOperatorCall extends OperatorCall {
* }
* ```
*/
class DelegateCall extends Call, @delegate_invocation_expr {
override Callable getTarget() { none() }
class DelegateCall extends DelegateLikeCall, @delegate_invocation_expr {
/**
* Gets a potential run-time target of this delegate call in the given
* call context `cc`.
*/
Callable getARuntimeTarget(CallContext::CallContext cc) {
exists(DelegateCallExpr call |
this = call.getDelegateCall() and
result = call.getARuntimeTarget(cc)
)
override Callable getARuntimeTarget(CallContext::CallContext cc) {
result = DelegateLikeCall.super.getARuntimeTarget(cc)
or
exists(AddEventSource aes, CallContext::CallContext cc2 |
aes = this.getAnAddEventSource(_) and
@@ -567,7 +602,7 @@ class DelegateCall extends Call, @delegate_invocation_expr {
}
private AddEventSource getAnAddEventSource(Callable enclosingCallable) {
this.getDelegateExpr().(EventAccess).getTarget() = result.getEvent() and
this.getExpr().(EventAccess).getTarget() = result.getEvent() and
enclosingCallable = result.getExpr().getEnclosingCallable()
}
@@ -579,25 +614,12 @@ class DelegateCall extends Call, @delegate_invocation_expr {
exists(Callable c | result = getAnAddEventSource(c) | c != this.getEnclosingCallable())
}
override Callable getARuntimeTarget() { result = getARuntimeTarget(_) }
override Expr getRuntimeArgument(int i) { result = getArgument(i) }
/**
* Gets the delegate expression of this delegate call. For example, the
* delegate expression of `X()` on line 5 is the access to the field `X` in
* DEPRECATED: use `getExpr` instead.
*
* ```csharp
* class A {
* Action X = () => { };
*
* void CallX() {
* X();
* }
* }
* ```
* Gets the delegate expression of this call.
*/
Expr getDelegateExpr() { result = this.getChild(-1) }
deprecated Expr getDelegateExpr() { result = this.getExpr() }
override string toString() { result = "delegate call" }
@@ -615,11 +637,7 @@ class DelegateCall extends Call, @delegate_invocation_expr {
* }
* ```
*/
class FunctionPointerCall extends Call, @function_pointer_invocation_expr {
override Callable getTarget() { none() }
override Expr getRuntimeArgument(int i) { result = getArgument(i) }
class FunctionPointerCall extends DelegateLikeCall, @function_pointer_invocation_expr {
override string toString() { result = "function pointer call" }
override string getAPrimaryQlClass() { result = "FunctionPointerCall" }

View File

@@ -78,7 +78,7 @@ predicate depends(ValueOrRefType t, ValueOrRefType u) {
or
exists(DelegateCall dc, DelegateType dt |
dc.getEnclosingCallable().getDeclaringType() = t and
dc.getDelegateExpr().getType() = dt and
dc.getExpr().getType() = dt and
usesType(dt.getUnboundDeclaration(), u)
)
or

View File

@@ -4,11 +4,11 @@ class DelegateFlow
{
void M1(int i) { }
void M2(Action<int> a)
static void M2(Action<int> a)
{
a(0);
a = _ => { };
a(0);
a(1);
}
void M3()
@@ -108,4 +108,20 @@ class DelegateFlow
}
public delegate void MyDelegate();
public unsafe void M16(delegate*<Action<int>, void> fnptr, Action<int> a)
{
fnptr(a);
}
public unsafe void M17()
{
M16(&M2, (i) => { });
}
public unsafe void M18()
{
delegate*<Action<int>, void> fnptr = &M2;
fnptr((i) => { });
}
}

View File

@@ -7,6 +7,8 @@ delegateCall
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:16:12:16:19 | (...) => ... | DelegateFlow.cs:16:12:16:19 | (...) => ... |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:27:12:27:19 | (...) => ... | DelegateFlow.cs:22:12:22:12 | access to parameter a |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:98:9:98:37 | LocalFunction | DelegateFlow.cs:99:12:99:24 | delegate creation of type Action<Int32> |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:119:18:119:27 | (...) => ... | DelegateFlow.cs:114:15:114:15 | access to parameter a |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:125:15:125:24 | (...) => ... | DelegateFlow.cs:125:15:125:24 | (...) => ... |
| DelegateFlow.cs:11:9:11:12 | delegate call | DelegateFlow.cs:10:13:10:20 | (...) => ... | file://:0:0:0:0 | <empty> |
| DelegateFlow.cs:33:9:33:13 | delegate call | DelegateFlow.cs:38:12:38:25 | (...) => ... | DelegateFlow.cs:38:12:38:25 | (...) => ... |
| DelegateFlow.cs:38:19:38:22 | delegate call | DelegateFlow.cs:5:10:5:11 | M1 | DelegateFlow.cs:33:12:33:12 | access to parameter a |

View File

@@ -0,0 +1,83 @@
using System;
unsafe class FunctionPointerFlow
{
public static void Log1(int i) { }
public static void Log2(int i) { }
public static void Log3(int i) { }
public static void Log4(int i) { }
public static void Log5(int i) { }
public static void Log6(int i) { }
public static void M2(delegate*<int, void> a)
{
a(1);
a = &Log3;
a(2);
}
public void M3()
{
M2(&Log1);
}
public static void M4(delegate*<int, void> a)
{
M2(a);
}
public void M5()
{
M4(&Log2);
}
public delegate*<int, void> M6()
{
return &Log4;
}
public void M7()
{
M6()(0);
}
public void M8()
{
static void LocalFunction(int i) { };
M2(&LocalFunction);
}
private static delegate*<int, void> field = &Log5;
public void M9()
{
field(1);
}
public void M10(delegate*<delegate*<int, void>, void> aa, delegate*<int, void> a)
{
aa(a);
}
public void M11()
{
M10(&M4, &Log6);
}
public void M16(delegate*<Action<int>, void> aa, Action<int> a)
{
aa(a);
}
public static void M17(Action<int> a)
{
a(0);
a = _ => { };
a(0);
}
public void M18()
{
M16(&M17, (i) => { });
}
}

View File

@@ -0,0 +1,9 @@
| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:5:24:5:27 | Log1 | FunctionPointerFlow.cs:21:12:21:16 | &... |
| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:6:24:6:27 | Log2 | FunctionPointerFlow.cs:26:12:26:12 | access to parameter a |
| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:10:24:10:27 | Log6 | FunctionPointerFlow.cs:26:12:26:12 | access to parameter a |
| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:46:9:46:44 | LocalFunction | FunctionPointerFlow.cs:47:12:47:25 | &... |
| FunctionPointerFlow.cs:16:9:16:12 | function pointer call | FunctionPointerFlow.cs:7:24:7:27 | Log3 | file://:0:0:0:0 | <empty> |
| FunctionPointerFlow.cs:41:9:41:15 | function pointer call | FunctionPointerFlow.cs:8:24:8:27 | Log4 | file://:0:0:0:0 | <empty> |
| FunctionPointerFlow.cs:54:9:54:16 | function pointer call | FunctionPointerFlow.cs:9:24:9:27 | Log5 | file://:0:0:0:0 | <empty> |
| FunctionPointerFlow.cs:59:9:59:13 | function pointer call | FunctionPointerFlow.cs:24:24:24:25 | M4 | FunctionPointerFlow.cs:64:13:64:15 | &... |
| FunctionPointerFlow.cs:69:9:69:13 | function pointer call | FunctionPointerFlow.cs:72:24:72:26 | M17 | FunctionPointerFlow.cs:81:13:81:16 | &... |

View File

@@ -0,0 +1,5 @@
import csharp
query predicate fptrCall(FunctionPointerCall fptrc, Callable c, CallContext::CallContext cc) {
c = fptrc.getARuntimeTarget(cc)
}

View File

@@ -9,7 +9,7 @@ where
m.hasName("MainDelegateAndMethodAccesses") and
e.getEnclosingCallable() = m and
e.getNumberOfArguments() = 1 and
e.getDelegateExpr() = a and
e.getExpr() = a and
a.getTarget().hasName("cd1") and
e.getArgument(0).getValue() = "-40"
select m, e, a

View File

@@ -9,7 +9,7 @@ where
m.hasName("MainDelegateAndMethodAccesses") and
e.getEnclosingCallable() = m and
e.getNumberOfArguments() = 1 and
e.getDelegateExpr() = a and
e.getExpr() = a and
a.getTarget().hasName("cd7") and
e.getArgument(0).(AddExpr).getRightOperand().(LocalVariableAccess).getTarget().hasName("x")
select m, e, a

View File

@@ -8,7 +8,7 @@ from Method m, DelegateCall e, LocalVariableAccess a
where
m.hasName("MainDelegateAndMethodAccesses") and
e.getEnclosingCallable() = m and
e.getDelegateExpr() = a and
e.getExpr() = a and
a.getTarget().hasName("cd7") and
a.getTarget().getType().(DelegateType).hasQualifiedName("Expressions.D")
select m, e, a

View File

@@ -11,7 +11,7 @@ where
e.getTarget().getName() = "Click" and
e.getTarget().getDeclaringType() = m.getDeclaringType() and
d.getEnclosingCallable() = m and
d.getDelegateExpr() = e and
d.getExpr() = e and
d.getArgument(0) instanceof ThisAccess and
d.getArgument(1).(ParameterAccess).getTarget().hasName("e")
select m, d, e