Why does Linux program that derefrences (char*)0 not always segfault?

蹲街弑〆低调 提交于 2019-12-09 13:01:43

问题


I'm testing code that is designed to detect when a child process has segfaulted. Imagine my surprised when this code does not always segfault:

#include <stdio.h>

int main() {
  char *p = (char *)(unsigned long)0;
  putchar(*p);
  return 0;
}

I'm running under a Debian Linux 2.6.26 kernel; my shell is the AT&T ksh93 from the Debian ksh package, Version M 93s+ 2008-01-31. Sometimes this program segfault but otherwise it simply terminates silently with a nonzero exit status but no message. My signal-detecting program reports the following:

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

Running under pure ksh shows that the segfault is also rare:

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

Interestingly, bash correctly detects the segfault every time.

I have two questions:

  1. Can anyone explain this behavior?

  2. Can anyone suggest a simple C program that will segfault reliably on every execution? I have also triedkill(getpid(), SIGSEGV), but I get similar results.


EDIT: jbcreix has the answer: my segfault detector was broken. I was fooled because ksh has the same problem. I tried with bash and bash gets it right every time.

My error was that I was passing WNOHANG to waitpid(), where I should have been passing zero. I don't know what I could have been thinking! One wonders what is the matter with ksh, but that's a separate question.


回答1:


Writing to NULL will reliably segfault or bus error.

Sometimes an OS will map a read-only page to the zero address. Thus, you can sometimes read from NULL.

Although C defines the NULL address as special, the 'implementation' of that special status is actually handled by the Operating System's Virtual Memory (VM) subsystem.

WINE and dosemu need to map a page at NULL for Windows compatibility. See mmap_min_addr in the Linux kernel to rebuild a kernel which cannot do this.

mmap_min_addr is currently a hot topic due to a related exploit and a public flame toward Linus (of Linux fame, obviously) from Theo de Raadt, of the OpenBSD effort.

If you are willing to code the child this way, you could always call: raise(SIGSEGV);

Also, you can obtain a guaranteed-to-segfault pointer from: int *ptr_segv = mmap(NULL, PAGE_SIZE, PROT_NONE, MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS, -1, 0);

Where PROT_NONE is the key to reserving memory which cannot be accessed. For 32-bit Intel Linux, PAGE_SIZE is 4096.




回答2:


I'm not sure why it doesn't have consistent behavior. I'd think that it's not as nit-picky with reading. Or something like that, though I'd probably be totally wrong.

Try writing at NULL. This seems to be consistent for me. I have no idea why you'd want to use this though. :)

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



回答3:


The answer to question number two from Wikipedia :

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


来源:https://stackoverflow.com/questions/1735349/why-does-linux-program-that-derefrences-char0-not-always-segfault

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!