Merge pull request #49 from max-schaefer/more-function-outputs

Make `FunctionOutput` more useful
This commit is contained in:
Sauyon Lee
2020-03-06 09:41:40 -08:00
committed by GitHub
7 changed files with 193 additions and 59 deletions

View File

@@ -97,9 +97,18 @@ private newtype TFunctionOutput =
// one among several results
exists(SignatureType s | exists(s.getResultType(index)))
}
or
TOutParameter(int index) {
// the receiver parameter
index = -1
or
// another parameter
exists(SignatureType s | exists(s.getParameterType(index)))
}
/**
* An abstract representation of an output of a function, which is one of its results.
* An abstract representation of an output of a function, which is one of its results
* or a parameter with mutable type.
*/
class FunctionOutput extends TFunctionOutput {
/** Holds if this represents the (single) result of a function. */
@@ -112,6 +121,16 @@ class FunctionOutput extends TFunctionOutput {
none()
}
/** Holds if this represents the receiver of a function. */
predicate isReceiver() {
none()
}
/** Holds if this represents the `i`th parameter of a function. */
predicate isParameter(int i) {
none()
}
/** Gets the data-flow node corresponding to this output for the call `c`. */
final DataFlow::Node getNode(DataFlow::CallNode c) { result = getExitNode(c) }
@@ -176,3 +195,39 @@ private class OutResult extends FunctionOutput, TOutResult {
index >= 0 and result = "result " + index
}
}
/** A result position of a function, viewed as an output. */
private class OutParameter extends FunctionOutput, TOutParameter {
int index;
OutParameter() {
this = TOutParameter(index)
}
override predicate isReceiver() {
index = -1
}
override predicate isParameter(int i) {
i = index and i >= 0
}
override DataFlow::Node getEntryNode(FuncDef f) {
// there is no generic way of assigning to a parameter; operations that taint a parameter
// have to be handled on a case-by-case basis
none()
}
override DataFlow::Node getExitNode(DataFlow::CallNode c) {
exists(DataFlow::ArgumentNode arg |
arg.argumentOf(c.asExpr(), index) and
result.(DataFlow::PostUpdateNode).getPreUpdateNode() = arg
)
}
override string toString() {
index = -1 and result = "receiver"
or
index >= 0 and result = "parameter " + index
}
}

View File

@@ -397,12 +397,15 @@ class PostUpdateNode extends Node {
or
preupd = base.(PointerDereferenceNode).getOperand()
)
or
preupd instanceof ArgumentNode and
mutableType(preupd.getType())
) and
(
preupd = this.(SsaNode).getAUse()
or
preupd = this and
not exists(getAPredecessor())
not basicLocalFlowStep(_, this)
)
}
@@ -412,6 +415,17 @@ class PostUpdateNode extends Node {
Node getPreUpdateNode() { result = preupd }
}
/**
* Gets the `i`th argument of call `c`, where the receiver of a method call
* counts as argument -1.
*/
private Node getArgument(CallNode c, int i) {
result = c.getArgument(i)
or
result = c.(MethodCallNode).getReceiver() and
i = -1
}
/**
* A data-flow node that occurs as an argument in a call, including receiver arguments.
*/
@@ -419,12 +433,7 @@ class ArgumentNode extends Node {
CallNode c;
int i;
ArgumentNode() {
this = c.getArgument(i)
or
this = c.(MethodCallNode).getReceiver() and
i = -1
}
ArgumentNode() { this = getArgument(c, i) }
/**
* Holds if this argument occurs at the given position in the given call.
@@ -449,6 +458,20 @@ class ArgumentNode extends Node {
CallNode getCall() { result = c }
}
/**
* Holds if `tp` is a type that may (directly or indirectly) reference a memory location.
*
* If a value with a mutable type is passed to a function, the function could potentially
* mutate it or something it points to.
*/
predicate mutableType(Type tp) {
tp instanceof ArrayType or
tp instanceof SliceType or
tp instanceof MapType or
tp instanceof PointerType or
tp instanceof InterfaceType
}
/**
* A node whose value is returned as a result from a function.
*
@@ -766,13 +789,10 @@ Node extractTupleElement(Node t, int i) {
predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFrom, nodeTo) }
/**
* INTERNAL: do not use.
*
* This is the local flow predicate that's used as a building block in global
* data flow. It may have less flow than the `localFlowStep` predicate.
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
* (intra-procedural) step, not taking function models into account.
*/
cached
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
private predicate basicLocalFlowStep(Node nodeFrom, Node nodeTo) {
// Instruction -> Instruction
exists(Expr pred, Expr succ |
succ.(LogicalBinaryExpr).getAnOperand() = pred or
@@ -807,7 +827,18 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
)
or
// GlobalFunctionNode -> use
nodeTo = MkGlobalFunctionNode(nodeFrom.asExpr().(FunctionName).getTarget())
nodeFrom = MkGlobalFunctionNode(nodeTo.asExpr().(FunctionName).getTarget())
}
/**
* INTERNAL: do not use.
*
* This is the local flow predicate that's used as a building block in global
* data flow. It may have less flow than the `localFlowStep` predicate.
*/
cached
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
basicLocalFlowStep(nodeFrom, nodeTo)
or
// step through function model
exists(FunctionModel m, CallNode c, FunctionInput inp, FunctionOutput outp |

View File

@@ -83,7 +83,8 @@ predicate tupleStep(DataFlow::Node pred, DataFlow::Node succ) {
/** Holds if taint flows from `pred` to `succ` via string concatenation. */
predicate stringConcatStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::BinaryOperationNode conc | conc.getOperator() = "+" |
exists(DataFlow::BinaryOperationNode conc |
conc.getOperator() = "+" and conc.getType() instanceof StringType |
succ = conc and conc.getAnOperand() = pred
)
}

View File

@@ -13,6 +13,30 @@ class StringMethod extends TaintTracking::FunctionModel, Method {
}
}
/**
* A model of the built-in `append` function, which propagates taint from its arguments to its
* result.
*/
private class AppendFunction extends TaintTracking::FunctionModel {
AppendFunction() { this = Builtin::append() }
override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) {
inp.isParameter(_) and outp.isResult()
}
}
/**
* A model of the built-in `copy` function, which propagates taint from its second argument
* to its first.
*/
private class CopyFunction extends TaintTracking::FunctionModel {
CopyFunction() { this = Builtin::copy() }
override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) {
inp.isParameter(1) and outp.isParameter(0)
}
}
/** Provides models of commonly used functions in the `path/filepath` package. */
module PathFilePath {
/** A path-manipulating function in the `path/filepath` package. */

View File

@@ -1,3 +1,44 @@
| file://:0:0:0:0 | function Encode | url.go:51:14:51:21 | selection of Encode |
| file://:0:0:0:0 | function EscapedPath | url.go:28:14:28:26 | selection of EscapedPath |
| file://:0:0:0:0 | function Get | url.go:52:14:52:18 | selection of Get |
| file://:0:0:0:0 | function Hostname | url.go:29:14:29:23 | selection of Hostname |
| file://:0:0:0:0 | function MarshalBinary | url.go:30:11:30:25 | selection of MarshalBinary |
| file://:0:0:0:0 | function Parse | url.go:23:10:23:18 | selection of Parse |
| file://:0:0:0:0 | function Parse | url.go:32:9:32:15 | selection of Parse |
| file://:0:0:0:0 | function ParseQuery | url.go:50:10:50:23 | selection of ParseQuery |
| file://:0:0:0:0 | function ParseRequestURI | url.go:27:9:27:27 | selection of ParseRequestURI |
| file://:0:0:0:0 | function Password | url.go:43:11:43:21 | selection of Password |
| file://:0:0:0:0 | function PathEscape | url.go:12:31:12:44 | selection of PathEscape |
| file://:0:0:0:0 | function PathUnescape | url.go:12:14:12:29 | selection of PathUnescape |
| file://:0:0:0:0 | function Port | url.go:33:14:33:19 | selection of Port |
| file://:0:0:0:0 | function Println | url.go:28:2:28:12 | selection of Println |
| file://:0:0:0:0 | function Println | url.go:29:2:29:12 | selection of Println |
| file://:0:0:0:0 | function Println | url.go:31:2:31:12 | selection of Println |
| file://:0:0:0:0 | function Println | url.go:33:2:33:12 | selection of Println |
| file://:0:0:0:0 | function Println | url.go:34:2:34:12 | selection of Println |
| file://:0:0:0:0 | function Println | url.go:35:2:35:12 | selection of Println |
| file://:0:0:0:0 | function Println | url.go:44:2:44:12 | selection of Println |
| file://:0:0:0:0 | function Println | url.go:45:2:45:12 | selection of Println |
| file://:0:0:0:0 | function Println | url.go:51:2:51:12 | selection of Println |
| file://:0:0:0:0 | function Println | url.go:52:2:52:12 | selection of Println |
| file://:0:0:0:0 | function Query | url.go:34:14:34:20 | selection of Query |
| file://:0:0:0:0 | function QueryEscape | url.go:14:32:14:46 | selection of QueryEscape |
| file://:0:0:0:0 | function QueryUnescape | url.go:14:14:14:30 | selection of QueryUnescape |
| file://:0:0:0:0 | function Replace | strings.go:9:8:9:22 | selection of Replace |
| file://:0:0:0:0 | function ReplaceAll | strings.go:10:8:10:25 | selection of ReplaceAll |
| file://:0:0:0:0 | function RequestURI | url.go:35:14:35:25 | selection of RequestURI |
| file://:0:0:0:0 | function ResolveReference | url.go:36:6:36:23 | selection of ResolveReference |
| file://:0:0:0:0 | function Sprint | strings.go:11:9:11:18 | selection of Sprint |
| file://:0:0:0:0 | function Sprintf | strings.go:11:30:11:40 | selection of Sprintf |
| file://:0:0:0:0 | function Sprintln | strings.go:11:54:11:65 | selection of Sprintln |
| file://:0:0:0:0 | function User | url.go:41:8:41:15 | selection of User |
| file://:0:0:0:0 | function UserPassword | url.go:42:7:42:22 | selection of UserPassword |
| file://:0:0:0:0 | function Username | url.go:45:14:45:24 | selection of Username |
| file://:0:0:0:0 | function append | main.go:39:8:39:13 | append |
| file://:0:0:0:0 | function append | main.go:40:8:40:13 | append |
| file://:0:0:0:0 | function copy | main.go:42:2:42:5 | copy |
| file://:0:0:0:0 | function make | main.go:41:8:41:11 | make |
| main.go:3:6:3:10 | function test1 | main.go:34:2:34:6 | test1 |
| main.go:3:12:3:12 | argument corresponding to x | main.go:3:12:3:12 | definition of x |
| main.go:3:12:3:12 | definition of x | main.go:5:5:5:5 | x |
| main.go:3:12:3:12 | definition of x | main.go:6:7:6:7 | x |
@@ -17,6 +58,8 @@
| main.go:10:7:10:27 | ...&&... | main.go:10:2:10:2 | definition of z |
| main.go:10:17:10:27 | ...>=... | main.go:10:7:10:27 | ...&&... |
| main.go:11:14:11:14 | z | main.go:11:9:11:15 | type conversion |
| main.go:14:6:14:10 | function test2 | main.go:34:8:34:12 | test2 |
| main.go:14:6:14:10 | function test2 | main.go:34:19:34:23 | test2 |
| main.go:15:2:15:4 | definition of acc | main.go:16:9:16:9 | capture variable acc |
| main.go:15:9:15:9 | 0 | main.go:15:2:15:4 | definition of acc |
| main.go:16:9:16:9 | capture variable acc | main.go:17:3:17:5 | acc |
@@ -34,23 +77,23 @@
| main.go:26:2:26:17 | ... := ...[1] | main.go:26:5:26:6 | definition of ok |
| main.go:26:5:26:6 | definition of ok | main.go:27:5:27:6 | ok |
| main.go:26:11:26:11 | x | main.go:26:11:26:17 | type assertion |
| main.go:34:2:34:6 | test1 | main.go:3:6:3:10 | function test1 |
| main.go:34:8:34:12 | test2 | main.go:14:6:14:10 | function test2 |
| main.go:34:19:34:23 | test2 | main.go:14:6:14:10 | function test2 |
| main.go:38:2:38:2 | definition of s | main.go:39:15:39:15 | s |
| main.go:38:2:38:2 | definition of s | main.go:40:15:40:15 | s |
| main.go:38:2:38:2 | definition of s | main.go:42:7:42:7 | s |
| main.go:38:7:38:20 | composite literal | main.go:38:2:38:2 | definition of s |
| main.go:39:2:39:3 | definition of s1 | main.go:40:18:40:19 | s1 |
| main.go:39:8:39:25 | call to append | main.go:39:2:39:3 | definition of s1 |
| main.go:41:2:41:3 | definition of s4 | main.go:42:10:42:11 | s4 |
| main.go:41:8:41:21 | call to make | main.go:41:2:41:3 | definition of s4 |
| strings.go:8:12:8:12 | argument corresponding to s | strings.go:8:12:8:12 | definition of s |
| strings.go:8:12:8:12 | definition of s | strings.go:9:24:9:24 | s |
| strings.go:8:12:8:12 | definition of s | strings.go:10:27:10:27 | s |
| strings.go:9:2:9:3 | definition of s2 | strings.go:11:20:11:21 | s2 |
| strings.go:9:2:9:3 | definition of s2 | strings.go:11:48:11:49 | s2 |
| strings.go:9:8:9:22 | selection of Replace | file://:0:0:0:0 | function Replace |
| strings.go:9:8:9:38 | call to Replace | strings.go:9:2:9:3 | definition of s2 |
| strings.go:10:2:10:3 | definition of s3 | strings.go:11:24:11:25 | s3 |
| strings.go:10:2:10:3 | definition of s3 | strings.go:11:67:11:68 | s3 |
| strings.go:10:8:10:25 | selection of ReplaceAll | file://:0:0:0:0 | function ReplaceAll |
| strings.go:10:8:10:42 | call to ReplaceAll | strings.go:10:2:10:3 | definition of s3 |
| strings.go:11:9:11:18 | selection of Sprint | file://:0:0:0:0 | function Sprint |
| strings.go:11:30:11:40 | selection of Sprintf | file://:0:0:0:0 | function Sprintf |
| strings.go:11:54:11:65 | selection of Sprintln | file://:0:0:0:0 | function Sprintln |
| url.go:8:12:8:12 | argument corresponding to b | url.go:8:12:8:12 | definition of b |
| url.go:8:12:8:12 | definition of b | url.go:11:5:11:5 | b |
| url.go:8:20:8:20 | argument corresponding to s | url.go:8:20:8:20 | definition of s |
@@ -60,14 +103,10 @@
| url.go:12:3:12:48 | ... = ...[0] | url.go:12:3:12:5 | definition of res |
| url.go:12:3:12:48 | ... = ...[1] | url.go:12:8:12:10 | definition of err |
| url.go:12:8:12:10 | definition of err | url.go:16:5:16:5 | err = phi(def@12:8, def@14:8) |
| url.go:12:14:12:29 | selection of PathUnescape | file://:0:0:0:0 | function PathUnescape |
| url.go:12:31:12:44 | selection of PathEscape | file://:0:0:0:0 | function PathEscape |
| url.go:14:3:14:5 | definition of res | url.go:16:5:16:5 | res = phi(def@12:3, def@14:3) |
| url.go:14:3:14:50 | ... = ...[0] | url.go:14:3:14:5 | definition of res |
| url.go:14:3:14:50 | ... = ...[1] | url.go:14:8:14:10 | definition of err |
| url.go:14:8:14:10 | definition of err | url.go:16:5:16:5 | err = phi(def@12:8, def@14:8) |
| url.go:14:14:14:30 | selection of QueryUnescape | file://:0:0:0:0 | function QueryUnescape |
| url.go:14:32:14:46 | selection of QueryEscape | file://:0:0:0:0 | function QueryEscape |
| url.go:16:5:16:5 | err = phi(def@12:8, def@14:8) | url.go:16:5:16:7 | err |
| url.go:16:5:16:5 | res = phi(def@12:3, def@14:3) | url.go:19:9:19:11 | res |
| url.go:22:12:22:12 | argument corresponding to i | url.go:22:12:22:12 | definition of i |
@@ -77,57 +116,30 @@
| url.go:22:19:22:19 | definition of s | url.go:27:29:27:29 | s |
| url.go:23:2:23:2 | definition of u | url.go:25:10:25:10 | u |
| url.go:23:2:23:21 | ... := ...[0] | url.go:23:2:23:2 | definition of u |
| url.go:23:10:23:18 | selection of Parse | file://:0:0:0:0 | function Parse |
| url.go:27:2:27:2 | definition of u | url.go:28:14:28:14 | u |
| url.go:27:2:27:2 | definition of u | url.go:29:14:29:14 | u |
| url.go:27:2:27:2 | definition of u | url.go:30:11:30:11 | u |
| url.go:27:2:27:2 | definition of u | url.go:32:9:32:9 | u |
| url.go:27:2:27:30 | ... = ...[0] | url.go:27:2:27:2 | definition of u |
| url.go:27:9:27:27 | selection of ParseRequestURI | file://:0:0:0:0 | function ParseRequestURI |
| url.go:28:2:28:12 | selection of Println | file://:0:0:0:0 | function Println |
| url.go:28:14:28:26 | selection of EscapedPath | file://:0:0:0:0 | function EscapedPath |
| url.go:29:2:29:12 | selection of Println | file://:0:0:0:0 | function Println |
| url.go:29:14:29:23 | selection of Hostname | file://:0:0:0:0 | function Hostname |
| url.go:30:2:30:3 | definition of bs | url.go:31:14:31:15 | bs |
| url.go:30:2:30:27 | ... := ...[0] | url.go:30:2:30:3 | definition of bs |
| url.go:30:11:30:25 | selection of MarshalBinary | file://:0:0:0:0 | function MarshalBinary |
| url.go:31:2:31:12 | selection of Println | file://:0:0:0:0 | function Println |
| url.go:32:2:32:2 | definition of u | url.go:33:14:33:14 | u |
| url.go:32:2:32:2 | definition of u | url.go:34:14:34:14 | u |
| url.go:32:2:32:2 | definition of u | url.go:35:14:35:14 | u |
| url.go:32:2:32:2 | definition of u | url.go:36:6:36:6 | u |
| url.go:32:2:32:2 | definition of u | url.go:36:25:36:25 | u |
| url.go:32:2:32:23 | ... = ...[0] | url.go:32:2:32:2 | definition of u |
| url.go:32:9:32:15 | selection of Parse | file://:0:0:0:0 | function Parse |
| url.go:33:2:33:12 | selection of Println | file://:0:0:0:0 | function Println |
| url.go:33:14:33:19 | selection of Port | file://:0:0:0:0 | function Port |
| url.go:34:2:34:12 | selection of Println | file://:0:0:0:0 | function Println |
| url.go:34:14:34:20 | selection of Query | file://:0:0:0:0 | function Query |
| url.go:35:2:35:12 | selection of Println | file://:0:0:0:0 | function Println |
| url.go:35:14:35:25 | selection of RequestURI | file://:0:0:0:0 | function RequestURI |
| url.go:36:2:36:2 | definition of u | url.go:37:9:37:9 | u |
| url.go:36:6:36:23 | selection of ResolveReference | file://:0:0:0:0 | function ResolveReference |
| url.go:36:6:36:26 | call to ResolveReference | url.go:36:2:36:2 | definition of u |
| url.go:41:8:41:15 | selection of User | file://:0:0:0:0 | function User |
| url.go:42:2:42:3 | definition of ui | url.go:43:11:43:12 | ui |
| url.go:42:2:42:3 | definition of ui | url.go:45:14:45:15 | ui |
| url.go:42:2:42:3 | definition of ui | url.go:46:9:46:10 | ui |
| url.go:42:7:42:22 | selection of UserPassword | file://:0:0:0:0 | function UserPassword |
| url.go:42:7:42:38 | call to UserPassword | url.go:42:2:42:3 | definition of ui |
| url.go:43:2:43:3 | definition of pw | url.go:44:14:44:15 | pw |
| url.go:43:2:43:23 | ... := ...[0] | url.go:43:2:43:3 | definition of pw |
| url.go:43:11:43:21 | selection of Password | file://:0:0:0:0 | function Password |
| url.go:44:2:44:12 | selection of Println | file://:0:0:0:0 | function Println |
| url.go:45:2:45:12 | selection of Println | file://:0:0:0:0 | function Println |
| url.go:45:14:45:24 | selection of Username | file://:0:0:0:0 | function Username |
| url.go:49:12:49:12 | argument corresponding to q | url.go:49:12:49:12 | definition of q |
| url.go:49:12:49:12 | definition of q | url.go:50:25:50:25 | q |
| url.go:50:2:50:2 | definition of v | url.go:51:14:51:14 | v |
| url.go:50:2:50:2 | definition of v | url.go:52:14:52:14 | v |
| url.go:50:2:50:2 | definition of v | url.go:53:9:53:9 | v |
| url.go:50:2:50:26 | ... := ...[0] | url.go:50:2:50:2 | definition of v |
| url.go:50:10:50:23 | selection of ParseQuery | file://:0:0:0:0 | function ParseQuery |
| url.go:51:2:51:12 | selection of Println | file://:0:0:0:0 | function Println |
| url.go:51:14:51:21 | selection of Encode | file://:0:0:0:0 | function Encode |
| url.go:52:2:52:12 | selection of Println | file://:0:0:0:0 | function Println |
| url.go:52:14:52:18 | selection of Get | file://:0:0:0:0 | function Get |

View File

@@ -1,9 +1,12 @@
| main.go:10:22:10:22 | x | main.go:10:22:10:27 | ...+... |
| main.go:10:24:10:27 | call to fn | main.go:10:22:10:27 | ...+... |
| main.go:17:3:17:5 | acc | main.go:17:3:17:7 | rhs of increment statement |
| main.go:17:3:17:7 | 1 | main.go:17:3:17:7 | rhs of increment statement |
| main.go:26:11:26:17 | type assertion | main.go:26:2:26:17 | ... := ...[0] |
| main.go:26:11:26:17 | type assertion | main.go:26:2:26:17 | ... := ...[1] |
| main.go:39:15:39:15 | s | main.go:39:8:39:25 | call to append |
| main.go:39:18:39:18 | 4 | main.go:39:8:39:25 | call to append |
| main.go:39:21:39:21 | 5 | main.go:39:8:39:25 | call to append |
| main.go:39:24:39:24 | 6 | main.go:39:8:39:25 | call to append |
| main.go:40:15:40:15 | s | main.go:40:8:40:23 | call to append |
| main.go:40:18:40:19 | s1 | main.go:40:8:40:23 | call to append |
| main.go:42:10:42:11 | s4 | main.go:38:2:38:2 | definition of s |
| strings.go:9:24:9:24 | s | strings.go:9:8:9:38 | call to Replace |
| strings.go:9:32:9:34 | "_" | strings.go:9:8:9:38 | call to Replace |
| strings.go:10:27:10:27 | s | strings.go:10:8:10:42 | call to ReplaceAll |

View File

@@ -33,3 +33,11 @@ func test3(b bool, x interface{}) string {
func main() {
test1(test2()(), test2())
}
func test9() {
s := []int{1, 2, 3}
s1 := append(s, 4, 5, 6)
s2 := append(s, s1...)
s4 := make([]int, 4)
copy(s, s4)
}