定义“库安全”代码的概念

时间:2013-04-28 02:23:44

标签: c++ c language-agnostic posix libraries

首先,我希望这个问题不会过于开放。作为编码标准/政策,图书馆/ API等的比较的一种成分,我希望为形容词"图书馆安全"建立正式或至少半正式的定义。为了不将我自己的观点或其他任意观点强加于最佳实践作为定义的一部分,我想避免过度限制性的定义。作为我的意思的一个例子,认为任何访问静态存储持续时间的非const限定对象的代码,或者改变其他进程全局状态(例如信号处理),库不安全可能是一个有效的定义,但是它比它需要的要严格得多。

我想要捕获的基本属性,我认为当人们将代码称为“#library; safe-safe"”时,我认为这些属性是:

  1. 拥有多个"用户"同一进程中的库代码,无论是使用库代码的相同代码的多个实例(递归或线程),还是使用库代码完全独立的程序部分,而这些用户都没有踩到彼此。脚趾。

  2. 不修改"属于"的状态调用者,除非在接口合同中有记录。

  3. 我认为对这个问题有益的答案类型将是对过去类似于定义这个概念的尝试的引用,用于汇总定义的强烈想法,或者说服试图定义这个概念的论点是徒劳的。

    顺便说一句,我已经标记了这个C,C ++和POSIX,因为这些是我对应用这样一个定义最感兴趣的上下文。在其他语言的上下文中可能不那么有趣。 ;例如,在一种非常纯粹的函数式语言中,所有代码都应该被认为是#34;库安全的#34;。


    建议的定义:

    如果存在程序A和B,则库L是库不安全的:

    • A和B不接受任何输入。
    • A和B除退出状态外不产生任何输出。
    • A和B不会调用未定义的行为。
    • A和B不包含L之外的任何改变全局状态的代码。
    • 将A和B组合成一个程序,必要时重命名L之外的函数或对象以避免冲突,这样A和B的主要函数都在自己的线程中运行,导致程序调用未定义的行为或其输出与分别运行A和B的输出不同。

2 个答案:

答案 0 :(得分:1)

我同意你不访问非常规合格全球国家的标准可能过于严格。特别是,对于库中的C代码来说,修改errno以让更高级别的代码知道发生故障的原因可能是完全合理的。

许多要求(无论如何我都会看到它们)可能比文档本身更适用于文档。修改errno很好,但肯定需要记录。

对于C ++代码,异常安全性有些类似。显然,您希望大多数代码能够(合理地)提供最强的异常安全性,但提供的确切级别远不如它提供的文档记录重要。

答案 1 :(得分:0)

ACSL是C函数的规范语言。它遵循“按合同设计”的理念。但是,它并不打算编译为运行时断言,这是在其他按合同设计启发的框架中使用前后条件的最常用方式。

这看起来像是一个非主题的答案,因为如果我理解正确的话,你要问的是关于适当的库函数做什么的常见概念,而ACSL用于指定单个函数的作用。但我确实认为两者必须齐心协力。 “适当的库函数”的唯一定义是“完成它所说的功能”。任何不太通用的东西都会限制太多。例如,正常的库函数通常不应该取消分配内存...除非它是free()或被指定为解除分配内存。


因此,至少,看看ACSL预期的内容以及某些ACSL子句的默认值可以提示正确的库函数应具有的属性,除非另有说明

  • ACSL合同描述了函数在前置条件下的期望,以及它在后置条件下提供的保证。
  • ACSL契约意味着函数终止所有满足前提条件的输入,尽管可以另外指定。函数不终止的两种方法是永远循环或导致程序退出。
  • 传递给函数的内存位置允许别名,除非在前置条件中另有说明。
  • ACSL合同描述了该功能可能修改的所有内存位置。未列出的任何内存位置都不能通过调用该函数来修改。
  • 可选地,它描述了为每个输出的计算读取哪些存储器位置。对存储在内存中的函数的两次调用表明只有在读取内存位置列表之外的内容才会改变相同的内存位置并将它们设置为相同的值。
  • 在函数调用期间动态分配或释放的内存块在特定的ACSL子句中列出。默认情况下,函数既不分配也不释放内存。

某些副作用(例如更改FPU舍入模式)不会在ACSL中建模,但可以视为更改全局变量。其他副作用(创建线程,安装信号处理程序)是指C语言范围之外的C方面。

ACSL合同的一个例子:

/*@ requires \valid(((char*)s)+(0..n - 1));
  @ assigns ((char*)s)[0..n - 1] \from c;
  @ assigns \result \from s;
  @ ensures \forall integer i; (0 <= i < n) ==> ((char*)s)[i] == (char) c;
  @ ensures \result == s;
  @*/
void *memset(void *s, int c, size_t n);

作为关于ACSL的实际使用和库的实际非正式规范的最后一点,C语言在封装和规范之间存在折衷。特别是,如果库函数修改了static变量,则该变量不能从规范中省略,尽管它对于调用者来说是超出范围的。

random()之类的函数不会分配\result \from \nothing,因为这意味着两个连续的调用总是返回相同的结果。另一方面,double sin(double x);这样的函数可以用assigns \result \from x;来描述,因为只要参数相同,对sin()的两次调用就会返回相同的结果(FPU舍入模式)如果FPU舍入模式被建模为全局变量,它将是该函数的输入。)