当我的输入正确时,为什么Z3会说这个等式不可满足?

时间:2014-08-27 16:36:03

标签: math z3 xor solver smt

给定一个简单的shift-and-XOR运算,其中'input'是符号:

input    = BitVec('input',32)
feedback = 0x8049d30
shiftreg = input ^ feedback
shiftreg = 0xffffffff & ((shiftreg << 8) | (shiftreg >> 24))
shiftreg = shiftreg ^ 0xcafebabe

s = Solver()
s.add(shiftreg == 0x804a008)
s.check()
# unsat

我们被告知这个等式是不可解的。如果打印,s包含:

[4294967295 &
 ((input ^ 134520112) << 8 | (input ^ 134520112) >> 24) ^
 3405691582 ==
 134520840]

但是,我可以轻而易举地创建一个解释“输入”这个等式的例子。

want = 0x804a008
want ^= 0xcafebabe
want = 0xffffffff & ((want >> 8) | (want << 24))
want ^= 0x8049d30
print hex(want)
# 0xbec6672a

将我们的解决方案输入到Z3的等式中,我们发现我们可以满足它。

input = 0xbec6672a
[4294967295 &
 ((input ^ 134520112) << 8 | (input ^ 134520112) >> 24) ^
 3405691582 ==
 134520840]
# True

为什么Z3找不到这个解决方案?

2 个答案:

答案 0 :(得分:3)

事实证明,在Z3中,移位运算符是算术移位而不是逻辑移位。

这意味着右移>>用符号位填充,而不是用零填充。

您必须使用逻辑右移(LShR)功能才能获得正常行为。

input    = BitVec('input',32)
feedback = 0x8049d30
shiftreg = input ^ feedback
shiftreg = (shiftreg << 8) | LShR(shiftreg, 24)
shiftreg = shiftreg ^ 0xcafebabe

s = Solver()
s.add(shiftreg == 0x804a008)
s.check()
hex(s.model()[input].as_long())
# 0xbec6672a

在此特定示例中,移位操作实际上是旋转。 Z3有一个直接进行旋转的机制(在这种情况下,它将是RotateLeft(shiftreg, 8)

答案 1 :(得分:1)

我相信“(shiftreg&gt;&gt; 24)”在z3 python API中被解释为算术右移:http://research.microsoft.com/en-us/um/redmond/projects/z3/z3.html(参见rshift)。我认为你期待合理的正确转变。首先,让我们将其重新编码为smt2。

(declare-fun input () (_ BitVec 32))
(define-fun feedback () (_ BitVec 32) #x08049d30)
(define-fun shiftreg0 () (_ BitVec 32)
  (bvxor feedback input))
(define-fun shiftreg1 () (_ BitVec 32)
  (bvand #xffffffff
         (bvor (bvshl shiftreg0 #x00000008)
               (bvlshr shiftreg0 #x00000018))))
(define-fun shiftreg2 () (_ BitVec 32)
  (bvxor shiftreg1  #xcafebabe))

(assert (= shiftreg2 #x0804a008))
(check-sat)

我们可以使用您最喜欢的QFBV解算器(z3,cvc4等)检查确实是这样。它是坐着的,它坐着“(=输入#xbec6672a)”断言。现在将“(bvlshr shiftreg0#x00000018)”更改为“(bvashr shiftreg0#x00000018)”。这是不可能的。改变转变。接下来,让我们检查shiftreg0的最高位是否需要为1.添加以下断言确实使问题不能解决。

(assert (not (= ((_ extract 31 31) shiftreg0) #b1)))

因此我们知道“(bvashr shiftreg0#x00000018)”将被强制转换为前14位的1s。因此,我们知道bvlshr和bvashr在此示例中的行为必须不同。

至于为什么最终的评价是真的,我只是猜测。 (我怀疑z3在python接口中无法推断出所有常量运算符的宽度,并且在内部有一个0在一个33 +位宽的常量中挂起。转Z3开发人员可以对此发表评论吗?)