Skip to content

Commit 66d4fbc

Browse files
committed
Non-initializing writes should target post-update nodes
1 parent 2d8986d commit 66d4fbc

File tree

37 files changed

+374
-256
lines changed

37 files changed

+374
-256
lines changed

go/ql/lib/semmle/go/controlflow/ControlFlowGraph.qll

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ module ControlFlow {
118118
/** Gets the left-hand side of this write. */
119119
IR::WriteTarget getLhs() { result = super.getLhs() }
120120

121+
private predicate isInitialization() { super.isInitialization() }
122+
121123
/** Gets the right-hand side of this write. */
122124
DataFlow::Node getRhs() { super.getRhs() = result.asInstruction() }
123125

@@ -134,13 +136,20 @@ module ControlFlow {
134136
* Holds if this node sets the value of field `f` on `base` (or its implicit dereference) to
135137
* `rhs`.
136138
*
137-
* For example, for the assignment `x.width = newWidth`, `base` is either the data-flow node
138-
* corresponding to `x` or (if `x` is a pointer) the data-flow node corresponding to the
139-
* implicit dereference `*x`, `f` is the field referenced by `width`, and `rhs` is the data-flow
140-
* node corresponding to `newWidth`.
139+
* For example, for the assignment `x.width = newWidth`, `base` is the post-update node of
140+
* either the data-flow node corresponding to `x` or (if `x` is a pointer) the data-flow node
141+
* corresponding to the implicit dereference `*x`, `f` is the field referenced by `width`, and
142+
* `rhs` is the data-flow node corresponding to `newWidth`. If this `WriteNode` is a struct
143+
* initialization then there is no need for a post-update node and `base` is the struct literal
144+
* being initialized.
141145
*/
142146
predicate writesField(DataFlow::Node base, Field f, DataFlow::Node rhs) {
143-
this.writesFieldInsn(base.asInstruction(), f, rhs.asInstruction())
147+
exists(DataFlow::Node b | this.writesFieldInsn(b.asInstruction(), f, rhs.asInstruction()) |
148+
this.isInitialization() and base = b
149+
or
150+
not this.isInitialization() and
151+
b = base.(DataFlow::PostUpdateNode).getPreUpdateNode()
152+
)
144153
}
145154

146155
private predicate writesFieldInsn(IR::Instruction base, Field f, IR::Instruction rhs) {
@@ -158,13 +167,22 @@ module ControlFlow {
158167
* Holds if this node sets the value of element `index` on `base` (or its implicit dereference)
159168
* to `rhs`.
160169
*
161-
* For example, for the assignment `xs[i] = v`, `base` is either the data-flow node
162-
* corresponding to `xs` or (if `xs` is a pointer) the data-flow node corresponding to the
163-
* implicit dereference `*xs`, `index` is the data-flow node corresponding to `i`, and `rhs`
164-
* is the data-flow node corresponding to `base`.
170+
* For example, for the assignment `xs[i] = v`, `base` is the post-update node of the data-flow
171+
* node corresponding to `xs` or (if `xs` is a pointer) the implicit dereference `*xs`, `index`
172+
* is the data-flow node corresponding to `i`, and `rhs` is the data-flow node corresponding to
173+
* `base`. If this `WriteNode` corresponds to the initialization of an array/slice/map then
174+
* there is no need for a post-update node and `base` is the array/slice/map literal being
175+
* initialized.
165176
*/
166177
predicate writesElement(DataFlow::Node base, DataFlow::Node index, DataFlow::Node rhs) {
167-
this.writesElementInsn(base.asInstruction(), index.asInstruction(), rhs.asInstruction())
178+
exists(DataFlow::Node b |
179+
this.writesElementInsn(b.asInstruction(), index.asInstruction(), rhs.asInstruction())
180+
|
181+
this.isInitialization() and base = b
182+
or
183+
not this.isInitialization() and
184+
b = base.(DataFlow::PostUpdateNode).getPreUpdateNode()
185+
)
168186
}
169187

170188
private predicate writesElementInsn(
@@ -184,7 +202,7 @@ module ControlFlow {
184202
* Holds if this node sets any field or element of `base` to `rhs`.
185203
*/
186204
predicate writesComponent(DataFlow::Node base, DataFlow::Node rhs) {
187-
this.writesComponentInstruction(base.asInstruction(), rhs.asInstruction())
205+
this.writesElement(base, _, rhs) or this.writesField(base, _, rhs)
188206
}
189207

190208
/**

go/ql/lib/semmle/go/controlflow/IR.qll

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -430,18 +430,24 @@ module IR {
430430
*/
431431
class WriteInstruction extends Instruction {
432432
WriteTarget lhs;
433+
Boolean initialization;
433434

434435
WriteInstruction() {
435-
lhs = MkLhs(this, _)
436-
or
437-
lhs = MkLiteralElementTarget(this)
436+
(
437+
lhs = MkLhs(this, _)
438+
or
439+
lhs = MkResultWriteTarget(this)
440+
) and
441+
initialization = false
438442
or
439-
lhs = MkResultWriteTarget(this)
443+
lhs = MkLiteralElementTarget(this) and initialization = true
440444
}
441445

442446
/** Gets the target to which this instruction writes. */
443447
WriteTarget getLhs() { result = lhs }
444448

449+
predicate isInitialization() { initialization = true }
450+
445451
/** Gets the instruction computing the value this instruction writes. */
446452
Instruction getRhs() { none() }
447453

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ predicate containerStoreStep(Node node1, Node node2, Content c) {
2222
t instanceof SliceType
2323
) and
2424
(
25-
exists(Write w | w.writesElement(node2.(PostUpdateNode).getPreUpdateNode(), _, node1))
25+
exists(Write w | w.writesElement(node2, _, node1))
2626
or
2727
node1 = node2.(ImplicitVarargsSlice).getCallNode().getAnImplicitVarargsArgument()
2828
or
@@ -44,11 +44,11 @@ predicate containerStoreStep(Node node1, Node node2, Content c) {
4444
or
4545
c instanceof MapKeyContent and
4646
t instanceof MapType and
47-
exists(Write w | w.writesElement(node2.(PostUpdateNode).getPreUpdateNode(), node1, _))
47+
exists(Write w | w.writesElement(node2, node1, _))
4848
or
4949
c instanceof MapValueContent and
5050
t instanceof MapType and
51-
exists(Write w | w.writesElement(node2.(PostUpdateNode).getPreUpdateNode(), _, node1))
51+
exists(Write w | w.writesElement(node2, _, node1))
5252
)
5353
}
5454

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ predicate storeStep(Node node1, ContentSet cs, Node node2) {
156156
// which in turn flows into the pointer content of `p`
157157
exists(Write w, Field f, DataFlow::Node base, DataFlow::Node rhs | w.writesField(base, f, rhs) |
158158
node1 = rhs and
159-
node2.(PostUpdateNode).getPreUpdateNode() = base and
159+
node2 = base and
160160
c = any(DataFlow::FieldContent fc | fc.getField() = f)
161161
or
162162
node1 = base and

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -435,13 +435,20 @@ module SourceSinkInterpretationInput implements
435435
mid.asCallable() = getNodeEnclosingCallable(ret)
436436
)
437437
or
438-
exists(SourceOrSinkElement e, DataFlow::Write fw, DataFlow::Node base, Field f |
438+
exists(
439+
SourceOrSinkElement e, DataFlow::Write fw, DataFlow::Node base, DataFlow::Node qual, Field f
440+
|
439441
e = mid.asElement() and
440442
f = e.asFieldEntity()
441443
|
442444
c = "" and
443445
fw.writesField(base, f, node.asNode()) and
444-
pragma[only_bind_into](e) = getElementWithQualifier(f, base)
446+
pragma[only_bind_into](e) = getElementWithQualifier(f, qual) and
447+
(
448+
qual = base.(PostUpdateNode).getPreUpdateNode()
449+
or
450+
not base instanceof PostUpdateNode and qual = base
451+
)
445452
)
446453
or
447454
// A package-scope (or universe-scope) variable

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ predicate referenceStep(DataFlow::Node pred, DataFlow::Node succ) {
144144
* `succ`.
145145
*/
146146
predicate elementWriteStep(DataFlow::Node pred, DataFlow::Node succ) {
147-
any(DataFlow::Write w).writesElement(succ.(DataFlow::PostUpdateNode).getPreUpdateNode(), _, pred)
147+
any(DataFlow::Write w).writesElement(succ, _, pred)
148148
or
149149
FlowSummaryImpl::Private::Steps::summaryStoreStep(pred.(DataFlowPrivate::FlowSummaryNode)
150150
.getSummaryNode(), any(DataFlow::ArrayContent ac).asContentSet(),

go/ql/lib/semmle/go/frameworks/GinCors.qll

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,15 @@ module GinCors {
2525
DataFlow::Node base;
2626

2727
AllowCredentialsWrite() {
28-
exists(Field f, Write w |
28+
exists(Field f, Write w, DataFlow::Node n |
2929
f.hasQualifiedName(packagePath(), "Config", "AllowCredentials") and
30-
w.writesField(base, f, this) and
31-
this.getType() instanceof BoolType
30+
w.writesField(n, f, this) and
31+
this.getType() instanceof BoolType and
32+
(
33+
base = n.(DataFlow::PostUpdateNode).getPreUpdateNode()
34+
or
35+
not n instanceof DataFlow::PostUpdateNode and base = n
36+
)
3237
)
3338
}
3439

@@ -59,10 +64,15 @@ module GinCors {
5964
DataFlow::Node base;
6065

6166
AllowOriginsWrite() {
62-
exists(Field f, Write w |
67+
exists(Field f, Write w, DataFlow::Node n |
6368
f.hasQualifiedName(packagePath(), "Config", "AllowOrigins") and
64-
w.writesField(base, f, this) and
65-
this.asExpr() instanceof SliceLit
69+
w.writesField(n, f, this) and
70+
this.asExpr() instanceof SliceLit and
71+
(
72+
base = n.(DataFlow::PostUpdateNode).getPreUpdateNode()
73+
or
74+
not n instanceof DataFlow::PostUpdateNode and base = n
75+
)
6676
)
6777
}
6878

@@ -93,10 +103,15 @@ module GinCors {
93103
DataFlow::Node base;
94104

95105
AllowAllOriginsWrite() {
96-
exists(Field f, Write w |
106+
exists(Field f, Write w, DataFlow::Node n |
97107
f.hasQualifiedName(packagePath(), "Config", "AllowAllOrigins") and
98-
w.writesField(base, f, this) and
99-
this.getType() instanceof BoolType
108+
w.writesField(n, f, this) and
109+
this.getType() instanceof BoolType and
110+
(
111+
base = n.(DataFlow::PostUpdateNode).getPreUpdateNode()
112+
or
113+
not n instanceof DataFlow::PostUpdateNode and base = n
114+
)
100115
)
101116
}
102117

@@ -109,14 +124,9 @@ module GinCors {
109124
* Get config variable holding header values
110125
*/
111126
override GinConfig getConfig() {
112-
exists(GinConfig gc |
113-
(
114-
gc.getV().getBaseVariable().getDefinition().(SsaExplicitDefinition).getRhs() =
115-
base.asInstruction() or
116-
gc.getV().getAUse() = base
117-
) and
118-
result = gc
119-
)
127+
result.getV().getBaseVariable().getDefinition().(SsaExplicitDefinition).getRhs() =
128+
base.asInstruction() or
129+
result.getV().getAUse() = base
120130
}
121131
}
122132

go/ql/lib/semmle/go/frameworks/NoSQL.qll

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,8 @@ module NoSql {
3838
*/
3939
predicate isAdditionalMongoTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
4040
// Taint an entry if the `Value` is tainted
41-
exists(Write w, DataFlow::Node base, Field f | w.writesField(base, f, pred) |
42-
base = succ.(DataFlow::PostUpdateNode).getPreUpdateNode() and
43-
base.getType().hasQualifiedName(package("go.mongodb.org/mongo-driver", "bson/primitive"), "E") and
41+
exists(Write w, Field f | w.writesField(succ, f, pred) |
42+
succ.getType().hasQualifiedName(package("go.mongodb.org/mongo-driver", "bson/primitive"), "E") and
4443
f.getName() = "Value"
4544
)
4645
}

go/ql/lib/semmle/go/frameworks/Protobuf.qll

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,10 @@ module Protobuf {
6464
*/
6565
private class MarshalStateStep extends TaintTracking::AdditionalTaintStep {
6666
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
67-
exists(DataFlow::PostUpdateNode marshalInput, DataFlow::CallNode marshalStateCall |
67+
exists(DataFlow::Node marshalInput, DataFlow::CallNode marshalStateCall |
6868
marshalStateCall = marshalStateMethod().getACall() and
6969
// pred -> marshalInput.Message
70-
any(DataFlow::Write w)
71-
.writesField(marshalInput.getPreUpdateNode(), inputMessageField(), pred) and
70+
any(DataFlow::Write w).writesField(marshalInput, inputMessageField(), pred) and
7271
// marshalInput -> marshalStateCall
7372
marshalStateCall.getArgument(0) = globalValueNumber(marshalInput).getANode() and
7473
// marshalStateCall -> succ
@@ -142,10 +141,13 @@ module Protobuf {
142141
private class WriteMessageFieldStep extends TaintTracking::AdditionalTaintStep {
143142
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
144143
[succ.getType(), succ.getType().getPointerType()] instanceof MessageType and
145-
exists(DataFlow::ReadNode base |
144+
exists(DataFlow::Node n, DataFlow::ReadNode base |
146145
succ.(DataFlow::PostUpdateNode).getPreUpdateNode() = getUnderlyingNode(base)
147146
|
148-
any(DataFlow::Write w).writesComponent(base, pred)
147+
any(DataFlow::Write w).writesComponent(n, pred) and
148+
// The below line only works because `base`'s type, `DataFlow::ReadNode`,
149+
// is incompatible with `DataFlow::PostUpdateNode`.
150+
base = [n, n.(DataFlow::PostUpdateNode).getPreUpdateNode()]
149151
)
150152
}
151153
}

go/ql/lib/semmle/go/frameworks/RsCors.qll

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,15 @@ module RsCors {
5252
DataFlow::Node base;
5353

5454
AllowCredentialsWrite() {
55-
exists(Field f, Write w |
55+
exists(Field f, Write w, DataFlow::Node n |
5656
f.hasQualifiedName(packagePath(), "Options", "AllowCredentials") and
57-
w.writesField(base, f, this) and
58-
this.getType() instanceof BoolType
57+
w.writesField(n, f, this) and
58+
this.getType() instanceof BoolType and
59+
(
60+
base = n.(DataFlow::PostUpdateNode).getPreUpdateNode()
61+
or
62+
not n instanceof DataFlow::PostUpdateNode and base = n
63+
)
5964
)
6065
}
6166

@@ -80,10 +85,15 @@ module RsCors {
8085
DataFlow::Node base;
8186

8287
AllowOriginsWrite() {
83-
exists(Field f, Write w |
88+
exists(Field f, Write w, DataFlow::Node n |
8489
f.hasQualifiedName(packagePath(), "Options", "AllowedOrigins") and
85-
w.writesField(base, f, this) and
86-
this.asExpr() instanceof SliceLit
90+
w.writesField(n, f, this) and
91+
this.asExpr() instanceof SliceLit and
92+
(
93+
base = n.(DataFlow::PostUpdateNode).getPreUpdateNode()
94+
or
95+
not n instanceof DataFlow::PostUpdateNode and base = n
96+
)
8797
)
8898
}
8999

@@ -111,10 +121,15 @@ module RsCors {
111121
DataFlow::Node base;
112122

113123
AllowAllOriginsWrite() {
114-
exists(Field f, Write w |
124+
exists(Field f, Write w, DataFlow::Node n |
115125
f.hasQualifiedName(packagePath(), "Options", "AllowAllOrigins") and
116-
w.writesField(base, f, this) and
117-
this.getType() instanceof BoolType
126+
w.writesField(n, f, this) and
127+
this.getType() instanceof BoolType and
128+
(
129+
base = n.(DataFlow::PostUpdateNode).getPreUpdateNode()
130+
or
131+
not n instanceof DataFlow::PostUpdateNode and base = n
132+
)
118133
)
119134
}
120135

0 commit comments

Comments
 (0)