消除bug.d作者.
原地址
作者:W.B.(d作者)
我们要消除bug,否则其消耗你时间/金钱.你可以通过改进工具而少遭罪.
数组溢出
计算数组和:
#include <stdio.h>
#define MAX 10
int sumArray(int* p) {
int sum = 0;
int i;
for (i = 0; i <= MAX; ++i)
sum += p[i];
return sum;
}
int main() {
static int values[MAX] = { 7,10,58,62,93,100,8,17,77,17 };
printf("sum = %d\n", sumArray(values));
return 0;
}
应输出:
sum = 449
在我Ubuntu上,用gcc/clang加-Wall,你会得到漏洞.
for (i = 0; i <= MAX; ++i)
^^
应该边界问题,而应该是:
for (i = 0; i < MAX; ++i)
左闭右开.尽管有错误,却输出正确结果.但在别人系统上,又会犯错.这是个海森漏洞.我会担心时间/金钱等.多年来,我得出,不要包含上界,不要用<=,但是否一定该我来解决它呢?假设我是审查者,要确保sumArray代码正确.
要查看:1,查看调用者传递的指针.2,验证指针是否指向数组,3,验证大小是否为MAX.
尽管程序很普通,但没有扩展性,调用者越多,越间接,则越难在你脑袋中保证数据流是否正确.即使你对了,你能确保吗?其他人改了,你能确保吗?你是否要再分析一遍.这是个工具问题.
这个基本问题就是c数组作为参数降级为指针时,没有转义,也没有检测,至少gcc/clang没有检测.
d的betterC工具修复了它.d有只是个胖指针的动态数组,像这样:
struct DynamicArray {
T* ptr;
size_t length;
}
这样声明:
int[] a;
例子,就变成了:
import core.stdc.stdio;
extern (C): // use C ABI for declarations
enum MAX = 10;
int sumArray(int[] a) {
int sum = 0;
for (int i = 0; i <= MAX; ++i)
sum += a[i];
return sum;
}
int main() {
__gshared int[MAX] values = [ 7,10,58,62,93,100,8,17,77,17 ];
printf("sum = %d\n", sumArray(values));
return 0;
}
编译:
dmd -betterC sum.d
运行:
./sum
替换<=为<.得到:
./sum
sum = 449
这是由于动态数组带长度,然后编译器插入边界检查代码.还有更多.如烦人的:
for (int i = 0; i < a.length; ++i)
可改为:
foreach (value; a)
sum += value;
现在像这样:
int sumArray(int[] a) {
int sum = 0;
foreach (value; a)
sum += value;
return sum;
}
现在可以独立审核该代码.你可短时间干更多事,并提高可靠性.证明给你加薪是合理的,至少不会喊你回来修八阿哥.
反对!,传递a要两次压,而压p只压了一次,你说了不必付费..确实.但MAX是清单常数,其并未像下面传入:
int sumArray(int *p, size_t length);
但,我们再来探讨不付费.D允许你按引用传递参数,其中包括固定数组,所以.
int sumArray(ref int[MAX] a) {
int sum = 0;
foreach (value; a)
sum += value;
return sum;
}
这里的a作为引用参数,运行时,仅是指针,但是书写为数组[MAX],因而可检查边界.不必检查调用方,编译器的类型系统将验证确实传递的是正确大小的数组.
反对!,D支持指针,我不能写指针吗?为什么不能?你说了,这是机械保证.
是的,你可以这样写:
import core.stdc.stdio;
extern (C): // use C ABI for declarations
enum MAX = 10;
int sumArray(int* p) {
int sum = 0;
for (int i = 0; i <= MAX; ++i)
sum += p[i];
return sum;
}
int main() {
__gshared int[MAX] values = [ 7,10,58,62,93,100,8,17,77,17 ];
printf("sum = %d\n", sumArray(&values[0]));
return 0;
}
仍然编译,但可怕的漏洞仍存在.你得到:
sum = 39479
很奇怪.如何保证不发生这样的事呢?给代码加@安全.
import core.stdc.stdio;
extern (C): // use C ABI for declarations
enum MAX = 10;
@safe int sumArray(int* p) {
int sum = 0;
for (int i = 0; i <= MAX; ++i)
sum += p[i];
return sum;
}
int main() {
__gshared int[MAX] values = [ 7,10,58,62,93,100,8,17,77,17 ];
printf("sum = %d\n", sumArray(&values[0]));
return 0;
}
编译时,得到:
sum.d(10): Error: safe function 'sum.sumArray' cannot index pointer 'p'
你需要加@安全才能审核代码,但也就一步.
总之,传递参数时避免数组降级,或禁止指针算术带来的间接,能够消除该漏洞.相信很多人都被缓冲溢出折腾过,请继续关注.
浙公网安备 33010602011771号