Skip to content

Commit

Permalink
Support of UTypeStreams and casts in JcInterpreter (#34)
Browse files Browse the repository at this point in the history
Co-authored-by: Yury Kamenev <[email protected]>
  • Loading branch information
sergeypospelov and Damtev authored Jul 12, 2023
1 parent cf9420c commit 34e6503
Show file tree
Hide file tree
Showing 158 changed files with 2,206 additions and 857 deletions.
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ object Versions {
const val ksmt = "0.5.3"
const val collections = "0.3.5"
const val coroutines = "1.6.4"
const val jcdb = "1.0.0"
const val jcdb = "1.1.3"
const val mockk = "1.13.4"
const val junitParams = "5.9.3"

Expand Down
5 changes: 3 additions & 2 deletions usvm-core/src/main/kotlin/org/usvm/Context.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import org.usvm.memory.UInputArrayRegion
import org.usvm.memory.UInputFieldRegion
import org.usvm.memory.splitUHeapRef
import org.usvm.solver.USolverBase
import org.usvm.types.UTypeSystem

@Suppress("LeakingThis")
open class UContext(
Expand Down Expand Up @@ -91,8 +92,8 @@ open class UContext(
lhs is UConcreteHeapRef && rhs is UConcreteHeapRef -> mkBool(lhs == rhs)
// unfolding
else -> {
val (concreteRefsLhs, symbolicRefLhs) = splitUHeapRef(lhs)
val (concreteRefsRhs, symbolicRefRhs) = splitUHeapRef(rhs)
val (concreteRefsLhs, symbolicRefLhs) = splitUHeapRef(lhs, ignoreNullRefs = false)
val (concreteRefsRhs, symbolicRefRhs) = splitUHeapRef(rhs, ignoreNullRefs = false)

val concreteRefLhsToGuard = concreteRefsLhs.associate { it.expr.address to it.guard }

Expand Down
30 changes: 28 additions & 2 deletions usvm-core/src/main/kotlin/org/usvm/Expressions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,28 @@ class UNullRef internal constructor(
}
}

// We split all addresses into three parts:
// * input values: [Int.MIN_VALUE..0),
// * null value: [0]
// * allocated values: (0..[Int.MAX_VALUE]

/**
* A constant corresponding to `null`.
*/
const val NULL_ADDRESS = 0

/**
* A constant corresponding to the first input address in any decoded model.
* Input addresses takes this semi-interval: [[Int.MIN_VALUE]..0)
*/
const val INITIAL_INPUT_ADDRESS = NULL_ADDRESS - 1
/**
* A constant corresponding to the first allocated address in any symbolic memory.
* Input addresses takes this semi-interval: (0..[Int.MAX_VALUE])
*/
const val INITIAL_CONCRETE_ADDRESS = NULL_ADDRESS + 1


//endregion

//region LValues
Expand Down Expand Up @@ -342,6 +364,10 @@ class UIndexedMethodReturnValue<Method, Sort : USort> internal constructor(

//region Subtyping Expressions

/**
* Means **either** [ref] is [UNullRef] **or** [ref] !is [UNullRef] and [ref] <: [type]. Thus, the actual type
* inheritance is checked only on non-null refs.
*/
class UIsExpr<Type> internal constructor(
ctx: UContext,
val ref: UHeapRef,
Expand All @@ -358,7 +384,7 @@ class UIsExpr<Type> internal constructor(


override fun print(printer: ExpressionPrinter) {
TODO("Not yet implemented")
printer.append("($ref instance of $type)")
}

override fun internEquals(other: Any): Boolean = structurallyEqual(other, { ref }, { type })
Expand All @@ -372,4 +398,4 @@ class UIsExpr<Type> internal constructor(
val UBoolExpr.isFalse get() = this == ctx.falseExpr
val UBoolExpr.isTrue get() = this == ctx.trueExpr

//endregion
//endregion
6 changes: 2 additions & 4 deletions usvm-core/src/main/kotlin/org/usvm/State.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ abstract class UState<Type, Field, Method, Statement>(
open var models: List<UModelBase<Field, Type>>,
open var path: PersistentList<Statement>
) {
open val ctx: UContext = ctx

/**
* Deterministic state id.
* TODO: Can be replaced with overridden hashCode
Expand Down Expand Up @@ -74,7 +72,7 @@ private fun <T : UState<Type, Field, *, *>, Type, Field> forkIfSat(
return null
}

val solver = state.ctx.solver<Field, Type, Any?>()
val solver = satisfiedCondition.uctx.solver<Field, Type, Any?>()
val satResult = solver.check(pathConstraints, useSoftConstraints = true)

return when (satResult) {
Expand Down Expand Up @@ -133,7 +131,7 @@ fun <T : UState<Type, Field, *, *>, Type, Field> fork(
holdsInModel.isTrue
}

val notCondition = state.ctx.mkNot(condition)
val notCondition = condition.uctx.mkNot(condition)
val (posState, negState) = when {

trueModels.isNotEmpty() && falseModels.isNotEmpty() -> {
Expand Down
2 changes: 1 addition & 1 deletion usvm-core/src/main/kotlin/org/usvm/StepScope.kt
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class StepScope<T : UState<Type, Field, *, *>, Type, Field>(
* Forks on a [condition], performing [blockOnTrueState] on a state satisfying [condition] and
* [blockOnFalseState] on a state satisfying [condition].not().
*
* If the [condition], sets underlying state to `null`.
* If the [condition] is unsatisfiable, sets underlying state to `null`.
*
* @return `null` if the [condition] is unsatisfiable.
*/
Expand Down
19 changes: 0 additions & 19 deletions usvm-core/src/main/kotlin/org/usvm/TypeSystem.kt

This file was deleted.

1 change: 1 addition & 0 deletions usvm-core/src/main/kotlin/org/usvm/UComponents.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.usvm

import org.usvm.solver.USolverBase
import org.usvm.types.UTypeSystem

/**
* Provides core USVM components tuned for specific language.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ class UEqualityConstraints private constructor(
equalReferences.subscribe(::rename)
}

var isContradiction = false
var isContradicting = false
private set

private fun contradiction() {
isContradiction = true
isContradicting = true
equalReferences.clear()
mutableDistinctReferences.clear()
mutableReferenceDisequalities.clear()
Expand Down Expand Up @@ -89,8 +89,8 @@ class UEqualityConstraints private constructor(
/**
* Adds an assertion that [ref1] is always equal to [ref2].
*/
fun addReferenceEquality(ref1: UHeapRef, ref2: UHeapRef) {
if (isContradiction) {
fun makeEqual(ref1: UHeapRef, ref2: UHeapRef) {
if (isContradicting) {
return
}

Expand Down Expand Up @@ -125,7 +125,7 @@ class UEqualityConstraints private constructor(
mutableReferenceDisequalities.remove(from)
fromDiseqs.forEach {
mutableReferenceDisequalities[it]?.remove(from)
addReferenceDisequality(to, it)
makeNonEqual(to, it)
}
}

Expand All @@ -142,7 +142,7 @@ class UEqualityConstraints private constructor(
}
} else if (containsNullableDisequality(from, to)) {
// If x === y, nullable disequality can hold only if both references are null
addReferenceEquality(to, nullRepr)
makeEqual(to, nullRepr)
} else {
val removedFrom = mutableNullableDisequalities.remove(from)
removedFrom?.forEach {
Expand Down Expand Up @@ -210,8 +210,8 @@ class UEqualityConstraints private constructor(
/**
* Adds an assertion that [ref1] is never equal to [ref2].
*/
fun addReferenceDisequality(ref1: UHeapRef, ref2: UHeapRef) {
if (isContradiction) {
fun makeNonEqual(ref1: UHeapRef, ref2: UHeapRef) {
if (isContradicting) {
return
}

Expand All @@ -233,7 +233,7 @@ class UEqualityConstraints private constructor(
* Adds an assertion that [ref1] is never equal to [ref2] or both are null.
*/
fun makeNonEqualOrBothNull(ref1: UHeapRef, ref2: UHeapRef) {
if (isContradiction) {
if (isContradicting) {
return
}

Expand All @@ -242,7 +242,7 @@ class UEqualityConstraints private constructor(

if (repr1 == repr2) {
// In this case, (repr1 != repr2) || (repr1 == null && repr2 == null) is equivalent to (repr1 == null).
addReferenceEquality(repr1, ctx.nullRef)
makeEqual(repr1, ctx.nullRef)
return
}

Expand Down Expand Up @@ -284,9 +284,9 @@ class UEqualityConstraints private constructor(
* Note that current subscribers get unsubscribed!
*/
fun clone(): UEqualityConstraints {
if (isContradiction) {
if (isContradicting) {
val result = UEqualityConstraints(ctx, DisjointSets(), mutableSetOf(), mutableMapOf(), mutableMapOf())
result.isContradiction = true
result.isContradicting = true
return result
}

Expand Down
15 changes: 10 additions & 5 deletions usvm-core/src/main/kotlin/org/usvm/constraints/PathConstraints.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ open class UPathConstraints<Type> private constructor(
constructor(ctx: UContext) : this(ctx, persistentSetOf())

open val isFalse: Boolean
get() = equalityConstraints.isContradiction ||
typeConstraints.isContradiction ||
get() = equalityConstraints.isContradicting ||
typeConstraints.isContradicting ||
logicalConstraints.singleOrNull() is UFalse

@Suppress("UNCHECKED_CAST")
Expand All @@ -54,9 +54,9 @@ open class UPathConstraints<Type> private constructor(
constraint == trueExpr || constraint in logicalConstraints -> {}

constraint is UEqExpr<*> && isSymbolicHeapRef(constraint.lhs) && isSymbolicHeapRef(constraint.rhs) ->
equalityConstraints.addReferenceEquality(constraint.lhs as UHeapRef, constraint.rhs as UHeapRef)
equalityConstraints.makeEqual(constraint.lhs as UHeapRef, constraint.rhs as UHeapRef)

constraint is UIsExpr<*> -> typeConstraints.cast(constraint.ref, constraint.type as Type)
constraint is UIsExpr<*> -> typeConstraints.addSupertype(constraint.ref, constraint.type as Type)

constraint is UAndExpr -> constraint.args.forEach(::plusAssign)

Expand All @@ -67,12 +67,17 @@ open class UPathConstraints<Type> private constructor(
isSymbolicHeapRef(notConstraint.lhs) &&
isSymbolicHeapRef(notConstraint.rhs) -> {
require(notConstraint.rhs.sort == addressSort)
equalityConstraints.addReferenceDisequality(
equalityConstraints.makeNonEqual(
notConstraint.lhs as UHeapRef,
notConstraint.rhs as UHeapRef
)
}

notConstraint is UIsExpr<*> -> typeConstraints.excludeSupertype(
notConstraint.ref,
notConstraint.type as Type
)

notConstraint in logicalConstraints -> contradiction(ctx)

notConstraint is UOrExpr -> notConstraint.args.forEach { plusAssign(ctx.mkNot(it)) }
Expand Down
Loading

0 comments on commit 34e6503

Please sign in to comment.