最近,我写了以下,buggy,c代码:
#include <stdio.h>
struct IpAddr {
unsigned char a, b, c, d;
};
struct IpAddr ipv4_from_str (const char * str) {
struct IpAddr res = {0};
sscanf(str, "%d.%d.%d.%d", &res.a, &res.b, &res.c, &res.d);
return res;
}
int main (int argc, char ** argv) {
struct IpAddr ip = ipv4_from_str("192.168.1.1");
printf("Read in ip: %d.%d.%d.%d\n", ip.a, ip.b, ip.c, ip.d);
return 0;
}
错误在于我在sscanf
中使用了%d
,同时提供了指向1字节宽无符号字符的指针.%d
接受一个4字节宽的int指针,这个差异会导致越界写入.越界写入肯定是错误的,程序会崩溃.
我的困惑在于这个错误的非恒定性.在超过before0次运行时,程序SEGbefore在50%的时间内对打印语句进行故障诊断,SEGafter在另外50%的时间内对语句进行故障诊断.我不明白为什么这会改变.程序的两次调用之间有什么区别?我的印象是堆栈的内存布局是一致的,我编写的小测试程序似乎证实了这一点.不是吗?
我使用的是gcc v11.Debian bookworm上的3.0,内核5.14.16-1,我编译时没有设置任何标志.
Here是我的编译器的汇编输出,仅供参考.