Skip to content

Commit fe808f7

Browse files
committed
Switch to implementation using a universal read-only ContentSet
1 parent 63ff0cb commit fe808f7

File tree

6 files changed

+135
-112
lines changed

6 files changed

+135
-112
lines changed

go/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll

Lines changed: 33 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -143,102 +143,55 @@ predicate jumpStep(Node n1, Node n2) {
143143
* Thus, `node2` references an object with a content `x` that contains the
144144
* value of `node1`.
145145
*/
146-
predicate storeStep(Node node1, ContentSet c, Node node2) {
147-
// a write `(*p).f = rhs` is modeled as two store steps: `rhs` is flows into field `f` of `(*p)`,
148-
// which in turn flows into the pointer content of `p`
149-
exists(Write w, Field f, DataFlow::Node base, DataFlow::Node rhs | w.writesField(base, f, rhs) |
150-
node1 = rhs and
151-
node2.(PostUpdateNode).getPreUpdateNode() = base and
152-
c = any(DataFlow::FieldContent fc | fc.getField() = f)
146+
predicate storeStep(Node node1, ContentSet cs, Node node2) {
147+
exists(Content c | cs.asOneContent() = c |
148+
// a write `(*p).f = rhs` is modeled as two store steps: `rhs` is flows into field `f` of `(*p)`,
149+
// which in turn flows into the pointer content of `p`
150+
exists(Write w, Field f, DataFlow::Node base, DataFlow::Node rhs | w.writesField(base, f, rhs) |
151+
node1 = rhs and
152+
node2.(PostUpdateNode).getPreUpdateNode() = base and
153+
c = any(DataFlow::FieldContent fc | fc.getField() = f)
154+
or
155+
node1 = base and
156+
node2.(PostUpdateNode).getPreUpdateNode() = node1.(PointerDereferenceNode).getOperand() and
157+
c = any(DataFlow::PointerContent pc | pc.getPointerType() = node2.getType())
158+
)
153159
or
154-
node1 = base and
155-
node2.(PostUpdateNode).getPreUpdateNode() = node1.(PointerDereferenceNode).getOperand() and
160+
node1 = node2.(AddressOperationNode).getOperand() and
156161
c = any(DataFlow::PointerContent pc | pc.getPointerType() = node2.getType())
162+
or
163+
containerStoreStep(node1, node2, c)
157164
)
158165
or
159-
node1 = node2.(AddressOperationNode).getOperand() and
160-
c = any(DataFlow::PointerContent pc | pc.getPointerType() = node2.getType())
161-
or
162-
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), c,
166+
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), cs,
163167
node2.(FlowSummaryNode).getSummaryNode())
164-
or
165-
containerStoreStep(node1, node2, c)
166-
}
167-
168-
/**
169-
* Gets a `DataFlow::ContentSet` containing a single `Content` appropriate
170-
* for reading a field, element, map value or channel message of type `containerType`.
171-
*/
172-
DataFlow::ContentSet getContentForType(Type containerType) {
173-
containerType instanceof ArrayType and
174-
result instanceof DataFlow::ArrayContent
175-
or
176-
containerType instanceof SliceType and
177-
result instanceof DataFlow::ArrayContent
178-
or
179-
containerType instanceof ChanType and
180-
result instanceof DataFlow::CollectionContent
181-
or
182-
containerType instanceof MapType and
183-
result instanceof DataFlow::MapValueContent
184-
or
185-
result.(DataFlow::PointerContent).getPointerType() = containerType
186-
or
187-
exists(Field f | f = containerType.(StructType).getField(_) |
188-
result.(DataFlow::FieldContent).getField() = f
189-
)
190-
}
191-
192-
/**
193-
* Gets the type of an array/slice element, channel value, map value,
194-
* pointer base type or named-type underlying type relating to `containerType`.
195-
*/
196-
Type getElementType(Type containerType) {
197-
result = containerType.(ArrayType).getElementType() or
198-
result = containerType.(SliceType).getElementType() or
199-
result = containerType.(ChanType).getElementType() or
200-
result = containerType.(MapType).getValueType() or
201-
result = containerType.(PointerType).getPointerType() or
202-
result = containerType.(NamedType).getUnderlyingType()
203-
}
204-
205-
/**
206-
* Gets the type of an array/slice element, channel value, map value,
207-
* pointer base type, named-type underlying type or struct field type
208-
* relating to `containerType`.
209-
*/
210-
Type getAnElementOrFieldType(Type containerType) {
211-
result = getElementType(containerType) or
212-
result = containerType.(StructType).getField(_).getType()
213168
}
214169

215170
/**
216171
* Holds if data can flow from `node1` to `node2` via a read of `c`.
217172
* Thus, `node1` references an object with a content `c` whose value ends up in
218173
* `node2`.
219174
*/
220-
predicate readStep(Node node1, ContentSet c, Node node2) {
221-
node1 = node2.(PointerDereferenceNode).getOperand() and
222-
c = any(DataFlow::PointerContent pc | pc.getPointerType() = node1.getType())
223-
or
224-
exists(FieldReadNode read |
225-
node2 = read and
226-
node1 = read.getBase() and
227-
c = any(DataFlow::FieldContent fc | fc.getField() = read.getField())
175+
predicate readStep(Node node1, ContentSet cs, Node node2) {
176+
exists(Content c | cs.asOneContent() = c |
177+
node1 = node2.(PointerDereferenceNode).getOperand() and
178+
c = any(DataFlow::PointerContent pc | pc.getPointerType() = node1.getType())
179+
or
180+
exists(FieldReadNode read |
181+
node2 = read and
182+
node1 = read.getBase() and
183+
c = any(DataFlow::FieldContent fc | fc.getField() = read.getField())
184+
)
185+
or
186+
containerReadStep(node1, node2, c)
228187
)
229188
or
230-
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c,
189+
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), cs,
231190
node2.(FlowSummaryNode).getSummaryNode())
232191
or
233-
containerReadStep(node1, node2, c)
234-
or
235-
exists(Type containerType |
236-
any(ImplicitFieldReadNode ifrn).shouldImplicitlyReadAllFields(node1) and
237-
getAnElementOrFieldType*(node1.getType()) = containerType
238-
|
239-
c = getContentForType(containerType) and
240-
node1 = node2
241-
)
192+
any(ImplicitFieldReadNode ifrn).shouldImplicitlyReadAllFields(node1) and
193+
cs.isUniversalContent() and
194+
node1 = node2
242195
}
243196

244197
/**

go/ql/lib/semmle/go/dataflow/internal/DataFlowUtil.qll

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,13 @@ class Content extends TContent {
182182
) {
183183
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
184184
}
185+
186+
/**
187+
* Gets the `ContentSet` contaning only this content.
188+
*/
189+
ContentSet asContentSet() {
190+
result.asOneContent() = this
191+
}
185192
}
186193

187194
/** A reference through a field. */
@@ -249,21 +256,35 @@ class SyntheticFieldContent extends Content, TSyntheticFieldContent {
249256
override string toString() { result = s.toString() }
250257
}
251258

259+
private newtype TContentSet =
260+
TOneContent(Content c) or
261+
TAllContent()
262+
252263
/**
253264
* An entity that represents a set of `Content`s.
254265
*
255266
* The set may be interpreted differently depending on whether it is
256267
* stored into (`getAStoreContent`) or read from (`getAReadContent`).
257268
*/
258-
class ContentSet instanceof Content {
269+
class ContentSet instanceof TContentSet {
259270
/** Gets a content that may be stored into when storing into this set. */
260-
Content getAStoreContent() { result = this }
271+
Content getAStoreContent() {
272+
this = TOneContent(result)
273+
}
261274

262275
/** Gets a content that may be read from when reading from this set. */
263-
Content getAReadContent() { result = this }
276+
Content getAReadContent() {
277+
this = TOneContent(result)
278+
or
279+
this = TAllContent() and result = any(Content c)
280+
}
264281

265282
/** Gets a textual representation of this content set. */
266-
string toString() { result = super.toString() }
283+
string toString() {
284+
exists(Content c | this = TOneContent(c) | result = c.toString())
285+
or
286+
this = TAllContent() and result = "all content"
287+
}
267288

268289
/**
269290
* Holds if this element is at the specified location.
@@ -275,7 +296,30 @@ class ContentSet instanceof Content {
275296
predicate hasLocationInfo(
276297
string filepath, int startline, int startcolumn, int endline, int endcolumn
277298
) {
278-
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
299+
exists(Content c | this = TOneContent(c) |
300+
c.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
301+
)
302+
or
303+
this = TAllContent() and
304+
filepath = "" and
305+
startline = 0 and
306+
startcolumn = 0 and
307+
endline = 0 and
308+
endcolumn = 0
309+
}
310+
311+
/**
312+
* If this is a singleton content set, returns the content.
313+
*/
314+
Content asOneContent() {
315+
this = TOneContent(result)
316+
}
317+
318+
/**
319+
* Holds if this is a universal content set.
320+
*/
321+
predicate isUniversalContent() {
322+
this = TAllContent()
279323
}
280324
}
281325

go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -55,26 +55,28 @@ module Input implements InputSig<Location, DataFlowImplSpecific::GoDataFlow> {
5555
}
5656

5757
string encodeContent(ContentSet cs, string arg) {
58-
exists(Field f, string package, string className, string fieldName |
59-
f = cs.(FieldContent).getField() and
60-
f.hasQualifiedName(package, className, fieldName) and
61-
result = "Field" and
62-
arg = package + "." + className + "." + fieldName
63-
)
64-
or
65-
exists(SyntheticField f |
66-
f = cs.(SyntheticFieldContent).getField() and result = "SyntheticField" and arg = f
58+
exists(Content c | cs.asOneContent() = c |
59+
exists(Field f, string package, string className, string fieldName |
60+
f = c.(FieldContent).getField() and
61+
f.hasQualifiedName(package, className, fieldName) and
62+
result = "Field" and
63+
arg = package + "." + className + "." + fieldName
64+
)
65+
or
66+
exists(SyntheticField f |
67+
f = c.(SyntheticFieldContent).getField() and result = "SyntheticField" and arg = f
68+
)
69+
or
70+
c instanceof ArrayContent and result = "ArrayElement" and arg = ""
71+
or
72+
c instanceof CollectionContent and result = "Element" and arg = ""
73+
or
74+
c instanceof MapKeyContent and result = "MapKey" and arg = ""
75+
or
76+
c instanceof MapValueContent and result = "MapValue" and arg = ""
77+
or
78+
c instanceof PointerContent and result = "Dereference" and arg = ""
6779
)
68-
or
69-
cs instanceof ArrayContent and result = "ArrayElement" and arg = ""
70-
or
71-
cs instanceof CollectionContent and result = "Element" and arg = ""
72-
or
73-
cs instanceof MapKeyContent and result = "MapKey" and arg = ""
74-
or
75-
cs instanceof MapValueContent and result = "MapValue" and arg = ""
76-
or
77-
cs instanceof PointerContent and result = "Dereference" and arg = ""
7880
}
7981

8082
bindingset[token]
@@ -339,7 +341,7 @@ module Private {
339341
SummaryComponent qualifier() { result = argument(-1) }
340342

341343
/** Gets a summary component for field `f`. */
342-
SummaryComponent field(Field f) { result = content(any(FieldContent c | c.getField() = f)) }
344+
SummaryComponent field(Field f) { result = content(any(FieldContent c | c.getField() = f).asContentSet()) }
343345

344346
/** Gets a summary component that represents the return value of a call. */
345347
SummaryComponent return() { result = SC::return(_) }

go/ql/lib/semmle/go/dataflow/internal/TaintTrackingUtil.qll

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,38 @@ predicate localTaintStep(DataFlow::Node src, DataFlow::Node sink) {
3434
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(src, sink, _)
3535
}
3636

37+
private Type getElementType(Type containerType) {
38+
result = containerType.(ArrayType).getElementType() or
39+
result = containerType.(SliceType).getElementType() or
40+
result = containerType.(ChanType).getElementType() or
41+
result = containerType.(MapType).getValueType() or
42+
result = containerType.(PointerType).getPointerType()
43+
}
44+
3745
/**
3846
* Holds if default `TaintTracking::Configuration`s should allow implicit reads
3947
* of `c` at sinks and inputs to additional taint steps.
4048
*/
4149
bindingset[node]
42-
predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::ContentSet c) {
43-
exists(Type containerType |
50+
predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::ContentSet cs) {
51+
exists(Type containerType, DataFlow::Content c |
4452
node instanceof DataFlow::ArgumentNode and
45-
DataFlowPrivate::getElementType*(node.getType()) = containerType
53+
getElementType*(node.getType()) = containerType and
54+
cs.asOneContent() = c
4655
|
47-
c = DataFlowPrivate::getContentForType(containerType)
56+
containerType instanceof ArrayType and
57+
c instanceof DataFlow::ArrayContent
58+
or
59+
containerType instanceof SliceType and
60+
c instanceof DataFlow::ArrayContent
61+
or
62+
containerType instanceof ChanType and
63+
c instanceof DataFlow::CollectionContent
64+
or
65+
containerType instanceof MapType and
66+
c instanceof DataFlow::MapValueContent
67+
or
68+
c.(DataFlow::PointerContent).getPointerType() = containerType
4869
)
4970
}
5071

@@ -122,7 +143,7 @@ predicate elementWriteStep(DataFlow::Node pred, DataFlow::Node succ) {
122143
any(DataFlow::Write w).writesElement(succ.(DataFlow::PostUpdateNode).getPreUpdateNode(), _, pred)
123144
or
124145
FlowSummaryImpl::Private::Steps::summaryStoreStep(pred.(DataFlowPrivate::FlowSummaryNode)
125-
.getSummaryNode(), any(DataFlow::Content c | c instanceof DataFlow::ArrayContent),
146+
.getSummaryNode(), any(DataFlow::ArrayContent ac).asContentSet(),
126147
succ.(DataFlowPrivate::FlowSummaryNode).getSummaryNode())
127148
}
128149

go/ql/lib/semmle/go/frameworks/stdlib/NetHttp.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ module NetHttp {
122122
|
123123
lastParamIndex = call.getCall().getCalleeType().getNumParameter() - 1 and
124124
varArgsSliceArgument = SummaryComponentStack::argument(lastParamIndex) and
125-
arrayContentSC = SummaryComponent::content(arrayContent) and
125+
arrayContentSC = SummaryComponent::content(arrayContent.asContentSet()) and
126126
stack = SummaryComponentStack::push(arrayContentSC, varArgsSliceArgument)
127127
)
128128
else stack = SummaryComponentStack::argument(n)

go/ql/test/library-tests/semmle/go/frameworks/serialization/texttemplate.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@ func test() {
4343
buff1.Read(bytes1)
4444
sink(bytes1) // $ hasTaintFlow=1 hasTaintFlow=2 hasTaintFlow=3
4545

46-
tmpl.Execute(buff2, toSerialize)
46+
// Read `buff2` via an `any`-typed variable, to ensure the static type of the argument to tmpl.Execute makes no difference to the result
47+
var toSerializeAsAny any
48+
toSerializeAsAny = toSerialize
49+
tmpl.Execute(buff2, toSerializeAsAny)
4750
buff2.Read(bytes2)
4851
sink(bytes2) // $ hasTaintFlow=1 hasTaintFlow=2 hasTaintFlow=3
4952

0 commit comments

Comments
 (0)