Also you can check it with the `write` syscall (just write a byte pointed by address to /dev/null). If a pointer is invalid, it will return setting errno to EFAULT.
Neat trick. I suppose you could also write the bytes to a self-pipe, avoiding the race the article mentions between ensuring the bytes are readable and actually reading them.
The link posted by oomo contains (in French) an explanation about why it just works and is clean, general and guaranteed:
from the page "les syscall ne peuvent pas segfault
[dek\] Donc tous ceux qui acceptent une adresse
[dek\] Sont obligées de checker qu’elles est valide
[dek\] Et retournent EFAULT en cas d’erreur"
In English: "syscalls cannot segfault, so all that accept an address (as parameter) have to check if it is valid and return EFAULT in case of error."
(Since C code actually calls libc not system calls directly, this rationale fails on calls that do something on pointers before calling the actual kernel system call.)
Also, given the time cost of making a system call, one has to has an approach finer than "pay a system call for each byte to write" if there are many.
Anyway, TL;DR : it's not a dirty hack, it has some general validity!
Another place where SIGSEGV handlers are common: showing backtraces and "how to report a bug" messages. For example here's where it's done in the Ruby VM:
They have a bug report handler that shows a bunch of helpful information:
- the Ruby-level backtrace
- the stack of VM control frames (an internal data structure)
- the C-level backtrace (which Ruby implementation functions were running)
- which files had been required and loaded into memory
- what segments of memory are mapped and what aren't
Another thing: it's designed to work even when the program ran out of memory!
The sigaltstack(2) system call lets you preallocate a buffer that the kernel knows how to set up before it runs your SIGSEGV handler, so you can have just enough stack memory to compute and print all this debug info before the program ultimately crashes. Here's that code, again in the Ruby VM:
In college, my database professor once told us about an object-oriented database that handled SIGSEGVs and created objects on the fly that were supposed to exist but didn’t (exact details are fuzzy)
The "fastmem" technique used by some emulators (Dolphin, for one) also uses a SIGSEGV handler to emulate the target machine's MMU using the host's MMU.
16 comments
[ 3.7 ms ] story [ 76.2 ms ] threadConsider the following (from memory, untested):
word_t peek(word_t addr, int success) { word_t ret; errno = 0; access(addr, F_OK); if (success) success = (errno != EFAULT); if (errno != EFAULT) ret = *addr; return ret; }
I first learnt about this trick in http://www.gcu-squad.org/2005/08/non-ce-n-est-pas-sale%21/
In English: "syscalls cannot segfault, so all that accept an address (as parameter) have to check if it is valid and return EFAULT in case of error."
(Since C code actually calls libc not system calls directly, this rationale fails on calls that do something on pointers before calling the actual kernel system call.)
Also, given the time cost of making a system call, one has to has an approach finer than "pay a system call for each byte to write" if there are many.
Anyway, TL;DR : it's not a dirty hack, it has some general validity!
https://github.com/ruby/ruby/blob/5445e0435260b449decf2ac16f...
They have a bug report handler that shows a bunch of helpful information:
- the Ruby-level backtrace
- the stack of VM control frames (an internal data structure)
- the C-level backtrace (which Ruby implementation functions were running)
- which files had been required and loaded into memory
- what segments of memory are mapped and what aren't
Another thing: it's designed to work even when the program ran out of memory!
The sigaltstack(2) system call lets you preallocate a buffer that the kernel knows how to set up before it runs your SIGSEGV handler, so you can have just enough stack memory to compute and print all this debug info before the program ultimately crashes. Here's that code, again in the Ruby VM:
https://github.com/ruby/ruby/blob/5445e0435260b449decf2ac16f...