Reputation: 53874
When debugging the next code snippet, the debugger returns to the line x=10/x
with x=5
and still gets a SIGFPE
.
#include <signal.h>
volatile sig_atomic_t x = 0;
void sigfpe_handler(int signum) {
x = 5;
// Notice that there is no exit()
}
int main() {
signal(SIGFPE, sigfpe_handler);
x = 10 / x;
return 0;
}
Upvotes: 0
Views: 105
Reputation: 121357
As noted already, this is technically undefined behaviour.
What happens is that the SIGFPE
occurs repeatedly. If you add a print statement from the signal handler, you could see:
#include <signal.h>
volatile sig_atomic_t x = 0;
void sigfpe_handler(int signum) {
x = 5;
write(2, "Received SIGFPE\n", sizeof "Received SIGFPE\n" - 1 );
}
int main() {
signal(SIGFPE, sigfpe_handler);
x = 10 / x;
return 0;
}
This is because when a signal occurs, the control is transferred to the signal handler and once it's returned, the "main" code resumes execution from the instruction (not the statement) the execution was interrupted (by the signal). In this case, it continues to do the division and SIGFPE
occurs and the cycle goes on.
You can see the exact instruction, it repeats from gdb:
Program received signal SIGFPE, Arithmetic exception.
0x00000000004005a6 in main ()
(gdb) disass
Dump of assembler code for function main:
0x000000000040057c <+0>: push %rbp
0x000000000040057d <+1>: mov %rsp,%rbp
0x0000000000400580 <+4>: sub $0x10,%rsp
0x0000000000400584 <+8>: mov $0x40054c,%esi
0x0000000000400589 <+13>: mov $0x8,%edi
0x000000000040058e <+18>: callq 0x400430 <signal@plt>
0x0000000000400593 <+23>: mov 0x20042b(%rip),%edx # 0x6009c4 <x>
0x0000000000400599 <+29>: mov %edx,-0x4(%rbp)
0x000000000040059c <+32>: mov $0xa,%eax
0x00000000004005a1 <+37>: mov %eax,%edx
0x00000000004005a3 <+39>: sar $0x1f,%edx
=> 0x00000000004005a6 <+42>: idivl -0x4(%rbp)
0x00000000004005a9 <+45>: mov %eax,0x200415(%rip) # 0x6009c4 <x>
0x00000000004005af <+51>: mov $0x0,%eax
(gdb) p $rbp
$8 = (void *) 0x7fffffffada0
(gdb) p *(int*)$rbp
$9 = 0
If you notice, the value of x
was already loaded into a register and that's what it uses. That's why the change to x
doesn't have the effect you're expecting.
You could do a "jump" and then skip over the FPE causing code:
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <setjmp.h>
sigjmp_buf jbuf;
volatile sig_atomic_t x = 0;
void sigfpe_handler(int signum) {
x = 5;
write(2, "Received SIGFPE\n", sizeof "Received SIGFPE\n" - 1 );
siglongjmp(jbuf, 1);
}
int main() {
signal(SIGFPE, sigfpe_handler);
if (sigsetjmp(jbuf, 0) == 0)
x = 10 / x;
else
printf("Returned from siglongjmp\n");
return 0;
}
Please read the manual about sigsetjmp/siglongjmp for the caveats.
Upvotes: 1
Reputation: 52344
You install a signal handler for SIGFPE
that returns normally, and then do something that causes your system to generate that signal.
From the POSIX spec:
The behavior of a process is undefined after it returns normally from a signal-catching function for a SIGBUS, SIGFPE, SIGILL, or SIGSEGV signal that was not generated by kill(), sigqueue(), or raise().
There's similar wording in the C standard. So what happens next is not something you can control because it's undefined behavior. Your program might continue on, might enter an infinite loop, etc.
Also, your x
variable needs to be of type volatile sig_atomic_t
if you want changes made to it in the handler to be reliably and portably seen outside the handler (assuming said handler doesn't otherwise cause undefined behavior like it does in this example). C11 lock-free atomic types are okay too.
Upvotes: 0