我正在测试用于检测子进程何时进行segfaulted的代码。想象一下,当这段代码并不总是出现错误时,我感到很惊讶:
#include <stdio.h>
int main() {
char *p = (char *)(unsigned long)0;
putchar(*p);
return 0;
}
我在Debian Linux 2.6.26内核下运行;我的shell是来自Debian ksh93
包,版本M 93s + 2008-01-31的AT&amp; T ksh
。有时这个程序会出现段错误,但除此之外它只是以非零退出状态静默终止但没有消息。我的信号检测程序报告如下:
segfault terminated by signal 11: Segmentation fault
segfault terminated by signal 53: Real-time signal 19
segfault terminated by signal 11: Segmentation fault
segfault terminated by signal 53: Real-time signal 19
segfault terminated by signal 53: Real-time signal 19
segfault terminated by signal 53: Real-time signal 19
segfault terminated by signal 53: Real-time signal 19
在纯ksh
下运行表明segfault也很少见:
Running...
Running...
Running...
Running...
Running...
Running... Memory fault
Running...
有趣的是, bash
每次都能正确检测到段错误。
我有两个问题:
任何人都可以解释这种行为吗?
有人能建议一个简单的C程序,它会在每次执行时可靠地进行段错误吗?我也试过了kill(getpid(), SIGSEGV)
,但我得到了类似的结果。
编辑: jbcreix有答案:我的segfault探测器坏了。我被骗了,因为ksh
有同样的问题。我尝试使用bash
和bash
每次都正确。
我的错误是我将WNOHANG
传递给waitpid()
,我本应该传递零。我不知道我能想到什么!人们想知道ksh
的问题是什么,但这是一个单独的问题。
答案 0 :(得分:12)
将写入NULL
将可靠地发生段错误或总线错误。
有时,操作系统会将只读页面映射到零地址。因此,您有时可以阅读NULL
。
虽然C将NULL
地址定义为特殊,但该特殊状态的“实现”实际上由操作系统的虚拟内存(VM)子系统处理。
WINE和dosemu需要在NULL
处映射页面以获得Windows兼容性。请参阅Linux内核中的mmap_min_addr
以重建无法执行此操作的内核。
mmap_min_addr
目前是一个热门话题,因为相关的漏洞利用以及公开的火焰来自Theo de Raadt的Linus(显然是Linux成名),OpenBSD的努力。
如果您愿意以这种方式为孩子编码,您可以随时致电:raise(SIGSEGV);
此外,您可以从以下位置获取保证到段错误的指针:
int *ptr_segv = mmap(NULL, PAGE_SIZE, PROT_NONE, MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS, -1, 0);
其中PROT_NONE
是保留无法访问的内存的关键。对于32位Intel Linux,PAGE_SIZE为4096。
答案 1 :(得分:1)
我不确定为什么它没有一致的行为。我认为这并不像读书那样挑剔。或类似的东西,虽然我可能完全错了。
尝试写入NULL。这对我来说似乎是一致的。我不知道为什么你想要使用它。 :)
int main()
{
*(int *)0 = 0xFFFFFFFF;
return -1;
}
答案 2 :(得分:1)
来自Wikipedia的第二个问题的答案:
int main(void)
{
char *s = "hello world";
*s = 'H';
}