11private import cpp
22private import semmle.code.cpp.ir.IR
33private import semmle.code.cpp.ir.dataflow.DataFlow
4+ private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
45
56Function viableImpl ( CallInstruction call ) { result = viableCallable ( call ) }
67
@@ -22,57 +23,133 @@ Function viableCallable(CallInstruction call) {
2223 strictcount ( Function other | functionSignatureWithBody ( qualifiedName , nparams , other ) ) = 1
2324 )
2425 or
25- // Rudimentary virtual dispatch support. It's essentially local data flow
26- // where the source is a derived-to-base conversion and the target is the
27- // qualifier of a call.
28- exists ( Class derived , DataFlow:: Node thisArgument |
29- nodeMayHaveClass ( derived , thisArgument ) and
30- overrideMayAffectCall ( derived , thisArgument , _, result , call )
31- )
26+ // Virtual dispatch
27+ result = call .( VirtualDispatch:: DataSensitiveCall ) .resolve ( )
3228}
3329
3430/**
35- * Holds if `call` is a virtual function call with qualifier `thisArgument` in
36- * `enclosingFunction`, whose static target is overridden by
37- * `overridingFunction` in `overridingClass`.
31+ * Provides virtual dispatch support compatible with the original
32+ * implementation of `semmle.code.cpp.security.TaintTracking`.
3833 */
39- pragma [ noinline]
40- private predicate overrideMayAffectCall (
41- Class overridingClass , DataFlow:: Node thisArgument , Function enclosingFunction ,
42- MemberFunction overridingFunction , CallInstruction call
43- ) {
44- call .getEnclosingFunction ( ) = enclosingFunction and
45- overridingFunction .getAnOverriddenFunction + ( ) = call .getStaticCallTarget ( ) and
46- overridingFunction .getDeclaringType ( ) = overridingClass and
47- thisArgument = DataFlow:: instructionNode ( call .getThisArgument ( ) )
48- }
34+ private module VirtualDispatch {
35+ /** A call that may dispatch differently depending on the qualifier value. */
36+ abstract class DataSensitiveCall extends DataFlowCall {
37+ /**
38+ * Gets the node whose value determines the target of this call. This node
39+ * could be the qualifier of a virtual dispatch or the function-pointer
40+ * expression in a call to a function pointer. What they have in common is
41+ * that we need to find out which data flows there, and then it's up to the
42+ * `resolve` predicate to stitch that information together and resolve the
43+ * call.
44+ */
45+ abstract DataFlow:: Node getDispatchValue ( ) ;
4946
50- /**
51- * Holds if `node` may have dynamic class `derived`, where `derived` is a class
52- * that may affect virtual dispatch within the enclosing function.
53- *
54- * For the sake of performance, this recursion is written out manually to make
55- * it a relation on `Class x Node` rather than `Node x Node` or `MemberFunction
56- * x Node`, both of which would be larger. It's a forward search since there
57- * should usually be fewer classes than calls.
58- *
59- * If a value is cast several classes up in the hierarchy, that will be modeled
60- * as a chain of `ConvertToBaseInstruction`s and will cause the search to start
61- * from each of them and pass through subsequent ones. There might be
62- * performance to gain by stopping before a second upcast and reconstructing
63- * the full chain in a "big-step" recursion after this one.
64- */
65- private predicate nodeMayHaveClass ( Class derived , DataFlow:: Node node ) {
66- exists ( ConvertToBaseInstruction toBase |
67- derived = toBase .getDerivedClass ( ) and
68- overrideMayAffectCall ( derived , _, toBase .getEnclosingFunction ( ) , _, _) and
69- node .asInstruction ( ) = toBase
70- )
71- or
72- exists ( DataFlow:: Node prev |
73- nodeMayHaveClass ( derived , prev ) and
74- DataFlow:: localFlowStep ( prev , node )
75- )
47+ /** Gets a candidate target for this call. */
48+ cached
49+ abstract Function resolve ( ) ;
50+
51+ /**
52+ * Whether `src` can flow to this call.
53+ *
54+ * Searches backwards from `getDispatchValue()` to `src`. The `allowFromArg`
55+ * parameter is true when the search is allowed to continue backwards into
56+ * a parameter; non-recursive callers should pass `_` for `allowFromArg`.
57+ */
58+ predicate flowsFrom ( DataFlow:: Node src , boolean allowFromArg ) {
59+ src = this .getDispatchValue ( ) and allowFromArg = true
60+ or
61+ exists ( DataFlow:: Node other , boolean allowOtherFromArg |
62+ this .flowsFrom ( other , allowOtherFromArg )
63+ |
64+ // Call argument
65+ exists ( DataFlowCall call , int i |
66+ other .( DataFlow:: ParameterNode ) .isParameterOf ( call .getStaticCallTarget ( ) , i ) and
67+ src .( ArgumentNode ) .argumentOf ( call , i )
68+ ) and
69+ allowOtherFromArg = true and
70+ allowFromArg = true
71+ or
72+ // Call return
73+ exists ( DataFlowCall call , ReturnKind returnKind |
74+ other = getAnOutNode ( call , returnKind ) and
75+ src .( ReturnNode ) .getKind ( ) = returnKind and
76+ call .getStaticCallTarget ( ) = src .getEnclosingCallable ( )
77+ ) and
78+ allowFromArg = false
79+ or
80+ // Local flow
81+ DataFlow:: localFlowStep ( src , other ) and
82+ allowFromArg = allowOtherFromArg
83+ )
84+ or
85+ // Flow through global variable
86+ exists ( StoreInstruction store , Variable var |
87+ store = src .asInstruction ( ) and
88+ var = store .getDestinationAddress ( ) .( VariableAddressInstruction ) .getASTVariable ( ) and
89+ this .flowsFromGlobal ( var ) and
90+ allowFromArg = true
91+ )
92+ }
93+
94+ private predicate flowsFromGlobal ( GlobalOrNamespaceVariable var ) {
95+ exists ( LoadInstruction load |
96+ this .flowsFrom ( DataFlow:: instructionNode ( load ) , _) and
97+ load .getSourceAddress ( ) .( VariableAddressInstruction ) .getASTVariable ( ) = var
98+ )
99+ }
100+ }
101+
102+ /** Call through a function pointer. */
103+ private class DataSensitiveExprCall extends DataSensitiveCall {
104+ DataSensitiveExprCall ( ) { not exists ( this .getStaticCallTarget ( ) ) }
105+
106+ override DataFlow:: Node getDispatchValue ( ) { result .asInstruction ( ) = this .getCallTarget ( ) }
107+
108+ override Function resolve ( ) {
109+ exists ( FunctionInstruction fi |
110+ this .flowsFrom ( DataFlow:: instructionNode ( fi ) , _) and
111+ result = fi .getFunctionSymbol ( )
112+ )
113+ }
114+ }
115+
116+ /** Call to a virtual function. */
117+ private class DataSensitiveOverriddenFunctionCall extends DataSensitiveCall {
118+ DataSensitiveOverriddenFunctionCall ( ) {
119+ exists ( this .getStaticCallTarget ( ) .( VirtualFunction ) .getAnOverridingFunction ( ) )
120+ }
121+
122+ override DataFlow:: Node getDispatchValue ( ) { result .asInstruction ( ) = this .getThisArgument ( ) }
123+
124+ override MemberFunction resolve ( ) {
125+ exists ( Class overridingClass |
126+ this .overrideMayAffectCall ( overridingClass , result ) and
127+ this .hasFlowFromCastFrom ( overridingClass )
128+ )
129+ }
130+
131+ /**
132+ * Holds if `this` is a virtual function call whose static target is
133+ * overridden by `overridingFunction` in `overridingClass`.
134+ */
135+ pragma [ noinline]
136+ private predicate overrideMayAffectCall ( Class overridingClass , MemberFunction overridingFunction ) {
137+ overridingFunction .getAnOverriddenFunction + ( ) = this .getStaticCallTarget ( ) .( VirtualFunction ) and
138+ overridingFunction .getDeclaringType ( ) = overridingClass
139+ }
140+
141+ /**
142+ * Holds if the qualifier of `this` has flow from an upcast from
143+ * `derivedClass`.
144+ */
145+ pragma [ noinline]
146+ private predicate hasFlowFromCastFrom ( Class derivedClass ) {
147+ exists ( ConvertToBaseInstruction toBase |
148+ this .flowsFrom ( DataFlow:: instructionNode ( toBase ) , _) and
149+ derivedClass = toBase .getDerivedClass ( )
150+ )
151+ }
152+ }
76153}
77154
78155/**
0 commit comments