c - 将字符串附加到动态字符数组的函数的 ACSL 规范

标签 c dynamic-arrays frama-c formal-verification acsl

我正在为将给定字符串附加到动态字符数组末尾的函数编写 ACSL 规范。

这是我目前所拥有的:

#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#ifndef SIZE_MAX
#define SIZE_MAX ((size_t)-1)
#endif

#undef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))

struct st_char_vector {
  char *buf;
  size_t capacity;
  size_t length;
};

/*@ predicate valid_char_vector(struct st_char_vector *vec) =
  @   \valid_read(vec) &&
  @   vec->capacity > 0 &&
  @   \valid(vec->buf + (0..vec->capacity - 1)) &&
  @   vec->length <= vec->capacity;
  @*/


/*@ requires valid_char_vector(vec);
  @ requires new_capacity >= vec->capacity;
  @ ensures valid_char_vector(vec);
  @ ensures \old(vec->length) == vec->length;
  @ ensures memcmp{Pre,Post}(vec->buf, vec->buf, vec->length) == 0;
  @ behavior err:
  @   ensures !\result;
  @   ensures \old(vec->buf) == vec->buf;
  @   ensures \old(vec->capacity) == vec->capacity;
  @ behavior ok:
  @   ensures \result;
  @   ensures vec->capacity >= new_capacity;
  @ complete behaviors;
  @ disjoint behaviors;
  @*/
static int char_vector_reallocate(struct st_char_vector *vec, size_t new_capacity);

/*@ requires valid_char_vector(vec);
  @ requires \valid_read(str + (0..str_length - 1));
  @ requires string_separated_from_extra_capacity:
  @   \separated(str + (0..str_length - 1), vec->buf + (vec->length..vec->capacity - 1));
  @ ensures valid_char_vector(vec);
  @ ensures old_content_unchanged: memcmp{Pre,Post}(vec->buf, vec->buf, \old(vec->length)) == 0;
  @ ensures \forall integer i; 0 <= i && i < \old(vec->length) ==> \old(vec->buf[i]) == vec->buf[i];
  @ behavior err:
  @   ensures !\result;
  @   ensures buf_unchanged: \old(vec->buf) == vec->buf;
  @   ensures capacity_unchanged: \old(vec->capacity) == vec->capacity;
  @   ensures length_unchanged: \old(vec->length) == vec->length;
  @ behavior ok:
  @   ensures \result;
  @   ensures str_length_added_to_length: vec->length == \old(vec->length) + str_length;
  @   ensures string_appended: memcmp{Post,Post}(vec->buf + \at(vec->length, Pre), str, str_length) == 0;
  @ complete behaviors;
  @ disjoint behaviors;
  @*/
int char_vector_append(struct st_char_vector *vec, const char *str, size_t str_length) {
  if (SIZE_MAX - str_length < vec->capacity) {
    return 0;
  }

  if (vec->capacity < (vec->length + str_length)) {
    if (!char_vector_reallocate(vec, vec->capacity + str_length)) {
      //@ assert \at(vec->length, Pre) == \at(vec->length, Here);
      return 0;
    }
  }

  memcpy(vec->buf + vec->length, str, str_length);
  vec->length += str_length;

  return 1;
}

因为还不支持动态内存分配的验证,所以我添加了一个占位符函数 char_vector_reallocate() 和 ACSL 规范,但没有显示实现。

使用 Frama-C Sodium-20150201 和 WP 插件,我无法验证 6 个属性:

  • typed_char_vector_append_disjoint_ok_err
  • typed_char_vector_append_err_post
  • typed_char_vector_append_err_post_length_unchanged
  • typed_char_vector_append_ok_post
  • typed_char_vector_append_ok_post_str_length_added_to_length
  • typed_char_vector_append_ok_post_string_appended

我没想到在验证前 5 个属性时会遇到任何困难。

如何修复 ACSL 以便验证 char_vector_append()

(作为旁注,是否有动态数组的 ACSL 规范示例,我可以将其作为指南引用?)

最佳答案

你缺少assumes允许 WP 区分您的 ok 的条款和 err个案。一个很好的见证是你无法证明 disjoint契约(Contract)的条款。基本上,如果

  • 缓冲区和字符串的总和太大,
  • 缓冲区太小,无法容纳字符串,
  • 剩余内存不足

为了对第三点进行建模,我建议使用一个 ghost 变量来指示是否有足够的空闲内存来扩展 vector 。在你的情况下,因为只有一个分配,一个简单的 bool 标志就可以了,例如//@ ghost int mem_full .

当然,你需要修改char_vector_reallocate的spec因此:它应该assigns mem_full及其 behavior应该有 assumes基于 mem_full 初始值的子句.

最后,您关于 memcmp 的第一个参数有问题在ensures char_vector_reallocate的条款在old_content_unchanged : 参数本身应该在 Pre 中被评估-状态。否则,您是说 vec->buf 指向的内容(处于 Post 状态)处于 Pre 状态-state 比较等于 was 由 vec->buf 指向(再次处于 Post 状态)在 Post 中-状态。即,评估 memcmp 的参数发生在当前状态,与参数中给出的标签无关。

以下是Alt-Ergo证明一切的版本

#include "stdlib.h"
#include "string.h"

#ifndef SIZE_MAX
#define SIZE_MAX ((size_t)-1)
#endif

#undef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))

struct st_char_vector {
  char *buf;
  size_t capacity;
  size_t length;
};

/*@ predicate valid_char_vector(struct st_char_vector *vec) =
  @   \valid_read(vec) &&
  @   vec->capacity > 0 &&
  @   \valid(vec->buf + (0..vec->capacity - 1)) &&
  @   vec->length <= vec->capacity;
  @*/

//@ ghost extern int mem_full;

/*@ requires valid_char_vector(vec);
  @ requires new_capacity >= vec->capacity;
  @ assigns mem_full;
  @ ensures valid_char_vector(vec);
  @ ensures \old(vec->length) == vec->length;
  @ ensures valid_char_vector(vec);
  @ ensures memcmp{Pre,Post}(\at(vec->buf,Pre), vec->buf, vec->length) == 0;
  @ behavior err:
      assumes mem_full;
  @   ensures !\result;
  @   ensures \old(vec->buf) == vec->buf;
  @   ensures \old(vec->capacity) == vec->capacity;
  @ behavior ok:
      assumes !mem_full;
  @   ensures \result;
  @   ensures vec->capacity >= new_capacity;
  @ complete behaviors;
  @ disjoint behaviors;
  @*/
static int char_vector_reallocate(struct st_char_vector *vec, size_t new_capacity);

/*@ requires valid_char_vector(vec);
  @ requires \valid_read(str + (0..str_length - 1));
  @ requires string_separated_from_extra_capacity:
  @   \separated(str + (0..str_length - 1), vec->buf + (vec->length..vec->capacity - 1));
  @ ensures valid_char_vector(vec);
  @ ensures old_content_unchanged: memcmp{Pre,Post}(\at(vec->buf,Pre), vec->buf, \old(vec->length)) == 0;
  @ ensures \forall integer i; 0 <= i && i < \old(vec->length) ==> \old(vec->buf[i]) == vec->buf[i];
  @ behavior err:
      assumes vec->capacity+str_length>SIZE_MAX || 
      (vec->length+str_length>vec->capacity && mem_full);
  @   ensures !\result;
  @   ensures buf_unchanged: \old(vec->buf) == vec->buf;
  @   ensures capacity_unchanged: \old(vec->capacity) == vec->capacity;
  @   ensures length_unchanged: \old(vec->length) == vec->length;
  @ behavior ok:
      assumes vec->capacity+str_length<=SIZE_MAX && 
      (vec->length+str_length<=vec->capacity || !mem_full);
  @   ensures \result;
  @   ensures str_length_added_to_length: vec->length == \old(vec->length) + str_length;
  @   ensures string_appended: memcmp{Post,Post}(vec->buf + \at(vec->length, Pre), str, str_length) == 0;
  @ complete behaviors;
  @ disjoint behaviors;
  @*/
int char_vector_append(struct st_char_vector *vec, const char *str, size_t str_length) {
  if (SIZE_MAX - str_length < vec->capacity) {
    return 0;
  }

  if (vec->capacity < (vec->length + str_length)) {
    if (!char_vector_reallocate(vec, vec->capacity + str_length)) {
      return 0;
    }
  }
   memcpy(vec->buf + vec->length, str, str_length);
  vec->length += str_length;

  return 1;
}

关于c - 将字符串附加到动态字符数组的函数的 ACSL 规范,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33682909/

相关文章:

annotations - 使用 Frama-C 脚本打印 ACSL 注释

c - 使用 fscanf 跳过行

c - 函数末尾出现段错误

c - 使用 Frama-c 测试大文件中的中间变量

Delphi 2006 - 分配动态数组函数结果

c - 动态字符串数组不起作用

ubuntu - 在 ubuntu 上安装 frama-c

c - 如何将链接列表传递给c中的函数

c - 获取位域结构成员的大小

条件操作