Merge pull request #2116 from erik-krogh/arrayCBRet

Approved by max-schaefer
This commit is contained in:
semmle-qlci
2019-11-05 11:32:13 +00:00
committed by GitHub
5 changed files with 221 additions and 127 deletions

View File

@@ -82,17 +82,87 @@ predicate alwaysThrows(Function f) {
)
}
from DataFlow::CallNode call
where
not call.isIndefinite(_) and
forex(Function f | f = call.getACallee() |
/**
* Holds if the last statement in the function is flagged by the js/useless-expression query.
*/
predicate lastStatementHasNoEffect(Function f) {
hasNoEffect(f.getExit().getAPredecessor())
}
/**
* Holds if `func` is a callee of `call`, and all possible callees of `call` never return a value.
*/
predicate callToVoidFunction(DataFlow::CallNode call, Function func) {
not call.isIncomplete() and
func = call.getACallee() and
forall(Function f | f = call.getACallee() |
returnsVoid(f) and not isStub(f) and not alwaysThrows(f)
)
}
/**
* Holds if `name` is the name of a method from `Array.prototype` or Lodash,
* where that method takes a callback as parameter,
* and the callback is expected to return a value.
*/
predicate hasNonVoidCallbackMethod(string name) {
name = "every" or
name = "filter" or
name = "find" or
name = "findIndex" or
name = "flatMap" or
name = "map" or
name = "reduce" or
name = "reduceRight" or
name = "some" or
name = "sort"
}
DataFlow::SourceNode array(DataFlow::TypeTracker t) {
t.start() and result instanceof DataFlow::ArrayCreationNode
or
exists (DataFlow::TypeTracker t2 |
result = array(t2).track(t2, t)
)
}
DataFlow::SourceNode array() { result = array(DataFlow::TypeTracker::end()) }
/**
* Holds if `call` is an Array or Lodash method accepting a callback `func`,
* where the `call` expects a callback that returns an expression,
* but `func` does not return a value.
*/
predicate voidArrayCallback(DataFlow::CallNode call, Function func) {
hasNonVoidCallbackMethod(call.getCalleeName()) and
exists(int index |
index = min(int i | exists(call.getCallback(i))) and
func = call.getCallback(index).getFunction()
) and
returnsVoid(func) and
not isStub(func) and
not alwaysThrows(func) and
(
call.getReceiver().getALocalSource() = array()
or
call.getCalleeNode().getALocalSource() instanceof LodashUnderscore::Member
)
}
from DataFlow::CallNode call, Function func, string name, string msg
where
(
callToVoidFunction(call, func) and
msg = "the $@ does not return anything, yet the return value is used." and
name = func.describe()
or
voidArrayCallback(call, func) and
msg = "the $@ does not return anything, yet the return value from the call to " + call.getCalleeName() + " is used." and
name = "callback function"
) and
not benignContext(call.asExpr()) and
not lastStatementHasNoEffect(func) and
// anonymous one-shot closure. Those are used in weird ways and we ignore them.
not oneshotClosure(call.asExpr())
select
call, "the function $@ does not return anything, yet the return value is used.", call.getACallee(), call.getCalleeName()
call, msg, func, name