Z3 嵌套存储操作的有效方法

Z3 嵌套存储操作的有效方法,z3,Z3,在关于如何使嵌套存储操作更高效的问题中,答案是可以使用selects替换嵌套存储操作,如:assert=select A i1 v1。但是,我需要存储操作,因为以前的约束必须替换为新的约束 例如:以下约束模拟以下装配程序: mov qword ptr [rax], rbx mov rcx, qword ptr [rax] 我想证明rbx和rcx是相等的,我断言=rbx!2个RCX!2并期望模型能够得到满足。这很好用。我断言不等于RBX!2个RCX!2并期望模型不能满足要求。当我将以下约束输入Z

在关于如何使嵌套存储操作更高效的问题中,答案是可以使用selects替换嵌套存储操作,如:assert=select A i1 v1。但是,我需要存储操作,因为以前的约束必须替换为新的约束

例如:以下约束模拟以下装配程序:

mov qword ptr [rax], rbx
mov rcx, qword ptr [rax]
我想证明rbx和rcx是相等的,我断言=rbx!2个RCX!2并期望模型能够得到满足。这很好用。我断言不等于RBX!2个RCX!2并期望模型不能满足要求。当我将以下约束输入Z3 eg时,它给出了一个几乎即时的答案:UNSAT。然而,如果我在C程序中证明相同的问题,请参见下文,它无法在合理的时间内推导出UNSAT

问:我怎样才能使C程序和SMT2.0程序一样快

(declare-fun RAX!0 () (_ BitVec 64))
(declare-fun RAX!1 () (_ BitVec 64))
(declare-fun RAX!2 () (_ BitVec 64))

(declare-fun RBX!0 () (_ BitVec 64))
(declare-fun RBX!1 () (_ BitVec 64))
(declare-fun RBX!2 () (_ BitVec 64))

(declare-fun RCX!0 () (_ BitVec 64))
(declare-fun RCX!1 () (_ BitVec 64))
(declare-fun RCX!2 () (_ BitVec 64))

(declare-fun MEM!0 () (Array (_ BitVec 64) (_ BitVec 8)))
(declare-fun MEM!1 () (Array (_ BitVec 64) (_ BitVec 8)))
(declare-fun MEM!2 () (Array (_ BitVec 64) (_ BitVec 8)))

(assert (= RAX!1 RAX!0))
(assert (= RBX!1 RBX!0))
(assert (= RCX!1 RCX!0))
(assert (let ((a!1 (store (store (store MEM!0 RAX!0 ((_ extract 7 0) RBX!0))
                         (bvadd #x0000000000000001 RAX!0)
                         ((_ extract 15 8) RBX!0))
                  (bvadd #x0000000000000002 RAX!0)
                  ((_ extract 23 16) RBX!0))))
(let ((a!2 (store (store (store a!1
                                (bvadd #x0000000000000003 RAX!0)
                                ((_ extract 31 24) RBX!0))
                         (bvadd #x0000000000000004 RAX!0)
                         ((_ extract 39 32) RBX!0))
                  (bvadd #x0000000000000005 RAX!0)
                  ((_ extract 47 40) RBX!0))))
  (= MEM!1
     (store (store a!2
                   (bvadd #x0000000000000006 RAX!0)
                   ((_ extract 55 48) RBX!0))
            (bvadd #x0000000000000007 RAX!0)
            ((_ extract 63 56) RBX!0))))))
(assert (= RAX!2 RAX!1))
(assert (= RBX!2 RBX!1))
(assert (= RCX!2
   (concat (select MEM!1 (bvadd #x0000000000000007 RAX!1))
           (select MEM!1 (bvadd #x0000000000000006 RAX!1))
           (select MEM!1 (bvadd #x0000000000000005 RAX!1))
           (select MEM!1 (bvadd #x0000000000000004 RAX!1))
           (select MEM!1 (bvadd #x0000000000000003 RAX!1))
           (select MEM!1 (bvadd #x0000000000000002 RAX!1))
           (select MEM!1 (bvadd #x0000000000000001 RAX!1))
           (select MEM!1 RAX!1))))
(assert (= MEM!2 MEM!1))
(assert (not (= RBX!2 RCX!2)))
C代码:

Dictionary<string, string> settings = new Dictionary<string, string>
{
    { "unsat-core", "false" },    // enable generation of unsat cores
    { "model", "false" },         // enable model generation
    { "proof", "false" },         // enable proof generation
    { "timeout", "60000" }        // 60000=1min
};
Context ctx = new Context(settings);

Solver solver = ctx.MkSolver(ctx.MkTactic("qfbv"));
BitVecExpr rax0 = ctx.MkBVConst("RAX!0", 64);
BitVecExpr rax1 = ctx.MkBVConst("RAX!1", 64);
BitVecExpr rax2 = ctx.MkBVConst("RAX!2", 64);

BitVecExpr rbx0 = ctx.MkBVConst("RBX!0", 64);
BitVecExpr rbx1 = ctx.MkBVConst("RBX!1", 64);
BitVecExpr rbx2 = ctx.MkBVConst("RBX!2", 64);

BitVecExpr rcx0 = ctx.MkBVConst("RCX!0", 64);
BitVecExpr rcx1 = ctx.MkBVConst("RCX!1", 64);
BitVecExpr rcx2 = ctx.MkBVConst("RCX!2", 64);

ArrayExpr mem0 = ctx.MkArrayConst("MEM!0", ctx.MkBitVecSort(64), ctx.MkBitVecSort(8));
ArrayExpr mem1 = ctx.MkArrayConst("MEM!1", ctx.MkBitVecSort(64), ctx.MkBitVecSort(8));
ArrayExpr mem2 = ctx.MkArrayConst("MEM!2", ctx.MkBitVecSort(64), ctx.MkBitVecSort(8));

solver.Assert(ctx.MkEq(rax1, rax0));
solver.Assert(ctx.MkEq(rbx1, rbx0));
solver.Assert(ctx.MkEq(rcx1, rcx0));
ArrayExpr memX0 = ctx.MkStore(mem0, ctx.MkBVAdd(ctx.MkBV(0, 64), rax0), ctx.MkExtract((1 * 8) - 1, 0 * 8, rbx0));
ArrayExpr memX1 = ctx.MkStore(memX0, ctx.MkBVAdd(ctx.MkBV(1, 64), rax0), ctx.MkExtract((2 * 8) - 1, 1 * 8, rbx0));
ArrayExpr memX2 = ctx.MkStore(memX1, ctx.MkBVAdd(ctx.MkBV(2, 64), rax0), ctx.MkExtract((3 * 8) - 1, 2 * 8, rbx0));
ArrayExpr memX3 = ctx.MkStore(memX2, ctx.MkBVAdd(ctx.MkBV(3, 64), rax0), ctx.MkExtract((4 * 8) - 1, 3 * 8, rbx0));
ArrayExpr memX4 = ctx.MkStore(memX3, ctx.MkBVAdd(ctx.MkBV(4, 64), rax0), ctx.MkExtract((5 * 8) - 1, 4 * 8, rbx0));
ArrayExpr memX5 = ctx.MkStore(memX4, ctx.MkBVAdd(ctx.MkBV(5, 64), rax0), ctx.MkExtract((6 * 8) - 1, 5 * 8, rbx0));
ArrayExpr memX6 = ctx.MkStore(memX5, ctx.MkBVAdd(ctx.MkBV(6, 64), rax0), ctx.MkExtract((7 * 8) - 1, 6 * 8, rbx0));
memX7 = ctx.MkStore(memX6, ctx.MkBVAdd(ctx.MkBV(7, 64), rax0), ctx.MkExtract((8 * 8) - 1, 7 * 8, rbx0));
solver.Assert(ctx.MkEq(mem1, memX7).Simplify() as BoolExpr);

solver.Assert(ctx.MkEq(rax2, rax1));
solver.Assert(ctx.MkEq(rbx2, rbx1));
BitVecExpr y0 = ctx.MkSelect(mem1, ctx.MkBVAdd(ctx.MkBV(0, 64), rax1)) as BitVecExpr;
BitVecExpr y1 = ctx.MkSelect(mem1, ctx.MkBVAdd(ctx.MkBV(1, 64), rax1)) as BitVecExpr;
BitVecExpr y2 = ctx.MkSelect(mem1, ctx.MkBVAdd(ctx.MkBV(2, 64), rax1)) as BitVecExpr;
BitVecExpr y3 = ctx.MkSelect(mem1, ctx.MkBVAdd(ctx.MkBV(3, 64), rax1)) as BitVecExpr;
BitVecExpr y4 = ctx.MkSelect(mem1, ctx.MkBVAdd(ctx.MkBV(4, 64), rax1)) as BitVecExpr;
BitVecExpr y5 = ctx.MkSelect(mem1, ctx.MkBVAdd(ctx.MkBV(5, 64), rax1)) as BitVecExpr;
BitVecExpr y6 = ctx.MkSelect(mem1, ctx.MkBVAdd(ctx.MkBV(6, 64), rax1)) as BitVecExpr;
BitVecExpr y7 = ctx.MkSelect(mem1, ctx.MkBVAdd(ctx.MkBV(7, 64), rax1)) as BitVecExpr;
BitVecExpr y = ctx.MkConcat(y7, ctx.MkConcat(y6, ctx.MkConcat(y5, ctx.MkConcat(y4, ctx.MkConcat(y3, ctx.MkConcat(y2, ctx.MkConcat(y1, y0)))))));
solver.Assert(ctx.MkEq(rcx2, y).Simplify() as BoolExpr);
solver.Assert(ctx.MkEq(mem2, mem1));

Status status_Neg = solver.Check(ctx.MkNot(ctx.MkEq(rbx2, rcx2)));
Console.WriteLine("Status Neg = "+status_Neg); // Go on holiday...

不幸的是,我没有办法运行C程序来玩它。但我注意到你有简化的要求:

我很好奇你为什么需要那个电话?也许这就是罪魁祸首

另一种尝试是使用未解释的函数来表示内存,而不是数组。UF通常更容易处理,它们在我的个人经验中提供了大致相同的抽象


看看C接口作为SMT库生成了什么,看看翻译是否与您认为的有很大不同,这可能是一个好主意。我想您可以使用以下函数来实现这一点:

Simplify是一个实验,它没有改变调用它的Expr,我错误地认为它不会有多大影响。这确实很重要,但它仍然没有解释为什么SMT2.0版本是0.05秒,而c版本是20秒。我的函数编程经验Lambda Calculation&Haskell有点生疏。请您给出一个提示,这样一个用于将值8位bitvec存储到地址64位bitvec的未经解释的函数看起来如何会覆盖以前的值。这种破坏性的文字让我很难理解。对不起,我的评论有误导性。我所说的意外函数并不是指SMTLib UFs。相反,我指的是自己跟踪记忆结构;仅生成可访问的值。从某种意义上说,您将在SMTLib之外进行编码,并将执行跟踪向下转换为SMTLib。我不熟悉C接口,但如果您愿意尝试Haskell,我可以帮助您使用SBV编写代码。特别是,这个例子似乎与此相关:
solver.Assert(ctx.MkEq(mem1, memX7).Simplify() as BoolExpr);