验证= TCB / PB减少

2021-05-12 18:08:18

在高中数学中,我们被引入了证明的概念 - “证明”这个词与“探针”一词的词语相关。在证明中,我们从第一个原则(AKA公理)的原因,为什么某种断言(AKA定理)保持真实。如有不同,我们验证定理,证明代表验证过程。

该语言的原理和证据需要一种语言 - 至少是该语言涉及语法和一组管理该语法的证明规则。基于数学规则的验证也称为正式验证,以区分其从口语使用中可能只是涉及人类检查的验证。

正在验证的定理通常被分为两部分:(1)实施,(2)所谓的规范,该规范应该适用于该实施。以下是我们假设的一个示例,规格,我们假设的公理系统,以及可用于完成证明的证明系统。

证明作者可以选择公理和证明规则的抽象级别,以便在证明简单和严格水平之间进行权衡。例如,作者可以选择考虑作为公理的自然数量的换向,也许在其他地方分开证明它。或者,作者可以选择仅将自己限制到较低级别的公理,以潜在地获得更加简洁和优雅的证明。

类似地,作者可以选择假设数学归纳的有效性,即使可以从诸如Noetherian诱导的更常规证明规则中衍生来源。

从纯粹的理论上的角度来看,你不应该重要(抽象水平)你选择的公理和证明规则。然而,从计算机系统的角度来看,这确实对您对验证工作的信心产生了影响,因为我们讨论了以下(TL;更高的抽象级别略微增加规格和证明规则的大小,因此可能增加了概率错误逃避验证过程)。

size_t strlen(char const * str){char * ptr; ulong * longword_ptr; ulong longword,himagic,lomagic; for(ptr = str;((unong)ptr& 7)!= 0; ++ PTR)如果(* ptr =='\ 0')返回ptr-str; longword_ptr =(Ulong *)PTR; himagic = 0x8080808080808080l; Lomagic = 0x0101010101010101L; for(;;){longword = * longword_ptr; if((longword - lomagic)&〜longword& himagic){char * cp =(char *)(longword_ptr - 1); if(cp [0] == 0)返回cp - str; if(cp [1] == 0)返回cp - str + 1; if(cp [2] == 0)返回cp - str + 2; if(cp [3] == 0)返回cp - str + 3; if(cp [4] == 0)返回cp - str + 4; if(cp [5] == 0)返回cp - str + 5; if(cp [6] == 0)返回cp - str + 6; if(cp [7] == 0)返回cp - str + 7;}}}

在软件中,我们通常对证明计算机程序的属性感兴趣。该程序将有自己的语法,具体取决于它已经开发的编程语言(PL)。计算机程序的语法可以与用于规范,公理和证明的语法不同。在下文中,我们显示了四种不同的方法来编写C库的strlen()函数的规范;对于每个规格方法,我们还提供所需的公理和证明系统。

具有BitVector和Array理论的无量值逻辑。操作员的证明规则,如下所示

操作员的证明规则如下所示。如果您不是读取此表示法的专家,则不会打扰 - 基本上这些是一组尝试解决操作员的规则,具体取决于它发生的上下文。机械检查器可以使用这些推理规则作为输入来决定证明步骤的有效性。

在这些推理规则中,CTX表示执行此评估的上下文,堆代表存储器阵列,Addr B和Addr E表示表示用于索引到堆的地址的BitVectors。 l表示一个(lambda)函数,它需要一个字节并返回true或false。直观地,操作员将返回字节的第一个索引(相对于ADDR B)(在Addr B和Addr E之前启动),其评估l为true。如果ADDR B和ADDR E之间没有评估L为TRUE,则使用-1的Sentinel值。例如,第一推断规则(基本情况)指出,如果Addr B和Addr E相等(即,范围内没有字节),则评估到Sentinel值-1(无论堆和l)。运算符代表与环绕语义的BitVector添加。

请注意,我们定论中的Addr E参数关联strlen为零,指示我们有兴趣在内存地址空间中找到str和最大地址值addr max之间的第一个空字符(调用该addr max 1 = 0)。

size_t strlennaive(char const * s){char const * p = s; for(p = s; * p; p ++);返回p - s;}

在这种规范方法中,STRLENNAVE和STRLEN都以相同的语法(C编程语言的语法)指定。然而,规范程序(StrennaIve)明显更简单,而不是实现(strlen)。

归纳字符串:SET:= |空字符串:字符串|字符串:ASCII - >弦 - >字符串FixPoint StrlencoQ(strcoq:string):nat:=匹配s |空心队=> 0 |字符串C s' => S(长度S')结束。

这里,不同的语法(COQ)用于规范程序。使用更高级别的COQ语法具有更高级别的语法不允许诸如C语法中可能的多个不安全行为(例如在Adam Chlipala博客文章中讨论的内存错误)的益处。另一方面,关于COQ规范的证据现在要求递送递送两个不同表示之间的数据结构 - 例如,C中的STR是需要与STRCOQ相关的字符阵列,该字符数组是用户指定的抽象数据类型。

具有BitVector和Array理论的无量值逻辑。掺入和分发。支持C和COQ中数据结构的关系谓词

如果它得出结论,即使没有,证明或验证过程是不正确的。

无论规范和证明方法如何,验证过程都可能不正确,原因之一:

不连贯的证明步骤:如果证明中的任何步骤都没有从现有定理/公理/证据规则中遵循,那么我们的证据不正确。防范这种可能性的常见解决方案是有机械证明检查器,被称为计算机程序,可以确定证明的所有步骤都有效。但是,如果证明检查器有错误或疏忽,那么您可能会承认证据不正确。

不正确的公理:如果公理是不正确的,那么这可能会破坏整个证明工作。在验证计算机系统的上下文中,例如,编程语言的语义形成了一组公理。此外,规范本身是公理的一部分,例如,如果更简单的strnenaive函数有一个错误,那么我们也会承认strlen实现中的相同错误。重要的是要么是小(使它们是手动验证的)或严格地测试。例如,Strennaive函数足够小,可以通过单独手动检查来发展信心。另一方面,C语言语言的语言语言很大,难以手动读取以发展信心。幸运的是,C语言语言涉及单个代码库,用于验证和测试成千上万的CodeBases,因此这种广泛的测试和生产使用提供了对其正确性的信心。例如,可以通过检查逻辑编码的输出与通过物理计算机上的C编译产生的输出相同的输出来测试C语义的逻辑编码。

证明规则不正确:如果任何证明规则都是非疑问(越野车),则校对步骤可能会变得不健全,这反过来导致证明不正确。在我们的示例中,大多数证明规则基于BitVectors和阵列的标准量化或一阶逻辑。这些证明规则经过了多种代码审查,广泛的测试,并在成千上万的用户使用中使用,因此我们可以合适地对其正确性充满信心。对于为自定义公理运算符添加的定制规则,诸如示例中添加的定制规则,有一个略高的概率。然而,即便如此,这些自定义的规则通常比实施更简单,他们确认(因为他们并不需要优化做),并进一步他们有更广泛的应用和测试(例如,可用于多种基于搜索的算法,并不限于Strlen)。

我们已经讨论过,证明可能不正确。如果您阅读了John Regehr的钢琴测试上的这个有趣的博客帖子,您现在知道您不想坐在那个钢琴下。

那么,如果不保证没有错误的情况,那么软件程序是什么意思的,如果不保证错误?

正式验证工作的价值是它降低了您的可信计算基础/错误的计算基础/概率,或TCB / PB。在我们的strlen的例子,如果实现已经正式验证(使用任意四种规格的方法),那么我们知道,那里是在执行中的错误,必须有规范或者错误(这是小得多不是实现),或者必须有C编程语言的语义(已严格测试的逻辑编码错误),或者必须有一个错误的证据规则(也大量测试)或存在必须在一个错误校对检查器(也严重测试)。如果这些子系统中的任何错误的机会低于原始实现中错误的机会,则正式验证过程增加了值。

值得强调的是,TCB中的“减少”不一定是可量化的减少,例如,它通常不能单独通过SLOC(代码源线)来衡量。对于证明的某些组件,例如规范,通过SLOC量化TCB减少是有意义的。但对于其他组件,如公理,证明规则和证明跳棋,我们不依靠SLOC减少,但这样的事实,这些都是已经经过大量测试,而且往往重生产中使用的基础设施组件。因此,TCB减少不足以捕获验证工作的价值,因此我们还需要量化错误概率(PB)的减少。

此外,即使这些基础架构验证组件中的任何一个都有一个角落案例错误,该错误也不太可能以它导致错误的证据方式表现出来。例如,我的STRLEN实现更有可能具有内存错误,而不是证明检查器中的内存错误的可能性,导致strlen验证到错误地成功。在最糟糕的是,验证工具中的一个角落案例错误会导致验证过程突然结束,不会导致错误的证据(具有很高的概率)。

在实践中,正式验证非常有用,因为它能够捕获几个角色案例错误。对于任务关键的应用,例如防御,航空航天和汽车领域的关键应用,软件的正式验证通过数量级来降低误差的可能性,并防止灾难(例如,波音)。

实现的速度发展比验证基础设施需要修改/调整的步伐更快 - 因此即使在连续代码修改的情况下也提供了验证线束的存在提供了众多所需的正确性。此类验证基础架构允许组织节省测试/ QA成本。

安全性属性通常更适合验证而不是测试。研究人员在严格测试的编译器和库中发现了严重的错误 - 大多数此类角色案例错误很难通过测试,并且更容易通过验证工具捕获。如果您通过我们的验证工具找到的错误示例查看了错误的页面,您会发现最常见的错误涉及像指针别名,缓冲区/整数溢出等角色函数,在类型转换期间溢出等等。验证工具具有在识别此类隐藏错误和安全漏洞的传统测试方法方面具有独特的优势。

机器可读证明用作可执行文件,并帮助工程团队随着时间的推移在开发和维护其软件方面。

我谦卑的预测是验证软件成为常态之前只是时间问题。