Skip to content

Commit def3d6b

Browse files
committed
C#: CSV-based flow summaries
1 parent 0ddeb7a commit def3d6b

File tree

11 files changed

+793
-13
lines changed

11 files changed

+793
-13
lines changed

csharp/ql/src/semmle/code/csharp/dataflow/ExternalFlow.qll

Lines changed: 428 additions & 0 deletions
Large diffs are not rendered by default.

csharp/ql/src/semmle/code/csharp/dataflow/internal/FlowSummaryImplSpecific.qll

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ private import DataFlowPublic
1010
private import FlowSummaryImpl::Private
1111
private import FlowSummaryImpl::Public
1212
private import semmle.code.csharp.Unification
13+
private import semmle.code.csharp.dataflow.ExternalFlow
1314

1415
/** Holds is `i` is a valid parameter position. */
1516
predicate parameterPosition(int i) { i in [-1 .. any(Parameter p).getPosition()] }
@@ -82,7 +83,40 @@ DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) {
8283
* Holds if an external flow summary exists for `c` with input specification
8384
* `input`, output specification `output`, and kind `kind`.
8485
*/
85-
predicate summaryElement(DataFlowCallable c, string input, string output, string kind) { none() }
86+
predicate summaryElement(DataFlowCallable c, string input, string output, string kind) {
87+
exists(
88+
string namespace, string type, boolean subtypes, string name, string signature, string ext
89+
|
90+
summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind) and
91+
c = interpretElement(namespace, type, subtypes, name, signature, ext)
92+
)
93+
}
94+
95+
/**
96+
* Holds if an external source specification exists for `e` with output specification
97+
* `output` and kind `kind`.
98+
*/
99+
predicate sourceElement(Element e, string output, string kind) {
100+
exists(
101+
string namespace, string type, boolean subtypes, string name, string signature, string ext
102+
|
103+
sourceModel(namespace, type, subtypes, name, signature, ext, output, kind) and
104+
e = interpretElement(namespace, type, subtypes, name, signature, ext)
105+
)
106+
}
107+
108+
/**
109+
* Holds if an external sink specification exists for `n` with input specification
110+
* `input` and kind `kind`.
111+
*/
112+
predicate sinkElement(Element e, string input, string kind) {
113+
exists(
114+
string namespace, string type, boolean subtypes, string name, string signature, string ext
115+
|
116+
sinkModel(namespace, type, subtypes, name, signature, ext, input, kind) and
117+
e = interpretElement(namespace, type, subtypes, name, signature, ext)
118+
)
119+
}
86120

87121
/** Gets the summary component for specification component `c`, if any. */
88122
bindingset[c]
@@ -102,18 +136,6 @@ SummaryComponent interpretComponentSpecific(string c) {
102136

103137
class SourceOrSinkElement = Element;
104138

105-
/**
106-
* Holds if an external source specification exists for `e` with output specification
107-
* `output` and kind `kind`.
108-
*/
109-
predicate sourceElement(Element e, string output, string kind) { none() }
110-
111-
/**
112-
* Holds if an external sink specification exists for `n` with input specification
113-
* `input` and kind `kind`.
114-
*/
115-
predicate sinkElement(Element e, string input, string kind) { none() }
116-
117139
/** Gets the return kind corresponding to specification `"ReturnValue"`. */
118140
NormalReturnKind getReturnValueKind() { any() }
119141

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
namespace My.Qltest
2+
{
3+
public class B
4+
{
5+
void Foo()
6+
{
7+
object arg1 = new object();
8+
Sink1(arg1);
9+
10+
object argToTagged = new object();
11+
TaggedSinkMethod(argToTagged);
12+
13+
object fieldWrite = new object();
14+
TaggedField = fieldWrite;
15+
}
16+
17+
object SinkMethod()
18+
{
19+
object res = new object();
20+
return res;
21+
}
22+
23+
[SinkAttribute]
24+
object TaggedSinkMethod()
25+
{
26+
object resTag = new object();
27+
return resTag;
28+
}
29+
30+
void Sink1(object x) { }
31+
32+
[SinkAttribute]
33+
void TaggedSinkMethod(object x) { }
34+
35+
[SinkAttribute]
36+
object TaggedField;
37+
}
38+
39+
class SinkAttribute : System.Attribute { }
40+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
namespace My.Qltest
2+
{
3+
public class A
4+
{
5+
void Foo()
6+
{
7+
object x;
8+
x = Src1();
9+
x = Src1("");
10+
11+
Sub sub = new Sub();
12+
x = sub.Src2();
13+
x = sub.Src3();
14+
15+
SrcArg(x);
16+
17+
x = TaggedSrcMethod();
18+
x = TaggedSrcField;
19+
20+
x = SrcTwoArg("", "");
21+
}
22+
23+
[SourceAttribute()]
24+
void Tagged1(object taggedMethodParam)
25+
{
26+
}
27+
28+
void Tagged2([SourceAttribute()] object taggedSrcParam)
29+
{
30+
}
31+
32+
object Src1() { return null; }
33+
34+
object Src1(string s) { return null; }
35+
36+
object Src2() { return null; }
37+
38+
public virtual object Src3() { return null; }
39+
40+
public virtual void SrcParam(object p) { }
41+
42+
class Sub : A
43+
{
44+
// inherit src2
45+
public override object Src3() { return null; }
46+
47+
public override void SrcParam(object p) { }
48+
}
49+
50+
void SrcArg(object src) { }
51+
52+
[SourceAttribute()]
53+
object TaggedSrcMethod() { return null; }
54+
55+
[SourceAttribute()]
56+
object TaggedSrcField;
57+
58+
object SrcTwoArg(string s1, string s2) { return null; }
59+
}
60+
61+
class SourceAttribute : System.Attribute { }
62+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
namespace My.Qltest
2+
{
3+
public class C
4+
{
5+
void Foo()
6+
{
7+
object arg1 = new object();
8+
StepArgRes(arg1);
9+
10+
object argIn1 = new object();
11+
object argOut1 = new object();
12+
StepArgArg(argIn1, argOut1);
13+
object argIn2 = new object();
14+
object argOut2 = new object();
15+
StepArgArg(argIn2, argOut2);
16+
17+
object arg2 = new object();
18+
StepArgQual(arg2);
19+
object arg3 = new object();
20+
this.StepArgQual(arg3);
21+
22+
this.StepQualRes();
23+
StepQualRes();
24+
25+
object argOut = new object();
26+
StepQualArg(argOut);
27+
28+
this.StepFieldGetter();
29+
30+
this.StepFieldSetter(0);
31+
32+
this.StepPropertyGetter();
33+
34+
this.StepPropertySetter(0);
35+
36+
this.StepElementGetter();
37+
38+
this.StepElementSetter(0);
39+
40+
var gen = new Generic<int>();
41+
gen.StepGeneric(0);
42+
gen.StepGeneric2(false);
43+
}
44+
45+
object StepArgRes(object x) { return null; }
46+
47+
void StepArgArg(object @in, object @out) { }
48+
49+
void StepArgQual(object x) { }
50+
51+
object StepQualRes() { return null; }
52+
53+
void StepQualArg(object @out) { }
54+
55+
int Field;
56+
57+
int StepFieldGetter() => throw null;
58+
59+
void StepFieldSetter(int value) => throw null;
60+
61+
int Property { get; set; }
62+
63+
int StepPropertyGetter() => throw null;
64+
65+
void StepPropertySetter(int value) => throw null;
66+
67+
int StepElementGetter() => throw null;
68+
69+
void StepElementSetter(int value) => throw null;
70+
71+
class Generic<T>
72+
{
73+
public T StepGeneric(T t) => throw null;
74+
75+
public T StepGeneric2<S>(S s) => throw null;
76+
}
77+
}
78+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
invalidModelRow
2+
#select
3+
| Sinks.cs:8:19:8:22 | access to local variable arg1 | qltest |
4+
| Sinks.cs:11:13:11:41 | this access | qltest-arg |
5+
| Sinks.cs:11:30:11:40 | access to local variable argToTagged | qltest-arg |
6+
| Sinks.cs:14:27:14:36 | access to local variable fieldWrite | qltest-nospec |
7+
| Sinks.cs:20:20:20:22 | access to local variable res | qltest |
8+
| Sinks.cs:27:20:27:25 | access to local variable resTag | qltest-retval |
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import csharp
2+
import DataFlow
3+
import semmle.code.csharp.dataflow.ExternalFlow
4+
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
5+
import CsvValidation
6+
7+
class SinkModelTest extends SinkModelCsv {
8+
override predicate row(string row) {
9+
row =
10+
[
11+
//"namespace;type;overrides;name;signature;ext;spec;kind",
12+
"My.Qltest;B;false;Sink1;(object);;Argument[0];qltest",
13+
"My.Qltest;B;false;SinkMethod;();;ReturnValue;qltest",
14+
"My.Qltest;SinkAttribute;false;;;Attribute;ReturnValue;qltest-retval",
15+
"My.Qltest;SinkAttribute;false;;;Attribute;Argument;qltest-arg",
16+
"My.Qltest;SinkAttribute;false;;;Attribute;;qltest-nospec"
17+
]
18+
}
19+
}
20+
21+
from DataFlow::Node node, string kind
22+
where sinkNode(node, kind)
23+
select node, kind
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
invalidModelRow
2+
#select
3+
| Sources.cs:8:17:8:22 | call to method Src1 | qltest |
4+
| Sources.cs:8:17:8:22 | call to method Src1 | qltest-all-overloads |
5+
| Sources.cs:9:17:9:24 | call to method Src1 | qltest |
6+
| Sources.cs:9:17:9:24 | call to method Src1 | qltest-all-overloads |
7+
| Sources.cs:9:17:9:24 | call to method Src1 | qltest-alt |
8+
| Sources.cs:12:17:12:26 | call to method Src2 | qltest |
9+
| Sources.cs:12:17:12:26 | call to method Src2 | qltest-w-subtypes |
10+
| Sources.cs:13:17:13:26 | call to method Src3 | qltest-w-subtypes |
11+
| Sources.cs:15:13:15:21 | [post] this access | qltest-argany |
12+
| Sources.cs:15:20:15:20 | [post] access to local variable x | qltest-argany |
13+
| Sources.cs:15:20:15:20 | [post] access to local variable x | qltest-argnum |
14+
| Sources.cs:17:17:17:33 | call to method TaggedSrcMethod | qltest-retval |
15+
| Sources.cs:18:17:18:30 | access to field TaggedSrcField | qltest-nospec |
16+
| Sources.cs:20:17:20:33 | call to method SrcTwoArg | qltest-longsig |
17+
| Sources.cs:20:17:20:33 | call to method SrcTwoArg | qltest-shortsig |
18+
| Sources.cs:24:14:24:20 | this | qltest-param |
19+
| Sources.cs:24:29:24:45 | taggedMethodParam | qltest-param |
20+
| Sources.cs:28:49:28:62 | taggedSrcParam | qltest-nospec |
21+
| Sources.cs:28:49:28:62 | taggedSrcParam | qltest-param |
22+
| Sources.cs:40:45:40:45 | p | qltest-param-override |
23+
| Sources.cs:47:50:47:50 | p | qltest-param-override |
24+
| Sources.cs:53:16:53:30 | this | qltest-param |
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import csharp
2+
import DataFlow
3+
import semmle.code.csharp.dataflow.ExternalFlow
4+
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
5+
import CsvValidation
6+
7+
class SourceModelTest extends SourceModelCsv {
8+
override predicate row(string row) {
9+
row =
10+
[
11+
//"namespace;type;overrides;name;signature;ext;spec;kind",
12+
"My.Qltest;A;false;Src1;();;ReturnValue;qltest",
13+
"My.Qltest;A;false;Src1;(string);;ReturnValue;qltest",
14+
"My.Qltest;A;false;Src1;(System.String);;ReturnValue;qltest-alt",
15+
"My.Qltest;A;false;Src1;;;ReturnValue;qltest-all-overloads",
16+
"My.Qltest;A;false;Src2;();;ReturnValue;qltest",
17+
"My.Qltest;A;false;Src3;();;ReturnValue;qltest",
18+
"My.Qltest;A;true;Src2;();;ReturnValue;qltest-w-subtypes",
19+
"My.Qltest;A;true;Src3;();;ReturnValue;qltest-w-subtypes",
20+
"My.Qltest;A;false;SrcArg;(object);;Argument[0];qltest-argnum",
21+
"My.Qltest;A;false;SrcArg;(object);;Argument;qltest-argany",
22+
"My.Qltest;A;true;SrcParam;(object);;Parameter[0];qltest-param-override",
23+
"My.Qltest;SourceAttribute;false;;;Attribute;ReturnValue;qltest-retval",
24+
"My.Qltest;SourceAttribute;false;;;Attribute;Parameter;qltest-param",
25+
"My.Qltest;SourceAttribute;false;;;Attribute;;qltest-nospec",
26+
"My.Qltest;A;false;SrcTwoArg;(string,string);;ReturnValue;qltest-shortsig",
27+
"My.Qltest;A;false;SrcTwoArg;(System.String,System.String);;ReturnValue;qltest-longsig"
28+
]
29+
}
30+
}
31+
32+
from DataFlow::Node node, string kind
33+
where sourceNode(node, kind)
34+
select node, kind
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
invalidModelRow
2+
summaryThroughStep
3+
| Steps.cs:8:24:8:27 | access to local variable arg1 | Steps.cs:8:13:8:28 | call to method StepArgRes | false |
4+
| Steps.cs:12:24:12:29 | access to local variable argIn1 | Steps.cs:12:32:12:38 | [post] access to local variable argOut1 | false |
5+
| Steps.cs:15:24:15:29 | access to local variable argIn2 | Steps.cs:15:32:15:38 | [post] access to local variable argOut2 | false |
6+
| Steps.cs:18:25:18:28 | access to local variable arg2 | Steps.cs:18:13:18:29 | [post] this access | false |
7+
| Steps.cs:20:30:20:33 | access to local variable arg3 | Steps.cs:20:13:20:16 | [post] this access | false |
8+
| Steps.cs:22:13:22:16 | this access | Steps.cs:22:13:22:30 | call to method StepQualRes | false |
9+
| Steps.cs:23:13:23:25 | this access | Steps.cs:23:13:23:25 | call to method StepQualRes | false |
10+
| Steps.cs:26:13:26:31 | this access | Steps.cs:26:25:26:30 | [post] access to local variable argOut | false |
11+
| Steps.cs:41:29:41:29 | 0 | Steps.cs:41:13:41:30 | call to method StepGeneric | true |
12+
| Steps.cs:42:30:42:34 | false | Steps.cs:42:13:42:35 | call to method StepGeneric2 | true |
13+
summaryGetterStep
14+
| Steps.cs:28:13:28:16 | this access | Steps.cs:28:13:28:34 | call to method StepFieldGetter | Steps.cs:55:13:55:17 | field Field |
15+
| Steps.cs:32:13:32:16 | this access | Steps.cs:32:13:32:37 | call to method StepPropertyGetter | Steps.cs:61:13:61:20 | property Property |
16+
| Steps.cs:36:13:36:16 | this access | Steps.cs:36:13:36:36 | call to method StepElementGetter | file://:0:0:0:0 | element |
17+
summarySetterStep
18+
| Steps.cs:30:34:30:34 | 0 | Steps.cs:30:13:30:16 | [post] this access | Steps.cs:55:13:55:17 | field Field |
19+
| Steps.cs:34:37:34:37 | 0 | Steps.cs:34:13:34:16 | [post] this access | Steps.cs:61:13:61:20 | property Property |
20+
| Steps.cs:38:36:38:36 | 0 | Steps.cs:38:13:38:16 | [post] this access | file://:0:0:0:0 | element |

0 commit comments

Comments
 (0)