Skip to content

Commit 6815a13

Browse files
authored
Merge pull request #6931 from hvitved/dataflow/restrict-derived-summaries
Data flow: Restrict derived flow summaries
2 parents 6c2713d + 4eacbd1 commit 6815a13

File tree

10 files changed

+288
-112
lines changed

10 files changed

+288
-112
lines changed

csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll

Lines changed: 82 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ module Public {
8585
/** Holds if this stack contains summary component `c`. */
8686
predicate contains(SummaryComponent c) { c = this.drop(_).head() }
8787

88+
/** Gets the bottom element of this stack. */
89+
SummaryComponent bottom() { result = this.drop(this.length() - 1).head() }
90+
8891
/** Gets a textual representation of this stack. */
8992
string toString() {
9093
exists(SummaryComponent head, SummaryComponentStack tail |
@@ -197,6 +200,8 @@ module Private {
197200
or
198201
tail.(RequiredSummaryComponentStack).required(TParameterSummaryComponent(_)) and
199202
head = thisParam()
203+
or
204+
derivedFluentFlowPush(_, _, _, head, tail, _)
200205
}
201206

202207
pragma[nomagic]
@@ -210,7 +215,7 @@ module Private {
210215
c.propagatesFlow(output, input, preservesValue) and
211216
preservesValue = true and
212217
isCallbackParameter(input) and
213-
isContentOfArgument(output)
218+
isContentOfArgument(output, _)
214219
or
215220
// flow from the receiver of a callback into the instance-parameter
216221
exists(SummaryComponentStack s, SummaryComponentStack callbackRef |
@@ -222,16 +227,81 @@ module Private {
222227
output = TConsSummaryComponentStack(thisParam(), input) and
223228
preservesValue = true
224229
)
230+
or
231+
exists(SummaryComponentStack arg, SummaryComponentStack return |
232+
derivedFluentFlow(c, input, arg, return, preservesValue)
233+
|
234+
arg.length() = 1 and
235+
output = return
236+
or
237+
exists(SummaryComponent head, SummaryComponentStack tail |
238+
derivedFluentFlowPush(c, input, arg, head, tail, 0) and
239+
output = SummaryComponentStack::push(head, tail)
240+
)
241+
)
242+
or
243+
// Chain together summaries where values get passed into callbacks along the way
244+
exists(SummaryComponentStack mid, boolean preservesValue1, boolean preservesValue2 |
245+
c.propagatesFlow(input, mid, preservesValue1) and
246+
c.propagatesFlow(mid, output, preservesValue2) and
247+
mid.drop(mid.length() - 2) =
248+
SummaryComponentStack::push(TParameterSummaryComponent(_),
249+
SummaryComponentStack::singleton(TArgumentSummaryComponent(_))) and
250+
preservesValue = preservesValue1.booleanAnd(preservesValue2)
251+
)
252+
}
253+
254+
/**
255+
* Holds if `c` has a flow summary from `input` to `arg`, where `arg`
256+
* writes to (contents of) the `i`th argument, and `c` has a
257+
* value-preserving flow summary from the `i`th argument to a return value
258+
* (`return`).
259+
*
260+
* In such a case, we derive flow from `input` to (contents of) the return
261+
* value.
262+
*
263+
* As an example, this simplifies modeling of fluent methods:
264+
* for `StringBuilder.append(x)` with a specified value flow from qualifier to
265+
* return value and taint flow from argument 0 to the qualifier, then this
266+
* allows us to infer taint flow from argument 0 to the return value.
267+
*/
268+
pragma[nomagic]
269+
private predicate derivedFluentFlow(
270+
SummarizedCallable c, SummaryComponentStack input, SummaryComponentStack arg,
271+
SummaryComponentStack return, boolean preservesValue
272+
) {
273+
exists(int i |
274+
summary(c, input, arg, preservesValue) and
275+
isContentOfArgument(arg, i) and
276+
summary(c, SummaryComponentStack::singleton(TArgumentSummaryComponent(i)), return, true) and
277+
return.bottom() = TReturnSummaryComponent(_)
278+
)
279+
}
280+
281+
pragma[nomagic]
282+
private predicate derivedFluentFlowPush(
283+
SummarizedCallable c, SummaryComponentStack input, SummaryComponentStack arg,
284+
SummaryComponent head, SummaryComponentStack tail, int i
285+
) {
286+
derivedFluentFlow(c, input, arg, tail, _) and
287+
head = arg.drop(i).head() and
288+
i = arg.length() - 2
289+
or
290+
exists(SummaryComponent head0, SummaryComponentStack tail0 |
291+
derivedFluentFlowPush(c, input, arg, head0, tail0, i + 1) and
292+
head = arg.drop(i).head() and
293+
tail = SummaryComponentStack::push(head0, tail0)
294+
)
225295
}
226296

227297
private predicate isCallbackParameter(SummaryComponentStack s) {
228298
s.head() = TParameterSummaryComponent(_) and exists(s.tail())
229299
}
230300

231-
private predicate isContentOfArgument(SummaryComponentStack s) {
232-
s.head() = TContentSummaryComponent(_) and isContentOfArgument(s.tail())
301+
private predicate isContentOfArgument(SummaryComponentStack s, int i) {
302+
s.head() = TContentSummaryComponent(_) and isContentOfArgument(s.tail(), i)
233303
or
234-
s = TSingletonSummaryComponentStack(TArgumentSummaryComponent(_))
304+
s = TSingletonSummaryComponentStack(TArgumentSummaryComponent(i))
235305
}
236306

237307
private predicate outputState(SummarizedCallable c, SummaryComponentStack s) {
@@ -508,9 +578,14 @@ module Private {
508578
* node, and back out to `p`.
509579
*/
510580
predicate summaryAllowParameterReturnInSelf(ParamNode p) {
511-
exists(SummarizedCallable c, int i |
512-
c.clearsContent(i, _) and
513-
p.isParameterOf(c, i)
581+
exists(SummarizedCallable c, int i | p.isParameterOf(c, i) |
582+
c.clearsContent(i, _)
583+
or
584+
exists(SummaryComponentStack inputContents, SummaryComponentStack outputContents |
585+
summary(c, inputContents, outputContents, _) and
586+
inputContents.bottom() = pragma[only_bind_into](TArgumentSummaryComponent(i)) and
587+
outputContents.bottom() = pragma[only_bind_into](TArgumentSummaryComponent(i))
588+
)
514589
)
515590
}
516591

@@ -534,22 +609,6 @@ module Private {
534609
preservesValue = false and not summary(c, inputContents, outputContents, true)
535610
)
536611
or
537-
// If flow through a method updates a parameter from some input A, and that
538-
// parameter also is returned through B, then we'd like a combined flow from A
539-
// to B as well. As an example, this simplifies modeling of fluent methods:
540-
// for `StringBuilder.append(x)` with a specified value flow from qualifier to
541-
// return value and taint flow from argument 0 to the qualifier, then this
542-
// allows us to infer taint flow from argument 0 to the return value.
543-
succ instanceof ParamNode and
544-
summaryPostUpdateNode(pred, succ) and
545-
preservesValue = true
546-
or
547-
// Similarly we would like to chain together summaries where values get passed
548-
// into callbacks along the way.
549-
pred instanceof ArgNode and
550-
summaryPostUpdateNode(succ, pred) and
551-
preservesValue = true
552-
or
553612
exists(SummarizedCallable c, int i |
554613
pred.(ParamNode).isParameterOf(c, i) and
555614
succ = summaryNode(c, TSummaryNodeClearsContentState(i, _)) and

csharp/ql/lib/semmle/code/csharp/frameworks/EntityFramework.qll

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -174,32 +174,34 @@ module EntityFramework {
174174
}
175175
}
176176

177-
private class RawSqlStringSummarizedCallable extends EFSummarizedCallable {
178-
private SummaryComponentStack input_;
179-
private SummaryComponentStack output_;
180-
private boolean preservesValue_;
181-
182-
RawSqlStringSummarizedCallable() {
177+
private class RawSqlStringConstructorSummarizedCallable extends EFSummarizedCallable {
178+
RawSqlStringConstructorSummarizedCallable() {
183179
exists(RawSqlStringStruct s |
184180
this = s.getAConstructor() and
185-
input_ = SummaryComponentStack::argument(0) and
186-
this.getNumberOfParameters() > 0 and
187-
output_ = SummaryComponentStack::return() and
188-
preservesValue_ = false
189-
or
190-
this = s.getAConversionTo() and
191-
input_ = SummaryComponentStack::argument(0) and
192-
output_ = SummaryComponentStack::return() and
193-
preservesValue_ = false
181+
this.getNumberOfParameters() > 0
194182
)
195183
}
196184

197185
override predicate propagatesFlow(
198186
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
199187
) {
200-
input = input_ and
201-
output = output_ and
202-
preservesValue = preservesValue_
188+
input = SummaryComponentStack::argument(0) and
189+
output = SummaryComponentStack::return() and
190+
preservesValue = false
191+
}
192+
}
193+
194+
private class RawSqlStringConversionSummarizedCallable extends EFSummarizedCallable {
195+
RawSqlStringConversionSummarizedCallable() {
196+
exists(RawSqlStringStruct s | this = s.getAConversionTo())
197+
}
198+
199+
override predicate propagatesFlow(
200+
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
201+
) {
202+
input = SummaryComponentStack::argument(0) and
203+
output = SummaryComponentStack::return() and
204+
preservesValue = false
203205
}
204206
}
205207

csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ void M4()
3333

3434
void M5()
3535
{
36-
this.StepFieldSetter(new object());
36+
Sink(((D)this.StepFieldSetter(new object()).Field2).Field);
3737
Sink(this.Field);
3838
}
3939

@@ -102,7 +102,7 @@ void M15()
102102
Sink(d);
103103
}, d1, d2);
104104
Sink(d1.Field);
105-
Sink(d2.Field2); // SPURIOUS FLOW
105+
Sink(d2.Field2);
106106
}
107107

108108
object StepArgRes(object x) { return null; }
@@ -120,7 +120,7 @@ void StepQualArg(object @out) { }
120120

121121
object StepFieldGetter() => throw null;
122122

123-
void StepFieldSetter(object value) => throw null;
123+
D StepFieldSetter(object value) => throw null;
124124

125125
object Property { get; set; }
126126

csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.expected

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@ edges
1111
| ExternalFlow.cs:30:13:30:16 | [post] this access [field Field] : Object | ExternalFlow.cs:31:18:31:21 | this access [field Field] : Object |
1212
| ExternalFlow.cs:30:26:30:37 | object creation of type Object : Object | ExternalFlow.cs:30:13:30:16 | [post] this access [field Field] : Object |
1313
| ExternalFlow.cs:31:18:31:21 | this access [field Field] : Object | ExternalFlow.cs:31:18:31:39 | call to method StepFieldGetter |
14-
| ExternalFlow.cs:36:13:36:16 | [post] this access [field Field] : Object | ExternalFlow.cs:37:18:37:21 | this access [field Field] : Object |
15-
| ExternalFlow.cs:36:34:36:45 | object creation of type Object : Object | ExternalFlow.cs:36:13:36:16 | [post] this access [field Field] : Object |
14+
| ExternalFlow.cs:36:19:36:62 | (...) ... [field Field] : Object | ExternalFlow.cs:36:18:36:69 | access to field Field |
15+
| ExternalFlow.cs:36:22:36:25 | [post] this access [field Field] : Object | ExternalFlow.cs:37:18:37:21 | this access [field Field] : Object |
16+
| ExternalFlow.cs:36:22:36:55 | call to method StepFieldSetter [field Field2, field Field] : Object | ExternalFlow.cs:36:22:36:62 | access to field Field2 [field Field] : Object |
17+
| ExternalFlow.cs:36:22:36:62 | access to field Field2 [field Field] : Object | ExternalFlow.cs:36:19:36:62 | (...) ... [field Field] : Object |
18+
| ExternalFlow.cs:36:43:36:54 | object creation of type Object : Object | ExternalFlow.cs:36:22:36:25 | [post] this access [field Field] : Object |
19+
| ExternalFlow.cs:36:43:36:54 | object creation of type Object : Object | ExternalFlow.cs:36:22:36:55 | call to method StepFieldSetter [field Field2, field Field] : Object |
1620
| ExternalFlow.cs:37:18:37:21 | this access [field Field] : Object | ExternalFlow.cs:37:18:37:27 | access to field Field |
1721
| ExternalFlow.cs:42:13:42:16 | [post] this access [property Property] : Object | ExternalFlow.cs:43:18:43:21 | this access [property Property] : Object |
1822
| ExternalFlow.cs:42:29:42:40 | object creation of type Object : Object | ExternalFlow.cs:42:13:42:16 | [post] this access [property Property] : Object |
@@ -48,10 +52,7 @@ edges
4852
| ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | ExternalFlow.cs:98:13:98:14 | [post] access to local variable d1 [field Field] : Object |
4953
| ExternalFlow.cs:100:20:100:20 | d : Object | ExternalFlow.cs:102:22:102:22 | access to parameter d |
5054
| ExternalFlow.cs:103:16:103:17 | access to local variable d1 [field Field] : Object | ExternalFlow.cs:100:20:100:20 | d : Object |
51-
| ExternalFlow.cs:103:16:103:17 | access to local variable d1 [field Field] : Object | ExternalFlow.cs:103:20:103:21 | [post] access to local variable d2 [field Field2] : Object |
52-
| ExternalFlow.cs:103:20:103:21 | [post] access to local variable d2 [field Field2] : Object | ExternalFlow.cs:105:18:105:19 | access to local variable d2 [field Field2] : Object |
5355
| ExternalFlow.cs:104:18:104:19 | access to local variable d1 [field Field] : Object | ExternalFlow.cs:104:18:104:25 | access to field Field |
54-
| ExternalFlow.cs:105:18:105:19 | access to local variable d2 [field Field2] : Object | ExternalFlow.cs:105:18:105:26 | access to field Field2 |
5556
nodes
5657
| ExternalFlow.cs:9:27:9:38 | object creation of type Object : Object | semmle.label | object creation of type Object : Object |
5758
| ExternalFlow.cs:10:18:10:33 | call to method StepArgRes | semmle.label | call to method StepArgRes |
@@ -69,8 +70,12 @@ nodes
6970
| ExternalFlow.cs:30:26:30:37 | object creation of type Object : Object | semmle.label | object creation of type Object : Object |
7071
| ExternalFlow.cs:31:18:31:21 | this access [field Field] : Object | semmle.label | this access [field Field] : Object |
7172
| ExternalFlow.cs:31:18:31:39 | call to method StepFieldGetter | semmle.label | call to method StepFieldGetter |
72-
| ExternalFlow.cs:36:13:36:16 | [post] this access [field Field] : Object | semmle.label | [post] this access [field Field] : Object |
73-
| ExternalFlow.cs:36:34:36:45 | object creation of type Object : Object | semmle.label | object creation of type Object : Object |
73+
| ExternalFlow.cs:36:18:36:69 | access to field Field | semmle.label | access to field Field |
74+
| ExternalFlow.cs:36:19:36:62 | (...) ... [field Field] : Object | semmle.label | (...) ... [field Field] : Object |
75+
| ExternalFlow.cs:36:22:36:25 | [post] this access [field Field] : Object | semmle.label | [post] this access [field Field] : Object |
76+
| ExternalFlow.cs:36:22:36:55 | call to method StepFieldSetter [field Field2, field Field] : Object | semmle.label | call to method StepFieldSetter [field Field2, field Field] : Object |
77+
| ExternalFlow.cs:36:22:36:62 | access to field Field2 [field Field] : Object | semmle.label | access to field Field2 [field Field] : Object |
78+
| ExternalFlow.cs:36:43:36:54 | object creation of type Object : Object | semmle.label | object creation of type Object : Object |
7479
| ExternalFlow.cs:37:18:37:21 | this access [field Field] : Object | semmle.label | this access [field Field] : Object |
7580
| ExternalFlow.cs:37:18:37:27 | access to field Field | semmle.label | access to field Field |
7681
| ExternalFlow.cs:42:13:42:16 | [post] this access [property Property] : Object | semmle.label | [post] this access [property Property] : Object |
@@ -116,11 +121,8 @@ nodes
116121
| ExternalFlow.cs:100:20:100:20 | d : Object | semmle.label | d : Object |
117122
| ExternalFlow.cs:102:22:102:22 | access to parameter d | semmle.label | access to parameter d |
118123
| ExternalFlow.cs:103:16:103:17 | access to local variable d1 [field Field] : Object | semmle.label | access to local variable d1 [field Field] : Object |
119-
| ExternalFlow.cs:103:20:103:21 | [post] access to local variable d2 [field Field2] : Object | semmle.label | [post] access to local variable d2 [field Field2] : Object |
120124
| ExternalFlow.cs:104:18:104:19 | access to local variable d1 [field Field] : Object | semmle.label | access to local variable d1 [field Field] : Object |
121125
| ExternalFlow.cs:104:18:104:25 | access to field Field | semmle.label | access to field Field |
122-
| ExternalFlow.cs:105:18:105:19 | access to local variable d2 [field Field2] : Object | semmle.label | access to local variable d2 [field Field2] : Object |
123-
| ExternalFlow.cs:105:18:105:26 | access to field Field2 | semmle.label | access to field Field2 |
124126
subpaths
125127
invalidModelRow
126128
#select
@@ -129,7 +131,8 @@ invalidModelRow
129131
| ExternalFlow.cs:18:18:18:24 | access to local variable argOut1 | ExternalFlow.cs:16:30:16:41 | object creation of type Object : Object | ExternalFlow.cs:18:18:18:24 | access to local variable argOut1 | $@ | ExternalFlow.cs:16:30:16:41 | object creation of type Object : Object | object creation of type Object : Object |
130132
| ExternalFlow.cs:25:18:25:21 | this access | ExternalFlow.cs:23:27:23:38 | object creation of type Object : Object | ExternalFlow.cs:25:18:25:21 | this access | $@ | ExternalFlow.cs:23:27:23:38 | object creation of type Object : Object | object creation of type Object : Object |
131133
| ExternalFlow.cs:31:18:31:39 | call to method StepFieldGetter | ExternalFlow.cs:30:26:30:37 | object creation of type Object : Object | ExternalFlow.cs:31:18:31:39 | call to method StepFieldGetter | $@ | ExternalFlow.cs:30:26:30:37 | object creation of type Object : Object | object creation of type Object : Object |
132-
| ExternalFlow.cs:37:18:37:27 | access to field Field | ExternalFlow.cs:36:34:36:45 | object creation of type Object : Object | ExternalFlow.cs:37:18:37:27 | access to field Field | $@ | ExternalFlow.cs:36:34:36:45 | object creation of type Object : Object | object creation of type Object : Object |
134+
| ExternalFlow.cs:36:18:36:69 | access to field Field | ExternalFlow.cs:36:43:36:54 | object creation of type Object : Object | ExternalFlow.cs:36:18:36:69 | access to field Field | $@ | ExternalFlow.cs:36:43:36:54 | object creation of type Object : Object | object creation of type Object : Object |
135+
| ExternalFlow.cs:37:18:37:27 | access to field Field | ExternalFlow.cs:36:43:36:54 | object creation of type Object : Object | ExternalFlow.cs:37:18:37:27 | access to field Field | $@ | ExternalFlow.cs:36:43:36:54 | object creation of type Object : Object | object creation of type Object : Object |
133136
| ExternalFlow.cs:43:18:43:42 | call to method StepPropertyGetter | ExternalFlow.cs:42:29:42:40 | object creation of type Object : Object | ExternalFlow.cs:43:18:43:42 | call to method StepPropertyGetter | $@ | ExternalFlow.cs:42:29:42:40 | object creation of type Object : Object | object creation of type Object : Object |
134137
| ExternalFlow.cs:49:18:49:30 | access to property Property | ExternalFlow.cs:48:37:48:48 | object creation of type Object : Object | ExternalFlow.cs:49:18:49:30 | access to property Property | $@ | ExternalFlow.cs:48:37:48:48 | object creation of type Object : Object | object creation of type Object : Object |
135138
| ExternalFlow.cs:55:18:55:41 | call to method StepElementGetter | ExternalFlow.cs:54:36:54:47 | object creation of type Object : Object | ExternalFlow.cs:55:18:55:41 | call to method StepElementGetter | $@ | ExternalFlow.cs:54:36:54:47 | object creation of type Object : Object | object creation of type Object : Object |
@@ -141,4 +144,3 @@ invalidModelRow
141144
| ExternalFlow.cs:92:18:92:18 | (...) ... | ExternalFlow.cs:90:21:90:34 | object creation of type String : String | ExternalFlow.cs:92:18:92:18 | (...) ... | $@ | ExternalFlow.cs:90:21:90:34 | object creation of type String : String | object creation of type String : String |
142145
| ExternalFlow.cs:102:22:102:22 | access to parameter d | ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | ExternalFlow.cs:102:22:102:22 | access to parameter d | $@ | ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | object creation of type Object : Object |
143146
| ExternalFlow.cs:104:18:104:25 | access to field Field | ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | ExternalFlow.cs:104:18:104:25 | access to field Field | $@ | ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | object creation of type Object : Object |
144-
| ExternalFlow.cs:105:18:105:26 | access to field Field2 | ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | ExternalFlow.cs:105:18:105:26 | access to field Field2 | $@ | ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | object creation of type Object : Object |

csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.ql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class SummaryModelTest extends SummaryModelCsv {
1717
"My.Qltest;D;false;StepArgQual;(System.Object);;Argument[0];Argument[-1];taint",
1818
"My.Qltest;D;false;StepFieldGetter;();;Field[My.Qltest.D.Field] of Argument[-1];ReturnValue;value",
1919
"My.Qltest;D;false;StepFieldSetter;(System.Object);;Argument[0];Field[My.Qltest.D.Field] of Argument[-1];value",
20+
"My.Qltest;D;false;StepFieldSetter;(System.Object);;Argument[-1];Field[My.Qltest.D.Field2] of ReturnValue;value",
2021
"My.Qltest;D;false;StepPropertyGetter;();;Property[My.Qltest.D.Property] of Argument[-1];ReturnValue;value",
2122
"My.Qltest;D;false;StepPropertySetter;(System.Object);;Argument[0];Property[My.Qltest.D.Property] of Argument[-1];value",
2223
"My.Qltest;D;false;StepElementGetter;();;Element of Argument[-1];ReturnValue;value",

0 commit comments

Comments
 (0)