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,125 @@ 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+ abstract DataFlow:: Node getSrc ( ) ;
4938
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- )
39+ /** Gets a candidate target for this call. */
40+ cached
41+ abstract Function resolve ( ) ;
42+
43+ /**
44+ * Whether `src` can flow to this call.
45+ *
46+ * Searches backwards from `getSrc()` to `src`. The `allowFromArg`
47+ * parameter is true when the search is allowed to continue backwards into
48+ * a parameter; non-recursive callers should pass `_` for `allowFromArg`.
49+ */
50+ predicate flowsFrom ( DataFlow:: Node src , boolean allowFromArg ) {
51+ src = this .getSrc ( ) and allowFromArg = true
52+ or
53+ exists ( DataFlow:: Node other , boolean allowOtherFromArg |
54+ this .flowsFrom ( other , allowOtherFromArg )
55+ |
56+ // Call argument
57+ exists ( DataFlowCall call , int i |
58+ other .( DataFlow:: ParameterNode ) .isParameterOf ( call .getStaticCallTarget ( ) , i ) and
59+ src .( ArgumentNode ) .argumentOf ( call , i )
60+ ) and
61+ allowOtherFromArg = true and
62+ allowFromArg = true
63+ or
64+ // Call return
65+ exists ( DataFlowCall call , ReturnKind returnKind |
66+ other = getAnOutNode ( call , returnKind ) and
67+ src .( ReturnNode ) .getKind ( ) = returnKind and
68+ call .getStaticCallTarget ( ) = src .getEnclosingCallable ( )
69+ ) and
70+ allowFromArg = false
71+ or
72+ // Local flow
73+ DataFlow:: localFlowStep ( src , other ) and
74+ allowFromArg = allowOtherFromArg
75+ )
76+ or
77+ // Flow through global variable
78+ exists ( StoreInstruction store , Variable var |
79+ store = src .asInstruction ( ) and
80+ var = store .getDestinationAddress ( ) .( VariableAddressInstruction ) .getASTVariable ( ) and
81+ this .flowsFromGlobal ( var ) and
82+ allowFromArg = true
83+ )
84+ }
85+
86+ private predicate flowsFromGlobal ( GlobalOrNamespaceVariable var ) {
87+ exists ( LoadInstruction load |
88+ this .flowsFrom ( DataFlow:: instructionNode ( load ) , _) and
89+ load .getSourceAddress ( ) .( VariableAddressInstruction ) .getASTVariable ( ) = var
90+ )
91+ }
92+ }
93+
94+ /** Call through a function pointer. */
95+ private class DataSensitiveExprCall extends DataSensitiveCall {
96+ DataSensitiveExprCall ( ) { not exists ( this .getStaticCallTarget ( ) ) }
97+
98+ override DataFlow:: Node getSrc ( ) { result .asInstruction ( ) = this .getCallTarget ( ) }
99+
100+ override Function resolve ( ) {
101+ exists ( FunctionInstruction fi |
102+ this .flowsFrom ( DataFlow:: instructionNode ( fi ) , _) and
103+ result = fi .getFunctionSymbol ( )
104+ )
105+ }
106+ }
107+
108+ /** Call to a virtual function. */
109+ private class DataSensitiveOverriddenFunctionCall extends DataSensitiveCall {
110+ DataSensitiveOverriddenFunctionCall ( ) {
111+ exists ( this .getStaticCallTarget ( ) .( VirtualFunction ) .getAnOverridingFunction ( ) )
112+ }
113+
114+ override DataFlow:: Node getSrc ( ) { result .asInstruction ( ) = this .getThisArgument ( ) }
115+
116+ override MemberFunction resolve ( ) {
117+ exists ( Class overridingClass |
118+ this .overrideMayAffectCall ( overridingClass , result ) and
119+ this .hasFlowFromCastFrom ( overridingClass )
120+ )
121+ }
122+
123+ /**
124+ * Holds if `this` is a virtual function call whose static target is
125+ * overridden by `overridingFunction` in `overridingClass`.
126+ */
127+ pragma [ noinline]
128+ private predicate overrideMayAffectCall ( Class overridingClass , MemberFunction overridingFunction ) {
129+ overridingFunction .getAnOverriddenFunction + ( ) = this .getStaticCallTarget ( ) .( VirtualFunction ) and
130+ overridingFunction .getDeclaringType ( ) = overridingClass
131+ }
132+
133+ /**
134+ * Holds if the qualifier of `this` has flow from an upcast from
135+ * `derivedClass`.
136+ */
137+ pragma [ noinline]
138+ private predicate hasFlowFromCastFrom ( Class derivedClass ) {
139+ exists ( ConvertToBaseInstruction toBase |
140+ this .flowsFrom ( DataFlow:: instructionNode ( toBase ) , _) and
141+ derivedClass = toBase .getDerivedClass ( )
142+ )
143+ }
144+ }
76145}
77146
78147/**
0 commit comments