为什么Linux程序derefrences(char *)0不总是段错误?

我正在testing旨在检测subprocess何时分离的代码。 想象一下,当这段代码不总是出现段错误时,我感到惊讶:

#include <stdio.h> int main() { char *p = (char *)(unsigned long)0; putchar(*p); return 0; } 

我在Debian Linux 2.6.26内核下运行; 我的shell是来自Debian ksh包的版本M 93s + 2008-01-31的AT&T ksh93 。 有时候这个程序会出现段错误,否则它会以一个非零的退出状态静静地终止,但是没有消息。 我的信号检测程序报告如下:

 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下运行表明,段错误也很罕见:

 Running... Running... Running... Running... Running... Running... Memory fault Running... 

有趣的是, bash每次都正确地检测到段错误

我有两个问题:

  1. 任何人都可以解释此行为?

  2. 任何人都可以提出一个简单的C程序,可以在每次执行时可靠的segfault? 我也试过kill(getpid(), SIGSEGV) ,但是我得到了类似的结果。


编辑: jbcreix有答案 :我的段故障检测器被打破。 我被骗了,因为ksh有同样的问题。 我试着用bashbash每次都正确。

我的错误是,我正在把WNOHANG传递给waitpid() ,在那里我应该传递零。 我不知道我能想什么 有人想知道ksh是怎么回事,但这是一个单独的问题。

写入 NULL将可靠地段错误或总线错误。

有时操作系统会将只读页面映射到零地址。 因此,你有时可以从NULL读取。

尽管C将NULL地址定义为特殊的,但是该特殊状态的“实现”实际上是由操作系统的虚拟存储器(VM)子系统处理的。

WINE和dosemu需要在NULL映射一个页面,以便兼容Windows。 请参阅Linux内核中的mmap_min_addr以重建不能执行此操作的内核。

mmap_min_addr目前是一个热门话题,因为对OpenBSD的Theo de Raadt的Linus(显然是Linux的名气)的一个相关攻击和一个公开的火焰。

如果你愿意这样编码孩子,你总是可以调用: 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。

我不确定为什么它没有一致的行为。 我认为阅读并不挑剔。 或者类似的东西,虽然我可能是完全错误的。

尝试写在NULL。 这似乎是一致的我。 我不知道你为什么要使用这个。 🙂

 int main() { *(int *)0 = 0xFFFFFFFF; return -1; } 

来自维基百科的第二个问题的答案:

  int main(void) { char *s = "hello world"; *s = 'H'; }