frama-c 切片插件似乎丢弃使用过的堆栈值

frama-c slicing plugin appears to discard used stack values

问题描述

我正在开发一个 frama-c 插件,它使用切片插件作为库来删除未使用的自动生成代码位。不幸的是,切片插件会丢弃一堆实际使用的堆栈值。只要它们的地址包含在传递给抽象外部函数的结构中,它们就会被使用。

简单示例

这是一个更简单的示例,它模拟了我所拥有的相同的一般结构。

/* Abstract external function */
void some_function(int* ints[]);

int main() {
  int i;
  int *p = &i;
  int *a[] = { &p };
  some_function(a);
  return 0;
}

当使用 frama-c-gui -slice-calls some_function experiment_slicing.c 对这个示例进行切片时(我还没有弄清楚在没有 gui 的情况下调用命令行时如何查看切片输出)它会删除除声明 int *a[]; 和调用之外的所有内容至 some_function.

尝试修复

我尝试通过添加 ACSL 注释来修复它。 然而,我认为合理的规范(见下文)不起作用

/*@ requires \valid(ints) && \valid(ints[0]);
 */
void some_function(int* ints[]);

然后我尝试使用确实具有所需行为的分配(见下文)。然而,这不是一个正确的规范,因为该函数实际上从未写入指针,而是需要读取它才能实现正确的功能。我担心如果我继续使用这样一个不正确的规范,它会导致奇怪的问题。

/*@ requires \valid(ints) && \valid(ints[0]);
   assigns  *ints;
*/
void some_function(int* ints[]);

您在正确的轨道上:您应该在此处使用 assigns 子句:它将指示内存状态的哪些部分与未定义函数的调用有关。但是,您需要提供一个完整的 assigns 子句,其 \from 部分(指示读取哪个内存位置以计算写入的内存位置的新值)。

我在您的示例中添加了一个 int 变量,因为您的函数没有 return 结果(void return 类型)。对于一个 returning 东西的函数,你还应该有一个子句 assigns \result \from ...;:

int x;

/*@ assigns x \from indirect:ints[..], *(ints[..]); */
void some_function(int* ints[]);

int main() {
  int i;
  int*p = &i;
  int *a[] = { &p };
  some_function(a);
  return 0;
}

assigns 子句表示 some_function 可能会更改 x 的值,并且新值将从 ints[..] 中存储的地址计算得出 (indirect 标签表明我们没有直接使用它们的值,这在 Eva's manual 的第 8.2 节中有更详细的描述),以及它们的内容。

using frama-c -slice-calls some_function file.c -then-last -print(这里最后的参数用于在标准输出上打印生成的文件:-then-last 表示以下选项应在最后创建的 Frama-C 项目上运行,因为如果是切片产生的结果,-print 打印所述项目的 C 代码。您也可以使用 -ocode output.c 将代码的漂亮打印重定向到 output.c。)给出以下结果:

* Generated by Frama-C */
void some_function(int **ints);

void main(void)
{
  int i;
  int *p = & i;
  int *a[1] = {(int *)(& p)};
  some_function(a);
  return;
}

另外请注意,您的示例类型不正确:&p 是指向 int 的指针,因此应存储在 int** 数组中,而不是 int* 大批。但我认为它只是源于减少你原来的例子,对切片本身并不重要。