消除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'
你需要加@安全
才能审核代码
,但也就一步.
总之,传递参数时避免数组降级,或禁止指针算术带来的间接
,能够消除该漏洞
.相信很多人都被缓冲溢出
折腾过,请继续关注.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现