Skip to content

Commit

Permalink
Eliminate ite expressions from type constraints (#40)
Browse files Browse the repository at this point in the history
  • Loading branch information
CaelmBleidd authored Jul 14, 2023
1 parent c09f086 commit 3b86203
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 26 deletions.
2 changes: 1 addition & 1 deletion usvm-core/src/main/kotlin/org/usvm/Expressions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ typealias USymbolicHeapRef = USymbol<UAddressSort>
typealias UConcreteHeapAddress = Int

fun isSymbolicHeapRef(expr: UExpr<*>) =
expr.sort == expr.uctx.addressSort && expr !is UConcreteHeapRef
expr.sort == expr.uctx.addressSort && expr is USymbol<*>

class UConcreteHeapRefDecl internal constructor(
ctx: UContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,13 @@ class UEqualityConstraints private constructor(
/**
* Returns if [ref1] is identical to [ref2] in *all* models.
*/
fun areEqual(ref1: UHeapRef, ref2: UHeapRef) =
internal fun areEqual(ref1: UHeapRef, ref2: UHeapRef) =
equalReferences.connected(ref1, ref2)

/**
* Returns if [ref] is null in all models.
*/
fun isNull(ref: UHeapRef) = areEqual(ctx.nullRef, ref)
internal fun isNull(ref: UHeapRef) = areEqual(ctx.nullRef, ref)

private fun areDistinctRepresentatives(repr1: UHeapRef, repr2: UHeapRef): Boolean {
if (repr1 == repr2) {
Expand All @@ -75,7 +75,7 @@ class UEqualityConstraints private constructor(
/**
* Returns if [ref1] is distinct from [ref2] in *all* models.
*/
fun areDistinct(ref1: UHeapRef, ref2: UHeapRef): Boolean {
internal fun areDistinct(ref1: UHeapRef, ref2: UHeapRef): Boolean {
val repr1 = equalReferences.find(ref1)
val repr2 = equalReferences.find(ref2)
return areDistinctRepresentatives(repr1, repr2)
Expand All @@ -84,12 +84,12 @@ class UEqualityConstraints private constructor(
/**
* Returns if [ref] is not null in all models.
*/
fun isNotNull(ref: UHeapRef) = areDistinct(ctx.nullRef, ref)
internal fun isNotNull(ref: UHeapRef) = areDistinct(ctx.nullRef, ref)

/**
* Adds an assertion that [ref1] is always equal to [ref2].
*/
fun makeEqual(ref1: UHeapRef, ref2: UHeapRef) {
internal fun makeEqual(ref1: UHeapRef, ref2: UHeapRef) {
if (isContradicting) {
return
}
Expand Down Expand Up @@ -210,7 +210,7 @@ class UEqualityConstraints private constructor(
/**
* Adds an assertion that [ref1] is never equal to [ref2].
*/
fun makeNonEqual(ref1: UHeapRef, ref2: UHeapRef) {
internal fun makeNonEqual(ref1: UHeapRef, ref2: UHeapRef) {
if (isContradicting) {
return
}
Expand All @@ -232,7 +232,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) {
internal fun makeNonEqualOrBothNull(ref1: UHeapRef, ref2: UHeapRef) {
if (isContradicting) {
return
}
Expand Down
25 changes: 19 additions & 6 deletions usvm-core/src/main/kotlin/org/usvm/constraints/PathConstraints.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import org.usvm.UHeapRef
import org.usvm.UIsExpr
import org.usvm.UNotExpr
import org.usvm.UOrExpr
import org.usvm.uctx
import org.usvm.isSymbolicHeapRef
import org.usvm.memory.map
import org.usvm.uctx

/**
* Mutable representation of path constraints.
Expand Down Expand Up @@ -42,8 +43,8 @@ open class UPathConstraints<Type> private constructor(

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

@Suppress("UNCHECKED_CAST")
open operator fun plusAssign(constraint: UBoolExpr): Unit =
Expand All @@ -56,16 +57,28 @@ open class UPathConstraints<Type> private constructor(
constraint is UEqExpr<*> && isSymbolicHeapRef(constraint.lhs) && isSymbolicHeapRef(constraint.rhs) ->
equalityConstraints.makeEqual(constraint.lhs as UHeapRef, constraint.rhs as UHeapRef)

constraint is UIsExpr<*> -> typeConstraints.addSupertype(constraint.ref, constraint.type as Type)
constraint is UIsExpr<*> -> {
val expr = constraint.ref.map(
concreteMapper = { typeConstraints.evalIs(it, constraint.type as Type) },
symbolicMapper = { if (it == nullRef) trueExpr else ctx.mkIsExpr(it, constraint.type) },
ignoreNullRefs = false
)

if (expr is UIsExpr<*>) {
typeConstraints.addSupertype(expr.ref, expr.type as Type)
} else {
logicalConstraints = logicalConstraints.add(expr)
}
}

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

constraint is UNotExpr -> {
val notConstraint = constraint.arg
when {
notConstraint is UEqExpr<*> &&
isSymbolicHeapRef(notConstraint.lhs) &&
isSymbolicHeapRef(notConstraint.rhs) -> {
isSymbolicHeapRef(notConstraint.lhs) &&
isSymbolicHeapRef(notConstraint.rhs) -> {
require(notConstraint.rhs.sort == addressSort)
equalityConstraints.makeNonEqual(
notConstraint.lhs as UHeapRef,
Expand Down
21 changes: 16 additions & 5 deletions usvm-core/src/main/kotlin/org/usvm/constraints/TypeConstraints.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import org.usvm.UConcreteHeapAddress
import org.usvm.UConcreteHeapRef
import org.usvm.UHeapRef
import org.usvm.UNullRef
import org.usvm.USymbolicHeapRef
import org.usvm.memory.map
import org.usvm.model.UModel
import org.usvm.model.UTypeModel
Expand Down Expand Up @@ -81,8 +82,11 @@ class UTypeConstraints<Type>(
* Constraints **either** the [ref] is null **or** the [ref] isn't null and the type of the [ref] to
* be a subtype of the [type]. If it is impossible within current type and equality constraints,
* then type constraints become contradicting (@see [isContradicting]).
*
* NB: this function **must not** be used to cast types in interpreters.
* To do so you should add corresponding constraint using [evalIs] function.
*/
fun addSupertype(ref: UHeapRef, type: Type) {
internal fun addSupertype(ref: UHeapRef, type: Type) {
when (ref) {
is UNullRef -> return

Expand All @@ -93,7 +97,7 @@ class UTypeConstraints<Type>(
}
}

else -> {
is USymbolicHeapRef -> {
val constraints = this[ref]
val newConstraints = constraints.addSupertype(type)
if (newConstraints.isContradicting) {
Expand All @@ -112,15 +116,20 @@ class UTypeConstraints<Type>(
this[ref] = newConstraints
}
}

else -> error("Provided heap ref must be either concrete or purely symbolic one, found $ref")
}
}

/**
* Constraints **both** the type of the [ref] to be a not subtype of the [type] and the [ref] not equals null.
* If it is impossible within current type and equality constraints,
* then type constraints become contradicting (@see [isContradicting]).
*
* NB: this function **must not** be used to exclude types in interpreters.
* To do so you should add corresponding constraint using [evalIs] function.
*/
fun excludeSupertype(ref: UHeapRef, type: Type) {
internal fun excludeSupertype(ref: UHeapRef, type: Type) {
when (ref) {
is UNullRef -> contradiction() // the [ref] can't be equal to null

Expand All @@ -131,7 +140,7 @@ class UTypeConstraints<Type>(
}
}

else -> {
is USymbolicHeapRef -> {
val constraints = this[ref]
val newConstraints = constraints.excludeSupertype(type)
equalityConstraints.makeNonEqual(ref, ref.uctx.nullRef)
Expand All @@ -150,6 +159,8 @@ class UTypeConstraints<Type>(
this[ref] = newConstraints
}
}

else -> error("Provided heap ref must be either concrete or purely symbolic one, found $ref")
}
}

Expand Down Expand Up @@ -252,7 +263,7 @@ class UTypeConstraints<Type>(
* * [UTypeUnsatResult] with reference disequalities constraints if the [model]
* doesn't satisfy this [UTypeConstraints], but other model may satisfy
*/
fun verify(model: UModel): USolverResult<UTypeModel<Type>> {
internal fun verify(model: UModel): USolverResult<UTypeModel<Type>> {
// firstly, group symbolic references by their interpretations in the [model]
val concreteRefToTypeRegions = symbolicRefToTypeRegion
.entries
Expand Down
69 changes: 66 additions & 3 deletions usvm-core/src/test/kotlin/org/usvm/types/TypeSolverTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import org.usvm.UContext
import org.usvm.constraints.UPathConstraints
import org.usvm.constraints.UTypeUnsatResult
import org.usvm.memory.UMemoryBase
import org.usvm.memory.URegionHeap
import org.usvm.memory.emptyInputArrayRegion
import org.usvm.model.UModelBase
import org.usvm.model.buildTranslatorAndLazyDecoder
import org.usvm.solver.USatResult
Expand Down Expand Up @@ -183,9 +185,9 @@ class TypeSolverTest {
val c = ctx.mkRegisterReading(3, addressSort)

pc += mkHeapRefEq(a, nullRef).not() and
mkHeapRefEq(b1, nullRef).not() and
mkHeapRefEq(b2, nullRef).not() and
mkHeapRefEq(c, nullRef).not()
mkHeapRefEq(b1, nullRef).not() and
mkHeapRefEq(b2, nullRef).not() and
mkHeapRefEq(c, nullRef).not()

pc += mkIsExpr(a, interfaceAB)
pc += mkIsExpr(b1, interfaceBC1)
Expand Down Expand Up @@ -281,6 +283,67 @@ class TypeSolverTest {
assertTrue(concreteA != 0 && concreteA == concreteB && concreteC == 0)
}

@Test
fun `ite must not occur as refs in type constraints`() = with(ctx) {
val arr1 = mkRegisterReading(0, addressSort)
val arr2 = mkRegisterReading(1, addressSort)

val val1 = mkConcreteHeapRef(1)
val val2 = mkConcreteHeapRef(2)

val pc = UPathConstraints<TestType>(ctx)

pc += mkHeapRefEq(arr1, nullRef).not()
pc += mkHeapRefEq(arr2, nullRef).not()

val idx1 = 0.toBv()
val idx2 = 0.toBv()

val field = mockk<Field>()
val heap = URegionHeap<Field, TestType>(ctx)

heap.writeField(val1, field, bv32Sort, 1.toBv(), trueExpr)
heap.writeField(val2, field, bv32Sort, 2.toBv(), trueExpr)

val inputRegion = emptyInputArrayRegion(mockk<TestType>(), addressSort)
.write(arr1 to idx1, val1, trueExpr)
.write(arr2 to idx2, val2, trueExpr)

val firstReading = inputRegion.read(arr1 to idx1)
val secondReading = inputRegion.read(arr2 to idx2)

pc += mkIsExpr(arr1, base1)
pc += mkIsExpr(arr2, base1)

pc.typeConstraints.allocate(val1.address, base2)
pc.typeConstraints.allocate(val2.address, base2)

pc += mkHeapRefEq(firstReading, nullRef).not()
pc += mkHeapRefEq(secondReading, nullRef).not()

pc += mkIsExpr(firstReading, base2)
pc += mkIsExpr(secondReading, base2)

val fstFieldValue = heap.readField(firstReading, field, bv32Sort)
val sndFieldValue = heap.readField(secondReading, field, bv32Sort)

pc += fstFieldValue eq sndFieldValue

val (translator, decoder) = buildTranslatorAndLazyDecoder<Field, TestType, Method>(ctx)

val solver = USolverBase(
this,
KZ3Solver(this),
translator,
decoder,
softConstraintsProvider = USoftConstraintsProvider(this)
)

val status = solver.check(pc, useSoftConstraints = true)

assertTrue { status is USatResult<*> }
}

@Test
@Disabled("Support propositional type variables")
fun `Test symbolic ref -- not instance of constraint`(): Unit = with(ctx) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ internal class DoubleExamplesTest : JavaMethodTestRunner() {
)
}

@Test
@Disabled("Timeout Expected exactly 2 executions, but 0 found")
fun testCompareWithDiv() {
checkDiscoveredProperties(
DoubleExamples::compareWithDiv,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.usvm.samples.JavaMethodTestRunner
import org.usvm.test.util.checkers.eq
import org.usvm.test.util.checkers.ignoreNumberOfAnalysisResults


internal class IntegerWrapperTest : JavaMethodTestRunner() {
Expand Down Expand Up @@ -33,10 +34,9 @@ internal class IntegerWrapperTest : JavaMethodTestRunner() {
fun numberOfZerosTest() {
checkDiscoveredProperties(
IntegerWrapper::numberOfZeros,
eq(4),
ignoreNumberOfAnalysisResults,
{ _, x, _ -> x == null },
{ _, x, r -> Integer.numberOfLeadingZeros(x) >= 5 && r == 0 },
{ _, x, r -> Integer.numberOfLeadingZeros(x) < 5 && Integer.numberOfTrailingZeros(x) >= 5 && r == 0 },
{ _, x, r -> Integer.numberOfLeadingZeros(x) >= 5 || Integer.numberOfTrailingZeros(x) >= 5 && r == 0 },
{ _, x, r -> Integer.numberOfLeadingZeros(x) < 5 && Integer.numberOfTrailingZeros(x) < 5 && r == 1 },
)
}
Expand Down

0 comments on commit 3b86203

Please sign in to comment.