是什么导致这个错误是不确定的
What causes this bug to be non-deterministic
最近写了如下,bug,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指针,不同导致越界写入。越界写肯定是ub,程序会崩溃
我的困惑在于错误的非恒定性。超过 1,000 次运行,程序 50% 的时间在打印语句 之前 出现段错误,另外 50% 的时间在 之后 语句出现段错误。我不明白为什么这会改变。程序的两次调用有什么区别?我的印象是堆栈的内存布局是一致的,我编写的小测试程序似乎证实了这一点。不是这样吗?
我在 Debian 书虫、内核 5.14.16-1 上使用 gcc v11.3.0,并且我编译时没有设置任何标志。
Here是我编译器的汇编输出,供参考。
未定义的行为意味着任何事情都可能发生,甚至是不一致的结果。
实际上,这种不一致很可能是由于 Address Space Layout Randomization。根据数据在内存中的位置,out-of-bounds 访问可能会或可能不会访问未分配的内存或覆盖关键指针。
另见 Why don't I get a segmentation fault when I write beyond the end of an array?
最近写了如下,bug,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指针,不同导致越界写入。越界写肯定是ub,程序会崩溃
我的困惑在于错误的非恒定性。超过 1,000 次运行,程序 50% 的时间在打印语句 之前 出现段错误,另外 50% 的时间在 之后 语句出现段错误。我不明白为什么这会改变。程序的两次调用有什么区别?我的印象是堆栈的内存布局是一致的,我编写的小测试程序似乎证实了这一点。不是这样吗?
我在 Debian 书虫、内核 5.14.16-1 上使用 gcc v11.3.0,并且我编译时没有设置任何标志。
Here是我编译器的汇编输出,供参考。
未定义的行为意味着任何事情都可能发生,甚至是不一致的结果。
实际上,这种不一致很可能是由于 Address Space Layout Randomization。根据数据在内存中的位置,out-of-bounds 访问可能会或可能不会访问未分配的内存或覆盖关键指针。
另见 Why don't I get a segmentation fault when I write beyond the end of an array?