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

时间:2009-11-14 19:35:27

标签: c linux segmentation-fault waitpid

我正在测试用于检测子进程何时进行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每次都能正确检测到段错误

我有两个问题:

  1. 任何人都可以解释这种行为吗?

  2. 有人能建议一个简单的C程序,它会在每次执行时可靠地进行段错误吗?我也试过了kill(getpid(), SIGSEGV),但我得到了类似的结果。


  3. 编辑: jbcreix有答案:我的segfault探测器坏了。我被骗了,因为ksh有同样的问题。我尝试使用bashbash每次都正确。

    我的错误是我将WNOHANG传递给waitpid(),我本应该传递零。我不知道我能想到什么!人们想知道ksh的问题是什么,但这是一个单独的问题。

3 个答案:

答案 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';
 }