Skip to content

Commit 6bd3557

Browse files
authored
Merge pull request #4353 from hvitved/csharp/dataflow/task-precise
C#: Precise data-flow for `System.Threading.Tasks`
2 parents 77abff7 + c39bca5 commit 6bd3557

File tree

13 files changed

+551
-421
lines changed

13 files changed

+551
-421
lines changed

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

Lines changed: 115 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ private import semmle.code.csharp.frameworks.system.io.Compression
1111
private import semmle.code.csharp.frameworks.system.linq.Expressions
1212
private import semmle.code.csharp.frameworks.system.Net
1313
private import semmle.code.csharp.frameworks.system.Text
14+
private import semmle.code.csharp.frameworks.system.runtime.CompilerServices
1415
private import semmle.code.csharp.frameworks.system.threading.Tasks
1516
private import semmle.code.csharp.frameworks.system.Web
1617
private import semmle.code.csharp.frameworks.system.web.ui.WebControls
@@ -34,6 +35,8 @@ private newtype TAccessPath =
3435
or
3536
tail = AccessPath::singleton(_) and
3637
head instanceof ElementContent
38+
or
39+
tail = AccessPath::element()
3740
}
3841

3942
/** An access path. */
@@ -93,6 +96,11 @@ module AccessPath {
9396
result = singleton(any(PropertyContent c | c.getProperty() = p.getSourceDeclaration()))
9497
}
9598

99+
/** Gets a singleton field access path. */
100+
AccessPath field(Field f) {
101+
result = singleton(any(FieldContent c | c.getField() = f.getSourceDeclaration()))
102+
}
103+
96104
/** Gets an access path representing a property inside a collection. */
97105
AccessPath properties(Property p) { result = TConsAccessPath(any(ElementContent c), property(p)) }
98106
}
@@ -1665,7 +1673,6 @@ class SystemThreadingTasksTaskFlow extends LibraryTypeDataFlow, SystemThreadingT
16651673
(
16661674
m.hasName("ContinueWith") and
16671675
sourceAp = AccessPath::empty() and
1668-
sinkAp = AccessPath::empty() and
16691676
(
16701677
// flow from supplied state to supplied delegate
16711678
exists(ConstructedDelegateType delegate, int i, int j, int k |
@@ -1677,79 +1684,83 @@ class SystemThreadingTasksTaskFlow extends LibraryTypeDataFlow, SystemThreadingT
16771684
) and
16781685
delegate.getTypeArgument(k) instanceof ObjectType and
16791686
source = TCallableFlowSourceArg(i) and
1680-
sink = getDelegateFlowSinkArg(m, j, k)
1687+
sink = getDelegateFlowSinkArg(m, j, k) and
1688+
sinkAp = AccessPath::empty()
16811689
)
16821690
or
16831691
// flow out of supplied function
16841692
exists(ConstructedDelegateType func, int i |
16851693
m.getParameter(i).getType() = func and
16861694
func.getUnboundGeneric() instanceof SystemFuncDelegateType and
16871695
source = getDelegateFlowSourceArg(m, i) and
1688-
sink = TCallableFlowSinkReturn()
1696+
sink = TCallableFlowSinkReturn() and
1697+
sinkAp = AccessPath::property(any(SystemThreadingTasksTaskTClass c).getResultProperty())
16891698
)
16901699
)
16911700
or
16921701
m.hasName("FromResult") and
1702+
source = TCallableFlowSourceArg(0) and
16931703
sourceAp = AccessPath::empty() and
1694-
sinkAp = AccessPath::empty() and
1695-
(
1696-
source = TCallableFlowSourceArg(0) and
1697-
sink = TCallableFlowSinkReturn()
1698-
)
1704+
sink = TCallableFlowSinkReturn() and
1705+
sinkAp = AccessPath::property(any(SystemThreadingTasksTaskTClass c).getResultProperty())
16991706
or
17001707
m.hasName("Run") and
1708+
m.getReturnType() = any(SystemThreadingTasksTaskTClass c).getAConstructedGeneric() and
1709+
m.(UnboundGenericMethod).getNumberOfTypeParameters() = 1 and
1710+
source = TCallableFlowSourceDelegateArg(0) and
17011711
sourceAp = AccessPath::empty() and
1702-
sinkAp = AccessPath::empty() and
1703-
(
1704-
m.getReturnType() = any(SystemThreadingTasksTaskTClass c).getAConstructedGeneric() and
1705-
m.(UnboundGenericMethod).getNumberOfTypeParameters() = 1 and
1706-
source = TCallableFlowSourceDelegateArg(0) and
1707-
sink = TCallableFlowSinkReturn()
1708-
)
1712+
sink = TCallableFlowSinkReturn() and
1713+
sinkAp = AccessPath::property(any(SystemThreadingTasksTaskTClass c).getResultProperty())
17091714
or
17101715
m.getName().regexpMatch("WhenAll|WhenAny") and
1711-
sinkAp = AccessPath::empty() and
1712-
(
1713-
m.getReturnType() = any(SystemThreadingTasksTaskTClass c).getAConstructedGeneric() and
1714-
m.(UnboundGenericMethod).getNumberOfTypeParameters() = 1 and
1715-
source = getFlowSourceArg(m, _, sourceAp) and
1716-
sink = TCallableFlowSinkReturn()
1717-
)
1716+
m.getReturnType() = any(SystemThreadingTasksTaskTClass c).getAConstructedGeneric() and
1717+
m.(UnboundGenericMethod).getNumberOfTypeParameters() = 1 and
1718+
source = getFlowSourceArg(m, _, _) and
1719+
sourceAp = AccessPath::properties(any(SystemThreadingTasksTaskTClass c).getResultProperty()) and
1720+
sink = TCallableFlowSinkReturn() and
1721+
sinkAp =
1722+
AccessPath::cons(any(PropertyContent c |
1723+
c.getProperty() = any(SystemThreadingTasksTaskTClass tc).getResultProperty()
1724+
), AccessPath::element())
17181725
)
17191726
}
17201727
}
17211728

17221729
/** Data flow for `System.Threading.Tasks.Task<>`. */
1723-
class SystemThreadingTasksTaskTFlow extends LibraryTypeDataFlow {
1724-
SystemThreadingTasksTaskTFlow() { this instanceof SystemThreadingTasksTaskTClass }
1725-
1730+
class SystemThreadingTasksTaskTFlow extends LibraryTypeDataFlow, SystemThreadingTasksTaskTClass {
17261731
override predicate callableFlow(
1727-
CallableFlowSource source, CallableFlowSink sink, SourceDeclarationCallable c,
1728-
boolean preservesValue
1732+
CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp,
1733+
SourceDeclarationCallable c, boolean preservesValue
17291734
) {
17301735
(
1731-
constructorFlow(source, sink, c)
1736+
constructorFlow(source, sourceAp, sink, sinkAp, c)
17321737
or
1733-
methodFlow(source, sink, c)
1734-
or
1735-
exists(Property p |
1736-
propertyFlow(p) and
1737-
source = TCallableFlowSourceQualifier() and
1738-
sink = TCallableFlowSinkReturn() and
1739-
c = p.getGetter()
1740-
)
1738+
methodFlow(source, sourceAp, sink, sinkAp, c)
17411739
) and
17421740
preservesValue = true
1741+
or
1742+
exists(Property p |
1743+
p = this.(SystemThreadingTasksTaskTClass).getResultProperty() and
1744+
source = TCallableFlowSourceQualifier() and
1745+
sourceAp = AccessPath::empty() and
1746+
sink = TCallableFlowSinkReturn() and
1747+
sinkAp = AccessPath::empty() and
1748+
c = p.getGetter() and
1749+
preservesValue = false
1750+
)
17431751
}
17441752

1745-
private predicate constructorFlow(CallableFlowSource source, CallableFlowSink sink, Constructor c) {
1753+
private predicate constructorFlow(
1754+
CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp,
1755+
Constructor c
1756+
) {
17461757
// flow from supplied function into constructed Task
17471758
c.getDeclaringType() = this and
1748-
(
1749-
c.getParameter(0).getType() = any(SystemFuncDelegateType t).getAConstructedGeneric() and
1750-
source = TCallableFlowSourceDelegateArg(0) and
1751-
sink = TCallableFlowSinkReturn()
1752-
)
1759+
c.getParameter(0).getType() = any(SystemFuncDelegateType t).getAConstructedGeneric() and
1760+
source = TCallableFlowSourceDelegateArg(0) and
1761+
sourceAp = AccessPath::empty() and
1762+
sink = TCallableFlowSinkReturn() and
1763+
sinkAp = AccessPath::property(this.(SystemThreadingTasksTaskTClass).getResultProperty())
17531764
or
17541765
// flow from supplied state to supplied delegate
17551766
c.getDeclaringType() = this and
@@ -1759,12 +1770,15 @@ class SystemThreadingTasksTaskTFlow extends LibraryTypeDataFlow {
17591770
func.getUnboundGeneric().(SystemFuncDelegateType).getNumberOfTypeParameters() = 2 and
17601771
func.getTypeArgument(0) instanceof ObjectType and
17611772
source = TCallableFlowSourceArg(1) and
1762-
sink = getDelegateFlowSinkArg(c, 0, 0)
1773+
sourceAp = AccessPath::empty() and
1774+
sink = getDelegateFlowSinkArg(c, 0, 0) and
1775+
sinkAp = AccessPath::empty()
17631776
)
17641777
}
17651778

17661779
private predicate methodFlow(
1767-
CallableFlowSource source, CallableFlowSink sink, SourceDeclarationMethod m
1780+
CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp,
1781+
SourceDeclarationMethod m
17681782
) {
17691783
m.getDeclaringType() = this and
17701784
m.hasName("ContinueWith") and
@@ -1781,27 +1795,37 @@ class SystemThreadingTasksTaskTFlow extends LibraryTypeDataFlow {
17811795
delegate.getTypeArgument(j) instanceof ObjectType and
17821796
m.getParameter(k).getType() instanceof ObjectType and
17831797
source = TCallableFlowSourceArg(k) and
1784-
sink = getDelegateFlowSinkArg(m, i, j)
1798+
sourceAp = AccessPath::empty() and
1799+
sink = getDelegateFlowSinkArg(m, i, j) and
1800+
sinkAp = AccessPath::empty()
17851801
)
17861802
or
17871803
// flow from this task to supplied delegate
17881804
delegate.getTypeArgument(j) = this and
17891805
source = TCallableFlowSourceQualifier() and
1790-
sink = getDelegateFlowSinkArg(m, i, j)
1806+
sourceAp = AccessPath::empty() and
1807+
sink = getDelegateFlowSinkArg(m, i, j) and
1808+
sinkAp = AccessPath::empty()
17911809
)
17921810
or
17931811
// flow out of supplied function
17941812
exists(ConstructedDelegateType func, int i |
17951813
m.getParameter(i).getType() = func and
17961814
func.getUnboundGeneric() instanceof SystemFuncDelegateType and
17971815
source = getDelegateFlowSourceArg(m, i) and
1798-
sink = TCallableFlowSinkReturn()
1816+
sourceAp = AccessPath::empty() and
1817+
sink = TCallableFlowSinkReturn() and
1818+
sinkAp = AccessPath::property(this.(SystemThreadingTasksTaskTClass).getResultProperty())
17991819
)
18001820
)
1801-
}
1802-
1803-
private predicate propertyFlow(Property p) {
1804-
p = this.(SystemThreadingTasksTaskTClass).getResultProperty()
1821+
or
1822+
m = this.getGetAwaiterMethod() and
1823+
source = TCallableFlowSourceQualifier() and
1824+
sourceAp = AccessPath::empty() and
1825+
sink = TCallableFlowSinkReturn() and
1826+
sinkAp =
1827+
AccessPath::field(any(SystemRuntimeCompilerServicesTaskAwaiterStruct s)
1828+
.getUnderlyingTaskField())
18051829
}
18061830
}
18071831

@@ -1813,15 +1837,16 @@ class SystemThreadingTasksFactoryFlow extends LibraryTypeDataFlow {
18131837
}
18141838

18151839
override predicate callableFlow(
1816-
CallableFlowSource source, CallableFlowSink sink, SourceDeclarationCallable c,
1817-
boolean preservesValue
1840+
CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp,
1841+
SourceDeclarationCallable c, boolean preservesValue
18181842
) {
1819-
methodFlow(source, sink, c) and
1843+
methodFlow(source, sourceAp, sink, sinkAp, c) and
18201844
preservesValue = true
18211845
}
18221846

18231847
private predicate methodFlow(
1824-
CallableFlowSource source, CallableFlowSink sink, SourceDeclarationMethod m
1848+
CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp,
1849+
SourceDeclarationMethod m
18251850
) {
18261851
m.getDeclaringType() = this and
18271852
(
@@ -1838,15 +1863,19 @@ class SystemThreadingTasksFactoryFlow extends LibraryTypeDataFlow {
18381863
delegate.getUnboundGeneric() instanceof SystemFuncDelegateType
18391864
) and
18401865
source = TCallableFlowSourceArg(i) and
1841-
sink = getDelegateFlowSinkArg(m, j, k)
1866+
sourceAp = AccessPath::empty() and
1867+
sink = getDelegateFlowSinkArg(m, j, k) and
1868+
sinkAp = AccessPath::empty()
18421869
)
18431870
or
18441871
// flow out of supplied function
18451872
exists(ConstructedDelegateType func, int i |
18461873
m.getParameter(i).getType() = func and
18471874
func.getUnboundGeneric() instanceof SystemFuncDelegateType and
18481875
source = getDelegateFlowSourceArg(m, i) and
1849-
sink = TCallableFlowSinkReturn()
1876+
sourceAp = AccessPath::empty() and
1877+
sink = TCallableFlowSinkReturn() and
1878+
sinkAp = AccessPath::property(any(SystemThreadingTasksTaskTClass c).getResultProperty())
18501879
)
18511880
)
18521881
or
@@ -1862,21 +1891,48 @@ class SystemThreadingTasksFactoryFlow extends LibraryTypeDataFlow {
18621891
) and
18631892
delegate.getTypeArgument(k) instanceof ObjectType and
18641893
source = TCallableFlowSourceArg(i) and
1865-
sink = getDelegateFlowSinkArg(m, j, k)
1894+
sourceAp = AccessPath::empty() and
1895+
sink = getDelegateFlowSinkArg(m, j, k) and
1896+
sinkAp = AccessPath::empty()
18661897
)
18671898
or
18681899
// flow out of supplied function
18691900
exists(ConstructedDelegateType func, int i |
18701901
m.getParameter(i).getType() = func and
18711902
func.getUnboundGeneric() instanceof SystemFuncDelegateType and
18721903
source = getDelegateFlowSourceArg(m, i) and
1873-
sink = TCallableFlowSinkReturn()
1904+
sourceAp = AccessPath::empty() and
1905+
sink = TCallableFlowSinkReturn() and
1906+
sinkAp = AccessPath::property(any(SystemThreadingTasksTaskTClass c).getResultProperty())
18741907
)
18751908
)
18761909
)
18771910
}
18781911
}
18791912

1913+
/** Data flow for `System.Runtime.CompilerServices.TaskAwaiter<>`. */
1914+
class SystemRuntimeCompilerServicesTaskAwaiterFlow extends LibraryTypeDataFlow,
1915+
SystemRuntimeCompilerServicesTaskAwaiterStruct {
1916+
override predicate callableFlow(
1917+
CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp,
1918+
SourceDeclarationCallable c, boolean preservesValue
1919+
) {
1920+
preservesValue = true and
1921+
c = this.getGetResultMethod() and
1922+
source = TCallableFlowSourceQualifier() and
1923+
sourceAp =
1924+
AccessPath::cons(any(FieldContent fc | fc.getField() = this.getUnderlyingTaskField()),
1925+
AccessPath::property(any(SystemThreadingTasksTaskTClass t).getResultProperty())) and
1926+
sink = TCallableFlowSinkReturn() and
1927+
sinkAp = AccessPath::empty()
1928+
}
1929+
1930+
override predicate requiresAccessPath(Content head, AccessPath tail) {
1931+
head.(FieldContent).getField() = this.getUnderlyingTaskField() and
1932+
tail = AccessPath::property(any(SystemThreadingTasksTaskTClass t).getResultProperty())
1933+
}
1934+
}
1935+
18801936
/** Data flow for `System.Text.Encoding`. */
18811937
library class SystemTextEncodingFlow extends LibraryTypeDataFlow, SystemTextEncodingClass {
18821938
override predicate callableFlow(

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

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ private import semmle.code.csharp.dispatch.Dispatch
1616
private import semmle.code.csharp.frameworks.EntityFramework
1717
private import semmle.code.csharp.frameworks.NHibernate
1818
private import semmle.code.csharp.frameworks.system.Collections
19+
private import semmle.code.csharp.frameworks.system.threading.Tasks
1920

2021
abstract class NodeImpl extends Node {
2122
/** Do not call: use `getEnclosingCallable()` instead. */
@@ -154,10 +155,6 @@ module LocalFlow {
154155
scope = e2 and
155156
isSuccessor = true
156157
or
157-
e1 = e2.(AwaitExpr).getExpr() and
158-
scope = e2 and
159-
isSuccessor = true
160-
or
161158
// An `=` expression, where the result of the expression is used
162159
e2 =
163160
any(AssignExpr ae |
@@ -679,6 +676,11 @@ private module Cached {
679676
storeStepLibrary(node1, c, node2)
680677
}
681678

679+
pragma[nomagic]
680+
private PropertyContent getResultContent() {
681+
result.getProperty() = any(SystemThreadingTasksTaskTClass c_).getResultProperty()
682+
}
683+
682684
/**
683685
* Holds if data can flow from `node1` to `node2` via a read of content `c`.
684686
*/
@@ -699,6 +701,10 @@ private module Cached {
699701
node2.(SsaDefinitionNode).getDefinition() = def and
700702
c instanceof ElementContent
701703
)
704+
or
705+
x.hasNodePath(node1, node2) and
706+
node2.asExpr().(AwaitExpr).getExpr() = node1.asExpr() and
707+
c = getResultContent()
702708
)
703709
or
704710
readStepLibrary(node1, c, node2)
@@ -2180,6 +2186,11 @@ private class ReadStepConfiguration extends ControlFlowReachabilityConfiguration
21802186
isSuccessor = true and
21812187
arrayRead(e1, e2) and
21822188
scope = e2
2189+
or
2190+
exactScope = false and
2191+
e1 = e2.(AwaitExpr).getExpr() and
2192+
scope = e2 and
2193+
isSuccessor = true
21832194
}
21842195

21852196
override predicate candidateDef(

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ private class LocalTaintExprStepConfiguration extends ControlFlowReachabilityCon
9595
scope = e2 and
9696
isSuccessor = true
9797
)
98+
or
99+
e1 = e2.(AwaitExpr).getExpr() and
100+
scope = e2 and
101+
isSuccessor = true
98102
)
99103
}
100104
}

0 commit comments

Comments
 (0)