Bruno Haible <bruno <at> clisp.org> writes: > > Eric Blake wrote: > > user_context->uc_stack.ss_sp contains the location > > of the currently running (alternate) stack, not the stack where the fault > > occurred. ... find out where the _original_ stack ended
I'm not sure if it is a bug or intentional that Linux sets ucontext_t.uc_link to NULL in the alternate stack. But in POSIX 2001, getcontext() states that when uc_link is NULL, the thread must exit when the context returns; and in the case of a sigsegv stack-overflow handler, returning from the signal (rather than longjmp'ing) is undefined behavior (besides, if you COULD return normally, you would just reprovoke the stack overflow). What's worse, POSIX 200x is withdrawing <ucontext.h> (its types should now reside in <signal.h>), as well as removing requirements for getcontext() and friends, so trying to rely on getcontext is a losing battle. On the other hand, POSIX states for sigaction() that the handler's "third argument can be cast to a pointer to an object of type ucontext_t to refer to the receiving thread’s context that was interrupted when the signal was delivered." The receiving thread may currently be in the alternate stack, but the signal was delivered when the thread was still in the primary stack. So maybe we should file this as a bug with the kernel folks, and hope that a future version of Linux change uc_stack to supply the information we want. After all, we don't need uc_stack to learn about the current (alternate) stack; we can use sigaltstack() for that. > File platforms uses > > stackvma-linux.c Linux /proc/self/maps, mincore() > stackvma-freebsd.c FreeBSD /proc/curproc/map, mincore() > stackvma-mincore.c OpenBSD, NetBSD mincore() > stackvma-mach.c MacOS X vm_region() > stackvma-procfs.c IRIX, OSF/1, Solaris ioctl(PIOCMAP) > stackvma-beos.c BeOS get_next_area_info() Cygwin provides /proc/self/maps (but not yet mincore()). Plus, you can probably exploit the Windows API for both mingw and cygwin (perhaps in the same way that cygwin implements /proc/self/maps). > > Would help to move this code to gnulib? Are there other uses of the stack VMA? Perhaps so. At any rate, the current implementation of c-stack precludes any detection of non-stack overflow segv's on Linux (at least for the kernel versions I was testing), because it claims ALL segvs are stack overflow, whether or not that is true. I don't know if any of the other OS's that support sigaltstack and ucontext_t and which do better at providing info about the main stack. M4 has a slightly better approach - when registering the overflow handler, probe the current stack location and getrlimit for the maximum stack size, then on fault, see if the fault lies near the pre-computed stack limit. It gets tricky on architectures with limited memory where stack and heap can collide, but these days, 16-bit memory spaces are not gnulib's primary porting target, and most OSs enforce large gaps between the virtual address ranges assigned to stack vs. heap. But m4's approach isn't quite right, either - on Linux, the current stack location + getrlimit information when the overflow handler is registered fails to account for stack previously occupied prior to calling the handler, and fails to account for the fact that the size of the memory mapped to the stack at the time of registration is different than the size of the map at the time of the fault. Thus, the handler often thinks that the faulting address falls outside the window it computed as the potential end of stack at registration time. > (Conservative garbage-collectors and Scheme interpreters only need to know > the current stack pointer and the stack top, AFAIK, not the stack bottom.) Just so I'm clear: By stack top, you mean the area reserved by main(), and by stack bottom, you mean the area near where the fault points on a stack overflow? When sigaction works, c-stack already has access to the faulting address and the stack direction. If we assume the fault was due to stack overflow, then we should also be able to assume that an address from the next page over, going the opposite direction from stack growth, is mapped, along with all other addresses in the primary stack. So c-stack could use a mincore()-like capability to quickly determine whether all memory from the page near the fault over to the stack location saved at the time the handler was registered is currently mapped (whether or not all of those pages are in core) (and using /proc/self/maps when that is faster than mincore()). -- Eric Blake