Skip to content

Commit 5e28e5a

Browse files
authored
Merge pull request #10909 from smowton/smowton/fix/kotlin-varargs-dataflow
Kotlin: Fix varargs dataflow, and varargs default handling
2 parents 7a8c9e7 + 1fe9e84 commit 5e28e5a

File tree

9 files changed

+473
-16
lines changed

9 files changed

+473
-16
lines changed

java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt

+8-4
Original file line numberDiff line numberDiff line change
@@ -1884,7 +1884,7 @@ open class KotlinFileExtractor(
18841884
IrConstImpl.defaultValueForType(0, 0, getDefaultsMethodLastArgType(callTarget))
18851885
)
18861886

1887-
extractCallValueArguments(id, valueArgsWithDummies + extraArgs, enclosingStmt, enclosingCallable, nextIdx)
1887+
extractCallValueArguments(id, valueArgsWithDummies + extraArgs, enclosingStmt, enclosingCallable, nextIdx, extractVarargAsArray = true)
18881888
}
18891889

18901890
private fun getFunctionInvokeMethod(typeArgs: List<IrTypeArgument>): IrFunction? {
@@ -1961,8 +1961,12 @@ open class KotlinFileExtractor(
19611961
superQualifierSymbol: IrClassSymbol? = null) {
19621962

19631963
val locId = tw.getLocation(locElement)
1964+
val varargParam = syntacticCallTarget.valueParameters.withIndex().find { it.value.isVararg }
1965+
// If the vararg param is the only one not specified, and it has no default value, then we don't need to call a $default method,
1966+
// as omitting it already implies passing an empty vararg array.
1967+
val nullAllowedIdx = if (varargParam != null && varargParam.value.defaultValue == null) varargParam.index else -1
19641968

1965-
if (valueArguments.any { it == null }) {
1969+
if (valueArguments.withIndex().any { (index, it) -> it == null && index != nullAllowedIdx }) {
19661970
extractsDefaultsCall(
19671971
syntacticCallTarget,
19681972
locId,
@@ -2082,11 +2086,11 @@ open class KotlinFileExtractor(
20822086
private fun extractCallValueArguments(callId: Label<out DbExprparent>, call: IrFunctionAccessExpression, enclosingStmt: Label<out DbStmt>, enclosingCallable: Label<out DbCallable>, idxOffset: Int) =
20832087
extractCallValueArguments(callId, (0 until call.valueArgumentsCount).map { call.getValueArgument(it) }, enclosingStmt, enclosingCallable, idxOffset)
20842088

2085-
private fun extractCallValueArguments(callId: Label<out DbExprparent>, valueArguments: List<IrExpression?>, enclosingStmt: Label<out DbStmt>, enclosingCallable: Label<out DbCallable>, idxOffset: Int) {
2089+
private fun extractCallValueArguments(callId: Label<out DbExprparent>, valueArguments: List<IrExpression?>, enclosingStmt: Label<out DbStmt>, enclosingCallable: Label<out DbCallable>, idxOffset: Int, extractVarargAsArray: Boolean = false) {
20862090
var i = 0
20872091
valueArguments.forEach { arg ->
20882092
if(arg != null) {
2089-
if (arg is IrVararg) {
2093+
if (arg is IrVararg && !extractVarargAsArray) {
20902094
arg.elements.forEachIndexed { varargNo, vararg -> extractVarargElement(vararg, enclosingCallable, callId, i + idxOffset + varargNo, enclosingStmt) }
20912095
i += arg.elements.size
20922096
} else {

java/ql/lib/semmle/code/java/Expr.qll

+22-3
Original file line numberDiff line numberDiff line change
@@ -2367,14 +2367,33 @@ class Argument extends Expr {
23672367
*/
23682368
predicate isNthVararg(int arrayindex) {
23692369
not this.isExplicitVarargsArray() and
2370-
exists(Callable tgt, int varargsParamPos |
2370+
exists(Callable tgt |
23712371
call.getCallee() = tgt and
2372-
tgt.getParameter(varargsParamPos).isVarargs() and
2373-
arrayindex = pos - varargsParamPos and
2372+
arrayindex = pos - tgt.getVaragsParameterIndex() and
23742373
arrayindex >= 0 and
23752374
arrayindex <= call.getNumArgument() - tgt.getNumberOfParameters()
23762375
)
23772376
}
2377+
2378+
/**
2379+
* Gets the parameter position that will receive this argument.
2380+
*
2381+
* For all vararg arguments, this is the position of the vararg array parameter.
2382+
*/
2383+
int getParameterPos() {
2384+
exists(Callable c | c = call.getCallee() |
2385+
if c.isVarargs()
2386+
then
2387+
if pos < c.getVaragsParameterIndex()
2388+
then result = pos // Vararg method argument, before the vararg parameter
2389+
else (
2390+
if this.isVararg()
2391+
then result = c.getVaragsParameterIndex() // Part of the implicit vararg array
2392+
else result = pos - (call.getNumArgument() - c.getNumberOfParameters()) // Vararg method argument, after the vararg parameter (offset could be -1 in the zero-vararg case)
2393+
)
2394+
else result = pos // Not a vararg method
2395+
)
2396+
}
23782397
}
23792398

23802399
/**

java/ql/lib/semmle/code/java/Member.qll

+3
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,9 @@ class Callable extends StmtParent, Member, @callable {
283283
/** Holds if the last parameter of this callable is a varargs (variable arity) parameter. */
284284
predicate isVarargs() { this.getAParameter().isVarargs() }
285285

286+
/** Gets the index of this callable's varargs parameter, if any exists. */
287+
int getVaragsParameterIndex() { this.getParameter(result).isVarargs() }
288+
286289
/**
287290
* Gets the signature of this callable, where all types in the signature have a fully-qualified name.
288291
* The parameter types are only separated by a comma (without space). If this callable has

java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll

+2-2
Original file line numberDiff line numberDiff line change
@@ -378,11 +378,11 @@ module Private {
378378
*/
379379
predicate argumentOf(DataFlowCall call, int pos) {
380380
exists(Argument arg | this.asExpr() = arg |
381-
call.asCall() = arg.getCall() and pos = arg.getPosition()
381+
call.asCall() = arg.getCall() and pos = arg.getParameterPos()
382382
)
383383
or
384384
call.asCall() = this.(ImplicitVarargsArray).getCall() and
385-
pos = call.asCall().getCallee().getNumberOfParameters() - 1
385+
pos = call.asCall().getCallee().getVaragsParameterIndex()
386386
or
387387
pos = -1 and this = getInstanceArgument(call.asCall())
388388
or

java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll

+2-7
Original file line numberDiff line numberDiff line change
@@ -282,13 +282,8 @@ private predicate constructorStep(Expr tracked, ConstructorCall sink) {
282282
* Converts an argument index to a formal parameter index.
283283
* This is relevant for varadic methods.
284284
*/
285-
private int argToParam(Call call, int arg) {
286-
exists(call.getArgument(arg)) and
287-
exists(Callable c | c = call.getCallee() |
288-
if c.isVarargs() and arg >= c.getNumberOfParameters()
289-
then result = c.getNumberOfParameters() - 1
290-
else result = arg
291-
)
285+
private int argToParam(Call call, int argIdx) {
286+
result = call.getArgument(argIdx).(Argument).getParameterPos()
292287
}
293288

294289
/** Access to a method that passes taint from qualifier to argument. */

0 commit comments

Comments
 (0)