Skip to content
This repository was archived by the owner on Jan 5, 2023. It is now read-only.

Commit fb85ccb

Browse files
committed
Look through implicit deref operations when propagating taint down a chain of field- and element-access instructions.
This enables us to use PostUpdateNode properly. Also introduce a test showing a case where this doesn't work, because the underlying variable doesn't have a post-update node.
1 parent 3635d7d commit fb85ccb

File tree

3 files changed

+39
-3
lines changed

3 files changed

+39
-3
lines changed

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

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,12 +140,22 @@ module Protobuf {
140140
}
141141

142142
/**
143-
* Gets the data-flow node representing the bottom of a stack of zero or more `ComponentReadNode`s.
143+
* Gets the base of `node`, looking through any dereference node found.
144+
*/
145+
private DataFlow::Node getBaseLookingThroughDerefs(DataFlow::ComponentReadNode node) {
146+
result = node.getBase().(DataFlow::PointerDereferenceNode).getOperand()
147+
or
148+
result = node.getBase() and not node.getBase() instanceof DataFlow::PointerDereferenceNode
149+
}
150+
151+
/**
152+
* Gets the data-flow node representing the bottom of a stack of zero or more `ComponentReadNode`s
153+
* perhaps with interleaved dereferences.
144154
*
145155
* For example, in the expression a.b[c].d[e], this would return the dataflow node for the read from `a`.
146156
*/
147157
DataFlow::Node getUnderlyingNode(DataFlow::ReadNode read) {
148-
(result = read or result = read.(DataFlow::ComponentReadNode).getBase+()) and
158+
(result = read or result = getBaseLookingThroughDerefs+(read)) and
149159
not result instanceof DataFlow::ComponentReadNode
150160
}
151161

@@ -155,7 +165,9 @@ module Protobuf {
155165
private class WriteMessageFieldStep extends TaintTracking::AdditionalTaintStep {
156166
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
157167
[succ.getType(), succ.getType().getPointerType()] instanceof MessageType and
158-
exists(DataFlow::ReadNode base | succ = getUnderlyingNode(base) |
168+
exists(DataFlow::ReadNode base |
169+
succ.(DataFlow::PostUpdateNode).getPreUpdateNode() = getUnderlyingNode(base)
170+
|
159171
any(DataFlow::Write w).writesComponent(base, pred)
160172
)
161173
}

ql/test/library-tests/semmle/go/frameworks/Protobuf/testDeprecatedApi.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,15 @@ func testTaintedMapFieldReadViaAlias() {
167167

168168
sinkString((*alias)[123]) // BAD
169169
}
170+
171+
func testTaintedSubmessageInPlaceNonPointerBase() {
172+
alert := query.Query_Alert{}
173+
174+
query := query.Query{}
175+
query.Alerts = append(query.Alerts, &alert)
176+
query.Alerts[0].Msg = getUntrustedString()
177+
178+
serialized, _ := proto.Marshal(query)
179+
180+
sinkBytes(serialized) // BAD (but not detected by our current analysis)
181+
}

ql/test/library-tests/semmle/go/frameworks/Protobuf/testModernApi.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,3 +224,15 @@ func testTaintedMapFieldReadViaAliasModern() {
224224

225225
sinkString((*alias)[123]) // BAD
226226
}
227+
228+
func testTaintedSubmessageInPlaceNonPointerBaseModern() {
229+
alert := query.Query_Alert{}
230+
231+
query := query.Query{}
232+
query.Alerts = append(query.Alerts, &alert)
233+
query.Alerts[0].Msg = getUntrustedString()
234+
235+
serialized, _ := proto.Marshal(query)
236+
237+
sinkBytes(serialized) // BAD (but not detected by our current implementation)
238+
}

0 commit comments

Comments
 (0)