Skip to content

Commit b2f4233

Browse files
author
Dominik Helm
committed
Invalidate StringBuilder/Buffer arguments of unknown calls that might modify them
1 parent 71959bf commit b2f4233

13 files changed

+98
-31
lines changed

OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/string/StringInterpreter.scala

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,6 @@ trait StringInterpreter {
5050

5151
protected[this] def computeFinalResult(p: StringFlowFunctionProperty)(implicit state: InterpretationState): Result =
5252
StringInterpreter.computeFinalResult(p)
53-
54-
protected[this] def isStringBuilderBufferCall(call: Call[V]): Boolean =
55-
(call.declaringClass eq ClassType.StringBuilder) || (call.declaringClass eq ClassType.StringBuffer)
5653
}
5754

5855
object StringInterpreter {
@@ -76,6 +73,32 @@ object StringInterpreter {
7673

7774
def computeFinalResult(p: StringFlowFunctionProperty)(implicit state: InterpretationState): Result =
7875
Result(FinalEP(InterpretationHandler.getEntity(state), p))
76+
77+
def invalidEntitiesForUnknownCall(call: Call[V], target: Option[PV] = None)(implicit
78+
state: InterpretationState
79+
): Set[PV] = {
80+
val relevantParameters = call.descriptor.parameterTypes.iterator.zipWithIndex.collect {
81+
case (p, index)
82+
if p.isClassType && ((p.asClassType eq ClassType.StringBuilder) || (p.asClassType eq ClassType.StringBuffer)) =>
83+
call.params(index)
84+
}
85+
val relevantReceiver = call.receiverOption.filter { _ => isStringBuilderBufferCall(call) }
86+
(relevantParameters ++ relevantReceiver).map(_.asVar.toPersistentForm(state.tac.stmts)).toSet ++ target
87+
}
88+
89+
def uninterpretedCall(call: Call[V], target: Option[PV] = None)(implicit
90+
state: InterpretationState,
91+
highSoundness: Boolean
92+
): Result = {
93+
val relevantEntities = invalidEntitiesForUnknownCall(call, target)
94+
val webs = relevantEntities.map(PDUWeb(state.pc, _))
95+
val flow = StringFlowFunctionProperty.constForEntities(state.pc, relevantEntities, failureTree)
96+
val p = StringFlowFunctionProperty(webs, flow)
97+
Result(FinalEP(InterpretationHandler.getEntity(state), p))
98+
}
99+
100+
def isStringBuilderBufferCall(call: Call[V]): Boolean =
101+
(call.declaringClass eq ClassType.StringBuilder) || (call.declaringClass eq ClassType.StringBuffer)
79102
}
80103

81104
/**

OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/string/l0/interpretation/L0InterpretationHandler.scala

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,10 @@ class L0InterpretationHandler(implicit override val project: SomeProject) extend
3030
SimpleValueConstExprInterpreter.interpretExpr(stmt, expr)
3131
case stmt @ Assignment(_, _, expr: BinaryExpr[V]) => BinaryExprInterpreter().interpretExpr(stmt, expr)
3232

33-
case ExprStmt(_, expr: InstanceFunctionCall[V]) => StringInterpreter.failure(expr.receiver.asVar)
34-
35-
case vmc: VirtualMethodCall[V] => StringInterpreter.failure(vmc.receiver.asVar)
36-
case nvmc: NonVirtualMethodCall[V] => StringInterpreter.failure(nvmc.receiver.asVar)
37-
38-
// Static calls without return value usage are irrelevant
39-
case ExprStmt(_, _: StaticFunctionCall[V]) | _: StaticMethodCall[V] =>
40-
StringInterpreter.computeFinalResult(StringFlowFunctionProperty.identity)
33+
case ExprStmt(_, call: InstanceFunctionCall[V]) => StringInterpreter.uninterpretedCall(call)
34+
case Assignment(_, target, call: InstanceFunctionCall[V]) =>
35+
StringInterpreter.uninterpretedCall(call, Some(target.asVar.toPersistentForm(state.tac.stmts)))
36+
case call: MethodCall[V] => StringInterpreter.uninterpretedCall(call)
4137

4238
case Assignment(_, target, _) => StringInterpreter.failure(target)
4339

OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/string/l1/interpretation/L1FunctionCallInterpreter.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,10 @@ trait L1FunctionCallInterpreter
105105
}
106106
}
107107

108-
tryComputeFinalResult
108+
computeResult
109109
}
110110

111-
private def tryComputeFinalResult(
111+
private def computeResult(
112112
implicit
113113
state: InterpretationState,
114114
callState: CallState
@@ -172,7 +172,7 @@ trait L1FunctionCallInterpreter
172172
case EUBP(_, _: StringConstancyProperty) =>
173173
val contextEPS = eps.asInstanceOf[EOptionP[VariableDefinition, StringConstancyProperty]]
174174
callState.updateReturnDependee(contextEPS.e.m, contextEPS)
175-
tryComputeFinalResult(state, callState)
175+
computeResult(state, callState)
176176

177177
case _ => throw new IllegalArgumentException(s"Encountered unknown eps: $eps")
178178
}

OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/string/l1/interpretation/L1InterpretationHandler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class L1InterpretationHandler(implicit override val project: SomeProject) extend
4444
case stmt @ Assignment(_, _, expr: StaticFunctionCall[V]) =>
4545
L1StaticFunctionCallInterpreter().interpretExpr(stmt, expr)
4646

47-
case vmc: VirtualMethodCall[V] => L1VirtualMethodCallInterpreter().interpret(vmc)
47+
case vmc: VirtualMethodCall[V] => new L1VirtualMethodCallInterpreter().interpret(vmc)
4848
case nvmc: NonVirtualMethodCall[V] => L1NonVirtualMethodCallInterpreter().interpret(nvmc)
4949

5050
case stmt => super.processStatement(state)(stmt)

OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/string/l1/interpretation/L1NonVirtualFunctionCallInterpreter.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@ case class L1NonVirtualFunctionCallInterpreter()(
3535
val target = expr.receiver.asVar.toPersistentForm(state.tac.stmts)
3636
val calleeMethod = expr.resolveCallTarget(state.dm.definedMethod.classFile.thisType)
3737
if (calleeMethod.isEmpty) {
38-
return failure(target)
38+
return StringInterpreter.uninterpretedCall(
39+
expr,
40+
expr.receiverOption.map { _.asVar.toPersistentForm(state.tac.stmts) }
41+
)
3942
}
4043

4144
val m = calleeMethod.value

OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/string/l1/interpretation/L1NonVirtualMethodCallInterpreter.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ case class L1NonVirtualMethodCallInterpreter()(
2828

2929
override def interpret(call: T)(implicit state: InterpretationState): ProperPropertyComputationResult = {
3030
call.name match {
31-
case "<init>" if isStringBuilderBufferCall(call) || (call.declaringClass eq ClassType.String) =>
31+
case "<init>"
32+
if StringInterpreter.isStringBuilderBufferCall(call) || (call.declaringClass eq ClassType.String) =>
3233
interpretInit(call)
3334
case _ =>
34-
computeFinalResult(StringFlowFunctionProperty.identity)
35+
StringInterpreter.uninterpretedCall(call)
3536
}
3637
}
3738

@@ -49,7 +50,7 @@ case class L1NonVirtualMethodCallInterpreter()(
4950
(env: StringTreeEnvironment) => env.update(pc, targetVar, env(pc, paramVar))
5051
)
5152
case _ =>
52-
failure(targetVar)
53+
StringInterpreter.uninterpretedCall(init)
5354
}
5455
}
5556
}

OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/string/l1/interpretation/L1StaticFunctionCallInterpreter.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ case class L1StaticFunctionCallInterpreter()(
4848
case _
4949
if (call.descriptor.returnType eq ClassType.String) || (call.descriptor.returnType eq ClassType.Object) =>
5050
interpretArbitraryCall(target, call)
51-
case _ => failure(target)
51+
case _ => StringInterpreter.uninterpretedCall(call, Some(target))
5252
}
5353
}
5454
}
@@ -67,7 +67,7 @@ private[string] trait L1ArbitraryStaticFunctionCallInterpreter
6767
): ProperPropertyComputationResult = {
6868
val calleeMethod = call.resolveCallTarget(state.dm.definedMethod.classFile.thisType)
6969
if (calleeMethod.isEmpty) {
70-
return failure(target)
70+
return StringInterpreter.uninterpretedCall(call, Some(target))
7171
}
7272

7373
val m = calleeMethod.value

OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/string/l1/interpretation/L1VirtualFunctionCallInterpreter.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ class L1VirtualFunctionCallInterpreter(
5252
val pt = call.receiver.asVar.toPersistentForm(state.tac.stmts)
5353

5454
call.name match {
55-
case "append" if isStringBuilderBufferCall(call) => interpretAppendCall(at, pt, call)
56-
case "toString" => interpretToStringCall(at, pt)
57-
case "replace" if isStringBuilderBufferCall(call) => interpretReplaceCall(pt)
55+
case "append" if StringInterpreter.isStringBuilderBufferCall(call) => interpretAppendCall(at, pt, call)
56+
case "toString" => interpretToStringCall(at, pt)
57+
case "replace" if StringInterpreter.isStringBuilderBufferCall(call) => interpretReplaceCall(pt)
5858
case "substring" if call.descriptor.returnType eq ClassType.String =>
5959
interpretSubstringCall(at, pt, call)
6060
case _ =>
@@ -76,7 +76,7 @@ class L1VirtualFunctionCallInterpreter(
7676
case _ if at.isDefined =>
7777
interpretArbitraryCall(at.get, call)
7878
case _ =>
79-
computeFinalResult(StringFlowFunctionProperty.identity)
79+
StringInterpreter.uninterpretedCall(call)
8080
}
8181
}
8282
}

OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/string/l1/interpretation/L1VirtualMethodCallInterpreter.scala

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import org.opalj.value.TheIntegerValue
2121
*
2222
* @author Maximilian Rüsch
2323
*/
24-
case class L1VirtualMethodCallInterpreter()(
24+
class L1VirtualMethodCallInterpreter()(
2525
implicit val highSoundness: Boolean
2626
) extends StringInterpreter {
2727

@@ -31,7 +31,7 @@ case class L1VirtualMethodCallInterpreter()(
3131
val pReceiver = call.receiver.asVar.toPersistentForm(state.tac.stmts)
3232

3333
call.name match {
34-
case "setLength" if isStringBuilderBufferCall(call) =>
34+
case "setLength" if StringInterpreter.isStringBuilderBufferCall(call) =>
3535
call.params.head.asVar.value match {
3636
case TheIntegerValue(intVal) if intVal == 0 =>
3737
computeFinalResult(StringFlowFunctionProperty.constForVariableAt(
@@ -63,7 +63,12 @@ case class L1VirtualMethodCallInterpreter()(
6363
}
6464

6565
case _ =>
66-
computeFinalResult(StringFlowFunctionProperty.identity)
66+
interpretArbitraryCall(call)
6767
}
6868
}
69+
70+
protected def interpretArbitraryCall(call: T)(implicit
71+
state: InterpretationState
72+
): ProperPropertyComputationResult =
73+
StringInterpreter.uninterpretedCall(call)
6974
}

OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/string/l2/interpretation/L2InterpretationHandler.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ class L2InterpretationHandler(implicit override val project: SomeProject) extend
4343
case stmt @ AssignmentLikeStmt(_, expr: VirtualFunctionCall[V]) =>
4444
new L2VirtualFunctionCallInterpreter().interpretExpr(stmt.asAssignmentLike, expr)
4545

46+
case vmc: VirtualMethodCall[V] => L2VirtualMethodCallInterpreter().interpret(vmc)
47+
4648
// IMPROVE add call-graph based interpreters for other call types than virtual function calls to L2
4749

4850
case stmt => super.processStatement(state)(stmt)

0 commit comments

Comments
 (0)