16 comments

[ 3.7 ms ] story [ 76.2 ms ] thread
(comment deleted)
It can be done easier with sigsetjmp, siglongjmp.
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.
And if you're on Linux, you can read the memory directly with process_vm_readv.
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.
Leaving this here as comments on that blog seem broken. TL;DR: access(2) is a bit more lightweight than write(2) for this purpose.

Consider 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/

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:

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...

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.
That's actually a really neat idea. I had no idea Dolphin did that.
This is how OpenJDK optimizes out some null checks as well.