Skip to content

Commit

Permalink
Fix passing catgeory 2 values as parameters
Browse files Browse the repository at this point in the history
This bug was revealed by checking that length of the parameter list matches the method descriptor in ReflectionExecutor
  • Loading branch information
kilzm committed Feb 15, 2024
1 parent 1baf919 commit d7f5d70
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

import java.util.Arrays;
import java.util.Collection;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import proguard.analysis.cpa.defaults.StackAbstractState;
import proguard.analysis.cpa.interfaces.AbstractState;
Expand Down Expand Up @@ -120,6 +122,26 @@ public ValueAbstractState getAbstractReferenceValue(
value));
}

@Override
protected void processCall(JvmAbstractState<ValueAbstractState> state, Call call) {
Deque<ValueAbstractState> operands = new LinkedList<>();
if (call.getTarget().descriptor.argumentTypes != null) {
List<String> argumentTypes = call.getTarget().descriptor.argumentTypes;
for (int i = argumentTypes.size() - 1; i >= 0; i--) {
boolean isCategory2 = ClassUtil.isInternalCategory2Type(argumentTypes.get(i));
operands.offerFirst(state.pop());
if (isCategory2) {
// ExecutingInvocationUnit expects a single parameter for category 2 values
state.pop();
}
}
}
if (!call.isStatic()) {
operands.offerFirst(state.pop());
}
invokeMethod(state, call, (List<ValueAbstractState>) operands);
}

@Override
public void invokeMethod(
JvmAbstractState<ValueAbstractState> state, Call call, List<ValueAbstractState> operands) {
Expand Down Expand Up @@ -151,9 +173,16 @@ private void executeMethod(
List<ValueAbstractState> operands) {
Clazz targetClass = call.getTargetClass();
Method targetMethod = call.getTargetMethod();

Value[] operandsArray =
operands.stream().map(ValueAbstractState::getValue).toArray(Value[]::new);

if (operandsArray.length
!= ClassUtil.internalMethodParameterCount(
call.getTarget().descriptor.toString(), call.isStatic())) {
throw new IllegalStateException("Unexpected number of parameters");
}

Value result = executingInvocationUnit.executeMethod(call, operandsArray);
String returnType = internalMethodReturnType(targetMethod.getDescriptor(targetClass));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ package proguard.analysis.cpa.jvm.domain.value

import io.kotest.core.spec.style.FreeSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.shouldBeInstanceOf
import proguard.analysis.cpa.jvm.cfa.JvmCfa
import proguard.analysis.cpa.jvm.util.CfaUtil
import proguard.classfile.MethodSignature
import proguard.classfile.ProgramClass
import proguard.classfile.Signature
import proguard.evaluation.value.ParticularReferenceValue
import proguard.testutils.AssemblerSource
import proguard.testutils.ClassPoolBuilder
import proguard.testutils.JavaSource

class CpaValueTest : FreeSpec({

Expand Down Expand Up @@ -57,4 +60,39 @@ class CpaValueTest : FreeSpec({
last.staticFields.size shouldBe 2
}
}

"Executed method taking category2 value" - {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
JavaSource(
"Test.java",
"""
import java.lang.StringBuilder;

public class Test {
public static void test() {
StringBuilder builder = new StringBuilder();
builder.append(1L);
String s = builder.toString();
}
}
""",
),
initialize = true,
)
val cfa: JvmCfa = CfaUtil.createInterproceduralCfa(programClassPool, libraryClassPool)
val clazz = programClassPool.getClass("Test") as ProgramClass
val mainSignature = Signature.of(clazz, clazz.findMethod("test", null)) as MethodSignature
val bamCpaRun = JvmValueBamCpaRun.Builder(cfa, mainSignature).setReduceHeap(true).build()
bamCpaRun.execute()
val cache = bamCpaRun.cpa.cache
val last = cache
.get(mainSignature)
.map { it.reachedSet.asCollection().maxBy { (it as JvmValueAbstractState).programLocation.offset } }
.single() as JvmValueAbstractState
"Correct string value" {
val value = last.frame.localVariables[1].value
value.shouldBeInstanceOf<ParticularReferenceValue>()
value.referenceValue().value() shouldBe "1"
}
}
})

0 comments on commit d7f5d70

Please sign in to comment.