消除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没有检测.
dbetterC工具修复了它.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'

你需要加@安全才能审核代码,但也就一步.
总之,传递参数时避免数组降级,或禁止指针算术带来的间接,能够消除该漏洞.相信很多人都被缓冲溢出折腾过,请继续关注.

posted @   zjh6  阅读(7)  评论(0编辑  收藏  举报  
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示