bale bale - 2 months ago 18
C Question

Why does initializing a sigint variable with memset() help prevent Seg Faults?

BACKGROUND:

struct sigaction is used to set up the handlers for a small C program that handles interrupts. The issue arose when I received a "Segmentation fault (core dumped)" error message after completing implementation of the second handler. I set up both handlers exactly the same way. The first interrupt handler worked fine without initializing the sigaction struct, however, I received the seg fault error when I finished implementing the second handler.

QUESTION:

Why does initializing the sigaction struct with memset() help fix the error?

CODE:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <signal.h>
#include <string.h>

#define ALARMSECONDS 3
int numSigints = 5;

/* Handler function that prints out the current time on a SIGALRM interrupt. */
static void hdl(int signum)
{
time_t currTime;
time(&currTime);
printf("\ncurrent time is %s", ctime(&currTime));
alarm(ALARMSECONDS);//re-arm the alarm

}

/* Handler function that captures SIGINT interrupts 5 times before
actually exiting. Makes use of global variable to keep track of
number of times the interrupt was sent.
*/
static void sigint_hdl(int signum)
{

numSigints -= 1;

if(numSigints == 0)
{
printf("\nFinal Control-c caught. Exiting.\n");
exit(1);
}
else
{
printf("\nControl-c caught. %d more before program is ended.\n",
numSigints);
}
}

/* Periodically prints out the current time in Unix date format using
sigaction interrupt handling.
*/
int main(int argc, char *argv[])
{

//
// vars
//
struct sigaction act;
struct sigaction sigint_act;

memset(&act, '\0', sizeof(act));//TODO Why does this fix SEGFAULT???

//
// intro
//
printf("Date will be printed every 3 seconds.\n");
printf("Enter ^C 5 times to end the program.\n");

//
// set alarm to go off in 3 seconds
//
alarm(ALARMSECONDS);

//
// set handler of the sigaction to 'hdl' function defined above
//
act.sa_handler = hdl;
sigint_act.sa_handler = sigint_hdl;

//
// activate sigaction
//
if(sigaction(SIGALRM, &act, NULL) < 0)
{
perror("sigaction -- SIGALRM");
return 1;
}

if(sigaction(SIGINT, &sigint_act, NULL) < 0)
{
perror("sigaction -- SIGINT");
return 1;
}

//
// infinite loop
//
while(1) {
}

return 0;
}

Answer

There are a number of fields in the sigaction structure. Their default value is all zeroes.

If they are not zeroes, they will be interpreted in whatever way is described in the system call's documentation. One example would be two fields sa_mask and sa_sigaction. and depending on the values of sa_mask, the sa_sigaction handler will be invoked instead of your intended signal handler, that your code installs. And since you did not initialize the pointer, it will have some garbage value, and an attempt will be made not to merely dereference, but invoke a function through a garbage pointer.

So, if you fail to initialize the structure, you get undefined behavior. Sometimes it'll work, sometimes it won't in mysterious ways, such as a crash.

By clearing the entire structure to zeroes, you are assuring that the default behavior will take place, as modified by any non-default settings you them make to the structure.

Lesson learned: always clear and initialize all structures that are passed to library functions.