2090 字
10 分钟
实验1:从软件测试到形式化验证
2026-10-25 22:50
无标签

任务1:测试整数中点计算函数#

程序运行输入输出:

Terminal window
❯ ./midpoint_test.out
0 0
0
❯ ./midpoint_test.out
0 1
0
❯ ./midpoint_test.out
1 2147483647
-1073741824
❯ ./midpoint_test.out
2147483647 2147483647
-1

已运行:

No.lowhighmidpointoutput
10000
20100
3121474836471073741824-1073741824
4214748364721474836472147483647-1

未运行:

No.lowhighmidpointoutput
5294738/
635077014025/
7107374182410737418241073741824/

测试结论:当前测试结果能够支持 low + high 较小的情形; 对于 low + high 较大的情况,程序会发生溢出,导致得到错误的结果。 当前测试结果可以支持测试点 5 与测试点 6 ,无法支持测试点 7 。

当前测试结果可以说明程序在 low + high 较小的情形下大致运行良好,在 low + high 较大的情形下可能产生错误的输出 不能说明对于所有的合法输入,程序都可以按照预期运行。

任务2:使用 CBMC 检查整数溢出#

程序运行输入输出:

Terminal window
❯ cbmc midpoint_cbmc.c --signed-overflow-check --all-properties --trace
CBMC version 6.11.0 (640889b) 64-bit x86_64 linux
Type-checking midpoint_cbmc
Generating GOTO Program
Adding CPROVER library (x86_64)
Removal of function pointers and virtual functions
Generic Property Instrumentation
108 collapsed lines
Starting Bounded Model Checking
Passing problem to propositional reduction
converting SSA
Running propositional reduction
SAT checker: instance is SATISFIABLE
Building error trace
** Results:
midpoint_cbmc.c function main
[main.assertion.1] line 10 midpoint is inside the interval: FAILURE
midpoint_cbmc.c function midpoint
[midpoint.overflow.1] line 3 arithmetic overflow on signed + in low + high: FAILURE
Trace for main.assertion.1:
State 13 file midpoint_cbmc.c function main line 6 thread 0
----------------------------------------------------
return_value_nondet_int=177393664 (00001010 10010010 11010000 00000000)
State 14 file midpoint_cbmc.c function main line 6 thread 0
----------------------------------------------------
low=177393664 (00001010 10010010 11010000 00000000)
State 17 file midpoint_cbmc.c function main line 7 thread 0
----------------------------------------------------
return_value_nondet_int$0=1981235200 (01110110 00010111 01000000 00000000)
State 18 file midpoint_cbmc.c function main line 7 thread 0
----------------------------------------------------
high=1981235200 (01110110 00010111 01000000 00000000)
Assumption:
file midpoint_cbmc.c line 8 function main
0 <= low && low <= high
State 24 file midpoint_cbmc.c function main line 9 thread 0
----------------------------------------------------
low=177393664 (00001010 10010010 11010000 00000000)
State 25 file midpoint_cbmc.c function main line 9 thread 0
----------------------------------------------------
high=1981235200 (01110110 00010111 01000000 00000000)
Violated property:
file midpoint_cbmc.c function midpoint line 3 thread 0
arithmetic overflow on signed + in low + high
!overflow("+", signed int, low, high)
State 27 file midpoint_cbmc.c function midpoint line 3 thread 0
----------------------------------------------------
goto_symex$$return_value$$midpoint=-1068169216 (11000000 01010101 00001000 00000000)
misskey
State 29 file midpoint_cbmc.c function main line 9 thread 0
----------------------------------------------------
return_value_midpoint=-1068169216 (11000000 01010101 00001000 00000000)
State 30 file midpoint_cbmc.c function main line 9 thread 0
----------------------------------------------------
mid=-1068169216 (11000000 01010101 00001000 00000000)
Violated property:
file midpoint_cbmc.c function main line 10 thread 0
midpoint is inside the interval
low <= mid && mid <= high
Trace for midpoint.overflow.1:
State 13 file midpoint_cbmc.c function main line 6 thread 0
----------------------------------------------------
return_value_nondet_int=177393664 (00001010 10010010 11010000 00000000)
State 14 file midpoint_cbmc.c function main line 6 thread 0
----------------------------------------------------
low=177393664 (00001010 10010010 11010000 00000000)
State 17 file midpoint_cbmc.c function main line 7 thread 0
----------------------------------------------------
return_value_nondet_int$0=1981235200 (01110110 00010111 01000000 00000000)
State 18 file midpoint_cbmc.c function main line 7 thread 0
----------------------------------------------------
high=1981235200 (01110110 00010111 01000000 00000000)
Assumption:
file midpoint_cbmc.c line 8 function main
0 <= low && low <= high
State 24 file midpoint_cbmc.c function main line 9 thread 0
----------------------------------------------------
low=177393664 (00001010 10010010 11010000 00000000)
State 25 file midpoint_cbmc.c function main line 9 thread 0
----------------------------------------------------
high=1981235200 (01110110 00010111 01000000 00000000)
Violated property:
file midpoint_cbmc.c function midpoint line 3 thread 0
arithmetic overflow on signed + in low + high
!overflow("+", signed int, low, high)
** 2 of 2 failed (2 iterations)
VERIFICATION FAILED

从下列代码片段中可知 low + high 的有符号整数溢出性质

Terminal window
Violated property:
file midpoint_cbmc.c function midpoint line 3 thread 0
arithmetic overflow on signed + in low + high
!overflow("+", signed int, low, high)

反例为low = 177393664; high = 1981235200; mid = -1068169216, 满足 0≤low≤high0 \leq low \leq high 的输入假设。 由数学计算知其和较小中点为 1079314432 ,仍在 int 表示范围内。 但根据函数 midpoint 的定义:

int midpoint(int low, int high) {
return (low + high) / 2;
}

程序执行过程中会先计算 (low + high) ,该过程中发生有符号整数溢出。

将反例作为任务 1 程序的输入,实际运行结果如下:

Terminal window
❯ ./midpoint_test.out
177393664 1981235200
-1068169216

运行结果与 CBMC 运行输出相符

任务3:分别检查计算安全和功能要求#

midpoint_test 输入输出:

Terminal window
❯ g++ midpoint_test.c -o midpoint_test.out
❯ ./midpoint_test.out
0 0
0
❯ ./midpoint_test.out
0 1
0
❯ ./midpoint_test.out
1 2147483647
1073741824
❯ ./midpoint_test.out
2147483647 2147483647
2147483647
❯ ./midpoint_test.out
29 47
38
❯ ./midpoint_test.out
350 7701
4025
❯ ./midpoint_test.out
1073741824 1073741824
1073741824

测试用例:

No.lowhighmidpointoutput
10000
20100
31214748364710737418241073741824
42147483647214748364721474836472147483647
529473838
6350770140254025
71073741824107374182410737418241073741824

midpoint_cbmc 输入输出:

Terminal window
❯ cbmc midpoint_cbmc.c --signed-overflow-check --all-properties --trace
CBMC version 6.11.0 (640889b) 64-bit x86_64 linux
Type-checking midpoint_cbmc
Generating GOTO Program
Adding CPROVER library (x86_64)
Removal of function pointers and virtual functions
Generic Property Instrumentation
19 collapsed lines
Starting Bounded Model Checking
Passing problem to propositional reduction
converting SSA
Running propositional reduction
SAT checker: instance is UNSATISFIABLE
** Results:
midpoint_cbmc.c function main
[main.assertion.1] line 13 midpoint is inside the interval: SUCCESS
[main.assertion.2] line 15 midpoint has the expected offset: SUCCESS
[main.overflow.1] line 15 arithmetic overflow on signed - in mid - low: SUCCESS
[main.overflow.2] line 15 arithmetic overflow on signed - in high - low: SUCCESS
midpoint_cbmc.c function midpoint
[midpoint.overflow.1] line 3 arithmetic overflow on signed - in high - low: SUCCESS
[midpoint.overflow.2] line 3 arithmetic overflow on signed + in low + (high - low) / 2: SUCCESS
** 0 of 6 failed (1 iterations)
VERIFICATION SUCCESSFUL

low 和 high 可以取任意 int 范围内整数,输入假设排除了 0>low∨low>high0 > low \vee low > high 的情形。

第一个断言检查结果是否仍在原区间中;第二个断言检查结果与 low 的距离是否等于区间长度的一半:

实际覆盖的输入条件为 low 与 high 均为 int 范围内整数且满足 0≤low≤high0 \leq low \leq high,具有避免有符号整数溢出的性质。

将 midpoint 修改为下面版本

int midpoint(int low, int high) {
return low / 2 + high / 2;
}

CBMC 运行结果如下:

Terminal window
❯ cbmc midpoint_cbmc.c --signed-overflow-check --all-properties --trace
CBMC version 6.11.0 (640889b) 64-bit x86_64 linux
Type-checking midpoint_cbmc
Generating GOTO Program
Adding CPROVER library (x86_64)
Removal of function pointers and virtual functions
Generic Property Instrumentation
122 collapsed lines
Starting Bounded Model Checking
Passing problem to propositional reduction
converting SSA
Running propositional reduction
SAT checker: instance is SATISFIABLE
Building error trace
Running propositional reduction
SAT checker: instance is SATISFIABLE
Building error trace
Running propositional reduction
SAT checker: instance is UNSATISFIABLE
** Results:
midpoint_cbmc.c function main
[main.assertion.1] line 13 midpoint is inside the interval: FAILURE
[main.assertion.2] line 15 midpoint has the expected offset: FAILURE
[main.overflow.1] line 15 arithmetic overflow on signed - in mid - low: SUCCESS
[main.overflow.2] line 15 arithmetic overflow on signed - in high - low: SUCCESS
midpoint_cbmc.c function midpoint
[midpoint.overflow.1] line 3 arithmetic overflow on signed + in low / 2 + high / 2: SUCCESS
Trace for main.assertion.1:
State 13 file midpoint_cbmc.c function main line 6 thread 0
----------------------------------------------------
return_value_nondet_int=1 (00000000 00000000 00000000 00000001)
State 14 file midpoint_cbmc.c function main line 6 thread 0
----------------------------------------------------
low=1 (00000000 00000000 00000000 00000001)
State 17 file midpoint_cbmc.c function main line 7 thread 0
----------------------------------------------------
return_value_nondet_int$0=1 (00000000 00000000 00000000 00000001)
State 18 file midpoint_cbmc.c function main line 7 thread 0
----------------------------------------------------
high=1 (00000000 00000000 00000000 00000001)
Assumption:
file midpoint_cbmc.c line 9 function main
0 <= low && low <= high
State 24 file midpoint_cbmc.c function main line 11 thread 0
----------------------------------------------------
low=1 (00000000 00000000 00000000 00000001)
State 25 file midpoint_cbmc.c function main line 11 thread 0
----------------------------------------------------
high=1 (00000000 00000000 00000000 00000001)
State 27 file midpoint_cbmc.c function midpoint line 3 thread 0
----------------------------------------------------
goto_symex$$return_value$$midpoint=0 (00000000 00000000 00000000 00000000)
State 29 file midpoint_cbmc.c function main line 11 thread 0
----------------------------------------------------
return_value_midpoint=0 (00000000 00000000 00000000 00000000)
State 30 file midpoint_cbmc.c function main line 11 thread 0
----------------------------------------------------
mid=0 (00000000 00000000 00000000 00000000)
Violated property:
file midpoint_cbmc.c function main line 13 thread 0
midpoint is inside the interval
low <= mid && mid <= high
Trace for main.assertion.2:
State 13 file midpoint_cbmc.c function main line 6 thread 0
----------------------------------------------------
return_value_nondet_int=1 (00000000 00000000 00000000 00000001)
State 14 file midpoint_cbmc.c function main line 6 thread 0
----------------------------------------------------
low=1 (00000000 00000000 00000000 00000001)
State 17 file midpoint_cbmc.c function main line 7 thread 0
----------------------------------------------------
return_value_nondet_int$0=3 (00000000 00000000 00000000 00000011)
State 18 file midpoint_cbmc.c function main line 7 thread 0
----------------------------------------------------
high=3 (00000000 00000000 00000000 00000011)
Assumption:
file midpoint_cbmc.c line 9 function main
0 <= low && low <= high
State 24 file midpoint_cbmc.c function main line 11 thread 0
----------------------------------------------------
low=1 (00000000 00000000 00000000 00000001)
State 25 file midpoint_cbmc.c function main line 11 thread 0
----------------------------------------------------
high=3 (00000000 00000000 00000000 00000011)
State 27 file midpoint_cbmc.c function midpoint line 3 thread 0
----------------------------------------------------
goto_symex$$return_value$$midpoint=1 (00000000 00000000 00000000 00000001)
State 29 file midpoint_cbmc.c function main line 11 thread 0
----------------------------------------------------
return_value_midpoint=1 (00000000 00000000 00000000 00000001)
State 30 file midpoint_cbmc.c function main line 11 thread 0
----------------------------------------------------
mid=1 (00000000 00000000 00000000 00000001)
Violated property:
file midpoint_cbmc.c function main line 15 thread 0
midpoint has the expected offset
mid - low == (high - low) / 2
** 2 of 5 failed (3 iterations)
VERIFICATION FAILED

从断言中可知一组失败的值为 low = 1; high = 1; mid = 0,此时有 low / 2 = 0; high / 2 = 0,二者之和为 0 ,而正确的较小中点为 1 。产生该结果的原因是计算整数除法时产生舍入误差。

任务4:比较软件测试与形式化验证#

  1. 任务 1 中 low = 0; high = 0; mid = 0 通过测试,而任务 2 中 low = 177393664; high = 1981235200; mid = -1068169216 产生了溢出,两组数据都满足 0≤low≤high0 \leq low \leq high 的条件。前一组输入通过只能说明程序在该用例上的正确性,不能排除其他输入可能产生的错误。

  2. 修正后的结果:

    Terminal window
    ❯ ./midpoint_test.out
    177393664 1981235200
    1079314432

    该条测试可以防止 low + high 产生有符号整数溢出的错误出现。

  3. 验证结论:

    • 适用的输入条件: 0≤low≤high0 \leq low \leq high
    • 使用的整数类型: int
    • 已经检查的溢出性质: low + high 产生有符号整数溢出
    • 功能性质:结果仍在原区间中;结果与 low 的距离等于区间长度的一半
    • 没有检查的输入与程序行为:没有检查整数类型为其他类型的行为;没有检查输入值超过 int 类型范围的情况
  4. 软件测试可以发现特定情形下的问题,软件验证覆盖更广泛,但相应实现难度更高。要根据具体场景选择使用。

实验1:从软件测试到形式化验证
https://blog.chiyo.uk/posts/2026/10/2522/
作者
千代有希=>
发布于
2026-10-25 22:50
许可协议
CC BY-NC-SA 4.0
千代有希的签名
Comment seems to stuck. Try to refresh?✨