python登录验证程序_自动化程序验证 for Python

0. Intro

本文介绍如何为Python中的函数进行auto-active verification。主要依赖于Hoare Logic的自动推导和SMT Solver。一时兴起写的小玩具(mini-dafny),随缘添加更多支持。

项目地址(Work-In-Progress):https://github.com/AD1024/veripy​github.com

为了提高食用体验,建议:了解Hoare Logic (Partial Correctness)

了解Program Verification with Hoare Logic

对Python没有生理性排斥反应(笑

1. 目标效果

@verify(

inputs=[('n', types.TINT)],

requires=['n >= 0'],

ensures=['x == n'])

def test_func(n):

y = n

x = 0

while y > 0:

invariant('x + y == n')

invariant('y >= 0')

x = x + 1

y = y - 1

return x

如上代码,我们想要实现verify这个装饰器。使用时给出输入的类型,对于输入的假设和输出满足的条件,然后验证这个函数是否满足我们的条件。对于loop invariant,我们用一个假的function call代替,在做AST mapping的时候处理。

主要流程如下:用inspect和ast两个库拿到对应函数的AST (Python AST)

将Python的AST转换到我们定义的AST(方便做Hoare Logic Inference)。实现一个Parser,能够将手动标注的表达式Parse到我们自己定义的AST (见transformer.py | parser/parser.py | parser/syntax.py)

跑weakeast_precondition(stmt, Q),其中stmt是转换好的AST,Q是用户提供的postcondition。目前这里的实现是返回一个

,其中

是我们的weakeast precondition,

是一个断言(side conditions)的集合。(verify.py)

利用Z3检查以下式子是否valid (若formula

满足

称其为valid): (其中

是用户提供的precondition)

(verify.py| transformer.py)

简单来说就是,通过transform AST得到weakest precondition然后通过rule of consequence检查hoare logic是否valid。一个简单的变换:因为我们用的是SMT Solver,只能检查是否存在一个model M满足我们的constraints,所以这里需要取反(

),然后检查我们的constraint是否unsat即可。

2. Syntax

为了方便计算weakest precondition,我们需要定义一个语法(maybe subset of Python)。最基础的我实现了这几个:赋值

While循环

If-then-else

Assert / Assume

Skip (No-op)

隐藏AST: Seq,连接两个Statement。

大致语法是这样(示意)

stmt ::= Skip

| Assert e

| Assume e

| If e then stmt else stmt

| While e do stmt

| Seq stmt stmt

e ::= e + e | e - e | e * e | e / e

| e and e

| e or e

| not e

| e ==> e

| e <==> e

Weakest Precondition

定义一个函数wp(s, Q),接受一个statement和一个postcondition,返回s的weakest precondition P使得P ==> Q。

这里给出weakest percondition的计算方法:

Skip: Trivial

Assume: 如果我们在程序中间(其实一般是在开始的时候assume输入值的一些性质)assume了一个e,那么我们至少要保证e => Q。

Assignment:对于x = y的wp,显然如果post condition是Q,那么最弱的需要被满足的precondition就是将y替换到Q中对x的断言。

Assert: 如果我们在程序执行过程中断言了e,那么若程序最终step到了Skip,说明这个assertion没有被打破,因此最弱的P是

Seq: 因为是wp,所以我们要从最后一个statement往前推,因此:

If e then s1 else s2: 因为当 e为True时我们会执行s1,反之执行s2,所以对于整个If语句,我们至少要满足:

While e s: 这里我们会用到side conditions那个set。While的wp本质就是loop invariant,但是除了满足invariants以外,我们还要保证 ,这里I是loop invariant,

是s的wp。s的wp也很容易,因为I自始至终都是被满足的,因此

然后我们将计算s的wp时的side conditions和上面的两个条件合并作为While的side conditions即可。

使用例

考虑以下求和程序,我们验证用这个While求和本质和使用求和公式一样

@verify(

inputs=[

('n', types.TINT)

],

requires=[

'n >= 0'

],

ensures=[

'ans == ((n + 1) * n) // 2'

]

)

def Summation(n):

ans = 0

i = 0

while i <= n:

ans = ans + i

i = i + 1

return ans

我们在不标注任何loop invariant的情况下进行验证,会有这样的报错

Exception: VerificationViolated on

Not(Implies(And(True, Not(i <= n)), ans == ((n + 1)*n)/2))

Model: [i = 1, ans = -1, n = 0]

Side condition violated

能看出来,这是说在循环结束之后,我们的precondition推不出我们的结论(ans == ((n + 1) * n) // 2)

OK,因为我们什么都没有标,invariant是True.....

我们知道循环条件是i <= n,那么一个很明显的invariant就是i <= n + 1。另外,我们还需要找到一个invariant建立ans和i的联系。

@verify(

inputs=[

('n', types.TINT)

],

requires=[

'n >= 0'

],

ensures=[

'ans == ((n + 1) * n) // 2'

]

)

def Summation(n):

ans = 0

i = 0

while i <= n:

invariant('i <= n + 1')

invariant('ans == i * (i - 1) // 2')

ans = ans + i

i = i + 1

return ans

找invariant就是纯靠经验(直觉)了...直觉告诉我,track上一个iteration的i就可以(小声

这时候再跑verification,嗯,verified.

更多例子可以看https://github.com/AD1024/veripy/tree/master/examples​github.com

之后的工作

可能有些人已经发现,这个weakest precondition的计算方法和想象中不太一样。是的...这样是比较偷懒的写法。常规操作是要unroll while loop,然后havoc loop variable。

这我只能说

在做了在做了

TODOs:Array

Havoc

Function call

Quantifiers

更多Python语法的支持

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值