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