I am using mprotect to set some memory pages as write protected. When any writing is tried in that memory region, the program gets a SIGSEGV signal. From the signal handler I know in which memory address the write was tried, but I don't know the way how to find out which instruction causes write protection violation. So inside the signal handler I am thinking of reading the program counter(PC) register to get the faulty instruction. Is there a easy way to do this?
If you install your signal handler using sigaction with the SA_SIGINFO flag, the third argument to the signal handler has type void * but points to a structure of type ucontext_t, which in turn contains a structure of type mcontext_t. The contents of mcontext_t are implementation-defined and generally cpu-architecture-specific, but this is where you will find the saved program counter.
It's also possible that the compiler's builtins (__builtin_return_address with a nonzero argument, I think) along with unwinding tables may be able to trace across the signal handler. While this is in some ways more general (it's not visibly cpu-arch-specific), I think it's also more fragile, and whether it actually works may be cpu-arch- and ABI-specific.
Related
i am currently writing a small VM in C/C++. Obviously i can't let the whole VM crash if the user dereferences a null pointer so i have to check every access which is becoming cumbersome as the VM grows and more systems are implemented.
So i had an idea: write a signal handler for sigsegv and let the OS do its thing but instead of closing the program call the VM exception handler.
It seems to work (with my very simple test cases), but i didn't find anything guaranteeing a Sigsegv being thrown on null-derefs nor the handler being called for OS generated signals.
So my question is:
Can i count on signal.h on modern destkop OSes (i don't really care if it's not standard on doesn't work on something other than linux/win: it's a pet project). Are there any non trivial stuff i should be aware of (obscure limitations of signal(...) or longjmp(...) ?)
Thank you !
Here is the pseudo implementation:
/* ... */
jmp_buf env;
/* ... */
void handler(int) {
longjmp(env, VM_NULLPTR);
}
/* ... */
if(setjmp(env)) {
return vm_throw("NullPtrException");
}
switch(opcode) {
/* instructions */
case INVOKE:
*stack_top = vm_call(stack_top->obj); // don't check anything in the case where stack_top or stack_top->obj is null handler() will be called an a "NullPtrException" will be thrown
break;
/* more instructions */
}
/* ... */
Note : i only need to check nulls, garbage (dangling) pointers are handled by the GC and should not happen.
Calling longjmp() from a signal handler is only safe if the signal handler is never invoked from async signal unsafe code. So for example if you might receive SIGSEGV by passing a bad pointer to any of the printf() family of functions, you cannot longjmp() from your signal handler.
Can i count on signal.h on modern destkop OSes
You can count on it in the sense that the header, and the functions within will be available on all standard compliant systems. However, exactly what what signals are thrown and when is not consistent across OSes.
On windows, you may need to compile your program with cygwin or similar environment to get the system to raise a segmentation fault. Programs compiled with visual studio use "structured exceptions" to handle invalid storage access.
Is signal.h a reliable way to catch null pointers?
There are situations where null pointer dereference does not result in the raising a segmentation fault signal, even on a POSIX system.
One case might be that the compiler has optimized the operation away, which is typical for example in the case of dereferencing a null pointer to call a member function that does not access any data members. When there is no invalid memory access, there is no signal either. Of course, in that case there won't be a crash either.
Another case might be that address 0 is in fact valid. That's the case on AIX, which you don't care about. It is also the case on Linux, which you do care about, but not by default and you might choose to not care about the case where it is. See this answer for more details.
Then there is your implementation of the signal handler. longjmp is not async signal safe, so if the signal was raised while another non-safe operation was being performed, the interrupted operation may have left your program in an inconsistent state. See the answer by John Zwinck and the libc documentation for details.
Is there any way to convert a NULL pointer access into a C++ exception under Linux ? Something similar to the NullPointerException in Java. I hope the following program would return successfully, instead of crash (assume the compiler cannot figure out this NULL pointer access during compile time):
class NullPointerException {};
void accessNullPointer(char* ptr) {
*ptr = 0;
}
int main() {
try {
accessNullPointer(0);
} catch (NullPointerException&) {
return 1;
}
return 0;
}
I'm not expecting any standard way of doing it, since NULL pointer access under C++ is undefined-behavior, just want to know how to get it done under x86_64 Linux/GCC.
I did some very primitive research in this, it might be possible:
When a NULL pointer is access under Linux, a SIGSEGV will be generated.
Inside the SIGSEGV handler, the program's memory and register information will be available (if sigaction() is used to register the signal handler). The instruction which caused the SIGSEGV is also available if the program is disassembled.
Modify the program's memory and/or register, and create/fake an exception instance (maybe by invoking the low level unwind library functions, like _Unwind_RaiseException, etc.)
Finally return from the signal handler, hope the program would start a C++ stack unwinding process like a normal exception was thrown.
Here's a quote from GCC's man page (-fnon-call-exceptions):
Generate code that allows trapping instructions to throw exceptions. Note that this requires platform-specific runtime support that does not exist everywhere. Moreover, it only allows trapping instructions to throw exceptions, i.e. memory references or floating point instructions. It does not allow exceptions to be
thrown from arbitrary signal handlers such as "SIGALRM".
It seems this "platform-specific runtime" is exactly what I want. Anyone knows such a runtime for Linux/x86_64 ? Or give me some information on how to implement such a runtime if no such runtime already exists ?
I want the solution to work in multi-threaded program as well.
No, there's no good way to do that, and there shouldn't be. Exceptions are thrown by a throw statement in source code. That's important for reasoning about exception safety: you can look at the code and see the places where exceptions can be thrown and, perhaps more important, you can look a the code and see the places where exceptions will not be thrown. If pretty much anything you do can throw an exception it becomes very difficult to write exception-safe code without cluttering it with catch clauses. Microsoft tried this in their early C++ compilers: they piggybacked C++ exception handling on top of their OS's structured exceptions, and the result was a disaster.
Register an alternative signal stack with signalaltstack().
The 3rd argument to a signal handler handler registered with the SA_SIGINFO is a pointer to a ucontext_t which contains the saved register. The signal handler should adjust this to simulate a call to a function. That function can then throw the exception.
Potential complications include the need to preserve the value of callee saved registers, the red-zone on x86-64 (which can be disabled) and the return address register on some ISAs, such as ARM.
Let's say I compiled using gcc --stack,4194304
Next in my program I do something like char what_is_wrong_with_me[8000000];
This will result in a segmentation fault, but the weird thing is I have a working segv_handler, where if I do something stupid like char *stupid=0; *stupid='x'; it will print an error message.
My question is, how do I handle the out of stack space segfault as well?
You can handle this but you've exhausted your primary stack. You need to set an alternate stack for your signal handler. You can do this with a sigaltstack syscall
When installing your segfault handler with sigaction, you'll also need the SA_ONSTACK option
So, your process exhausted its allocated stack space (intentionally, in your case, but it doesn't matter whether it's intentional or not). As soon as an attempt is made to write the next stack frame into the unallocated page occurs, a SIGSEGV signal gets sent to the process.
An attempt is then made to invoke your installed signal handler. Now, lets remember that SIGSEGV is just like any other signal. And, as you know, when a signal handler gets invoked upon receipt of a signal, when the signal handler returns the process continues to execute.
In other words, the signal handler gets invoked as if it were a function call, and when the function call returns, the original execution thread resumes running.
Of course, you know what needs to happen for a function call, right? A new call frame gets pushed on the stack, containing the return address, and a few other things. You know, so when the function call returns, the original execution thread resumes running where it left off (in case of a signal you also get an entire register dump in there, and the rest of the CPU state, but that's an irrelevant detail).
And now, perhaps, you can figure out, all by yourself, the answer to your own question, why your signal handler does not get invoked in this situation, when the stack space has been exhausted...
My program needs to perform read-only access to the contents of a vector<string> in a signal handler for SIGINT. (The alternative is to use a fixed-size array of fixed-length C strings.) The program is designed to run in a POSIX environment.
Are vector::operator[] and vector::size() asynchronous-safe (or signal-safe)?
No, it's not safe. C++11 1.9/6:
When the processing of the abstract machine is interrupted by receipt of a signal, the values of objects which
are neither
of type volatile std::sig_atomic_t nor
lock-free atomic objects (29.4)
are unspecified during the execution of the signal handler, and the value of any object not in either of these
two categories that is modified by the handler becomes undefined.
Angew's answer is correct considering C++. Now that the question mentions POSIX environment, which could provide stronger guarantees, this needs another answer, which is:
If the process is multi-threaded, or if the process is single-threaded and a signal handler is executed other than as the result of:
The process calling abort(), raise(), kill(), pthread_kill(), or sigqueue() to generate a signal that is not blocked
A pending signal being unblocked and being delivered before the call that unblocked it returns
the behavior is undefined if the signal handler refers to any object other than errno with static storage duration other than by assigning a value to an object declared as volatile sig_atomic_t, or if the signal handler calls any function defined in this standard other than one of the functions listed in the following table.
Source: The Open Group Base Specifications Issue 7
IEEE Std 1003.1, 2013 Edition, 2.4.3
This is... still a very weak guarantee. As far as I can understand this:
vector::operator[] is not safe. Fixed arrays are not safe. Access to fixed arrays is safe if the array is non-static.
Why? vector::operator[] doesn't specify exactly how it should be implemented, only the preconditions and postconditions. The access to elements of an array is possible (if the array is non-static), this implies that the access to vector elements is also safe if you create a pointer (with vec.data() or &vec[0]) before signalling, and then accessing the elements through the pointer.
EDIT: Originally I missed that because I wasn't aware of the sigaction function - with signal you could only access your local arrays in the signal handler, but with sigaction you can provide pointers to automatic and dynamically arrays. The advice with doing as little as possible in signal handlers still applies here though.
Bottom line: You're doing too much in your signal handlers. Try doing as little as possible. One approach is to assign to a flag (of type volatile sig_atomic_t), and return. The code can later check if the flag was triggered (e.g. in an event loop)
I believe that if you know the reason that access to a vector is not safe then you can work around it. Note that access still isn't guaranteed safe. But it will work on anything that isn't a Death Station 9000.
A signal handler interrupts the execution of the program much like a interrupt handler would if you were programming directly to the hardware. The operating system simply stops executing your program, wherever it happens to be. This might be in the middle of anything. For example, if your vector has elements being added to it and it is updating its size value or it is copying the contents to a new, longer vector, that might be interrupted by the signal. And then your signal handler would try to read from the vector resulting in disaster.
You can access the vector from the signal handler as long as it is effectively constant. If you set up the whole thing at program start and then never write to it again, it is safe to use. Note, not safe to use according to the standards documents, but effectively safe.
This is a lot like multi-threading on a single-core CPU.
Now, if you do need to update the vector while the program is running you need to "lock" the signal handler away from it by masking the signal or disabling the handler before updating the vector, to ensure that the handler won't run while the vector is in an inconsistent state.
I have a C++ application cross-compiled for Linux running on an ARM CortexA9 processor which is crashing with a SIGFPE/Arithmetic exception. Initially I thought that it's because of some optimizations introduced by the -O3 flag of gcc but then I built it in debug mode and it still crashes.
I debugged the application with gdb which catches the exception but unfortunately the operation triggering exception seems to also trash the stack so I cannot get any detailed information about the place in my code which causes that to happen. The only detail I could finally get was the operation triggering the exception(from the following piece of stack trace):
3 raise() 0x402720ac
2 __aeabi_uldivmod() 0x400bb0b8
1 __divsi3() 0x400b9880
The __aeabi_uldivmod() is performing an unsigned long long division and reminder so I tried the brute force approach and searched my code for places that might use that operation but without much success as it proved to be a daunting task. Also I tried to check for potential divisions by zero but again the code base it's pretty large and checking every division operation it's a cumbersome and somewhat dumb approach. So there must be a smarter way to figure out what's happening.
Are there any techniques to track down the causes of such exceptions when the debugger cannot do much to help?
UPDATE: After crunching on hex numbers, dumping memory and doing stack forensics(thanks Crashworks) I came across this gem in the ARM Compiler documentation(even though I'm not using the ARM Ltd. compiler):
Integer division-by-zero errors can be trapped and identified by
re-implementing the appropriate C library helper functions. The
default behavior when division by zero occurs is that when the signal
function is used, or
__rt_raise() or __aeabi_idiv0() are re-implemented, __aeabi_idiv0() is
called. Otherwise, the division function returns zero.
__aeabi_idiv0() raises SIGFPE with an additional argument, DIVBYZERO.
So I put a breakpoint at __aeabi_idiv0(_aeabi_ldiv0) et Voila!, I had my complete stack trace before being completely trashed. Thanks everybody for their very informative answers!
Disclaimer: the "winning" answer was chosen solely and subjectively taking into account the weight of its suggestions into my debugging efforts, because more than one was informative and really helpful.
My first suggestion would be to open a memory window looking at the region around your stack pointer, and go digging through it to see if you can find uncorrupted stack frames nearby that might give you a clue as to where the crash was. Usually stack-trashes only burn a couple of the stack frames, so if you look upwards a few hundred bytes, you can get past the damaged area and get a general sense of where the code was. You can even look down the stack, on the assumption that the dead function might have called some other function before it died, and thus there might be an old frame still in memory pointing back at the current IP.
In the comments, I linked some presentation slides that illustrate the technique on a PowerPC — look at around #73-86 for a case study in a similar botched-stack crash. Obviously your ARM's stack frames will be laid out differently, but the general principle holds.
(Using the basic idea from Fedor Skrynnikov, but with compiler help instead)
Compile your code with -pg. This will insert calls to mcount and mcountleave() in every function. Do not link against the GCC profiling lib, but provide your own. The only thing you want to do in your mcount and mcountleave() is to keep a copy of the current stack, so just copy the top 128 bytes or so of the stack to a fixed buffer. Both the stack and the buffer will be in cache all the time so it's fairly cheap.
You can implement special guards in functions that can cause the exception. Guard is a simple class, in constractor of this class you put the name of the file and line (_FILE_, _LINE_) into file/array/whatever. The main condition is that this storage should be the same for all instances of this class(kind of stack). In the destructor you remove this line. To make it works you need to put the creation of this guard on the first line of each function and to create it only on stack. When you will be out of current block deconstructor will be called. So in the moment of your exception you will know from this improvised callstack which function is causing a problem.
Ofcaurse you may put creation of this class under debug condition
Enable generation of core files, and open the core file with the debuger
Since it uses raise() to raise the exception, I would expect that signal() should be able to catch it. Is this not the case?
Alternatively, you can set a conditional breakpoint at __aeabi_uldivmod to break when divisor (r1) is 0.