-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathProtobuf.qll
More file actions
153 lines (128 loc) · 5.73 KB
/
Protobuf.qll
File metadata and controls
153 lines (128 loc) · 5.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/** Provides models of commonly used functions and types in the protobuf packages. */
overlay[local?]
module;
import go
/** Provides models of commonly used functions and types in the protobuf packages. */
module Protobuf {
/** Gets the name of the modern protobuf top-level implementation package. */
string modernProtobufPackage() { result = package("google.golang.org/protobuf", "proto") }
/** Gets the name of the modern protobuf implementation's `protoiface` subpackage. */
string protobufIfacePackage() {
result = package("google.golang.org/protobuf", "runtime/protoiface")
}
/** Gets the name of the modern protobuf implementation's `protoreflect` subpackage. */
string protobufReflectPackage() {
result = package("google.golang.org/protobuf", "reflect/protoreflect")
}
/** Gets the name of a top-level protobuf implementation package. */
string protobufPackages() {
result in [package("github.com/golang/protobuf", "proto"), modernProtobufPackage()]
}
/** The `Marshal` and `MarshalAppend` functions in the protobuf packages. */
private class MarshalFunction extends MarshalingFunction::Range {
string name;
MarshalFunction() {
name = ["Marshal", "MarshalAppend"] and
(
this.hasQualifiedName(protobufPackages(), name) or
this.(Method).hasQualifiedName(modernProtobufPackage(), "MarshalOptions", name)
)
}
override DataFlow::FunctionInput getAnInput() {
if name = "MarshalAppend" then result.isParameter(1) else result.isParameter(0)
}
override DataFlow::FunctionOutput getOutput() {
name = "MarshalAppend" and result.isParameter(0)
or
result.isResult(0)
}
override string getFormat() { result = "protobuf" }
}
private Field inputMessageField() {
result.hasQualifiedName(protobufIfacePackage(), "MarshalInput", "Message")
}
private Method marshalStateMethod() {
result.hasQualifiedName(protobufIfacePackage(), "MarshalOptions", "MarshalState")
}
/**
* Additional taint-flow step modeling flow from `MarshalInput.Message` to `MarshalOutput`,
* mediated by a `MarshalOptions.MarshalState` call.
*
* Note we can taint the whole `MarshalOutput` as it only has one field (`Buf`), and taint-
* tracking always considers a field of a tainted struct to itself be tainted.
*/
private class MarshalStateStep extends TaintTracking::AdditionalTaintStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::Node marshalInput, DataFlow::CallNode marshalStateCall |
marshalStateCall = marshalStateMethod().getACall() and
// pred -> marshalInput.Message
any(DataFlow::Write w).writesField(marshalInput, inputMessageField(), pred) and
// marshalInput -> marshalStateCall
marshalStateCall.getArgument(0) = globalValueNumber(marshalInput).getANode() and
// marshalStateCall -> succ
marshalStateCall.getResult() = succ
)
}
}
/** The `Unmarshal` function in the protobuf packages. */
class UnmarshalFunction extends UnmarshalingFunction::Range {
UnmarshalFunction() {
this.hasQualifiedName(protobufPackages(), "Unmarshal") or
this.(Method).hasQualifiedName(modernProtobufPackage(), "UnmarshalOptions", "Unmarshal")
}
override DataFlow::FunctionInput getAnInput() { result.isParameter(0) }
override DataFlow::FunctionOutput getOutput() { result.isParameter(1) }
override string getFormat() { result = "protobuf" }
}
/** A protobuf `Message` type. */
class MessageType extends Type {
MessageType() { this.implements(protobufReflectPackage(), "ProtoMessage") }
}
/** A `Get` method of a protobuf `Message` type. */
class GetMethod extends TaintTracking::FunctionModel, Method {
GetMethod() {
exists(string name | name.matches("Get%") | this = any(MessageType msg).getMethod(name))
}
override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) {
inp.isReceiver() and outp.isResult()
}
}
/** A `ProtoReflect` method of a protobuf `Message` type. */
private class ProtoReflectMethod extends TaintTracking::FunctionModel, Method {
ProtoReflectMethod() { this = any(MessageType msg).getMethod("ProtoReflect") }
override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) {
inp.isReceiver() and outp.isResult()
}
}
/**
* Gets the base of `node`, looking through any dereference node found.
*/
private DataFlow::Node getBaseLookingThroughDerefs(DataFlow::ComponentReadNode node) {
result = node.getBase().(DataFlow::PointerDereferenceNode).getOperand()
or
result = node.getBase() and not node.getBase() instanceof DataFlow::PointerDereferenceNode
}
/**
* Gets the data-flow node representing the bottom of a stack of zero or more `ComponentReadNode`s
* perhaps with interleaved dereferences.
*
* For example, in the expression a.b[c].d[e], this would return the dataflow node for the read from `a`.
*/
private DataFlow::Node getUnderlyingNode(DataFlow::ReadNode read) {
(result = read or result = getBaseLookingThroughDerefs+(read)) and
not result instanceof DataFlow::ComponentReadNode
}
/**
* Additional taint step tainting a Message when taint is written to any of its fields and/or elements.
*/
private class WriteMessageFieldStep extends TaintTracking::AdditionalTaintStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
[succ.getType(), succ.getType().getPointerType()] instanceof MessageType and
exists(DataFlow::Write w, DataFlow::ReadNode base |
w.writesElementPreUpdate(base, _, pred) or w.writesFieldPreUpdate(base, _, pred)
|
succ.(DataFlow::PostUpdateNode).getPreUpdateNode() = getUnderlyingNode(base)
)
}
}
}