Main Content

Misuse of errno in a signal handler

You read errno after calling an errno-setting function in a signal handler

Description

This defect occurs when you call one of these functions in a signal handler:

  • signal: You call the signal function in a signal handler and then read the value of errno.

    For instance, the signal handler function handler calls signal and then calls perror, which reads errno.

    typedef void (*pfv)(int);
    
    void handler(int signum) {
      pfv old_handler = signal(signum, SIG_DFL);
      if (old_handler == SIG_ERR) {
        perror("SIGINT handler"); 
      }
    }

  • errno-setting POSIX® function: You call an errno-setting POSIX function in a signal handler but do not restore errno when returning from the signal handler.

    For instance, the signal handler function handler calls waitpid, which changes errno, but does not restore errno before returning.

    #include <stddef.h>
    #include <errno.h>
    #include <sys/wait.h>
    
    void handler(int signum) {
      int rc = waitpid(-1, NULL, WNOHANG);
      if (ECHILD != errno) {
      }
    }

Risk

In each case that the checker flags, you risk relying on an indeterminate value of errno.

  • signal: If the call to signal in a signal handler fails, the value of errno is indeterminate (see C11 Standard, Sec. 7.14.1.1). If you rely on a specific value of errno, you can see unexpected results.

  • errno-setting POSIX function: An errno-setting function sets errno on failure. If you read errno after a signal handler is called and the signal handler itself calls an errno-setting function, you can see unexpected results.

Fix

Avoid situations where you risk relying on an indeterminate value of errno.

  • signal: After calling the signal function in a signal handler, do not read errno or use a function that reads errno.

  • errno-setting POSIX function: Before calling an errno-setting function in a signal handler, save errno to a temporary variable. Restore errno from this variable before returning from the signal handler.

Examples

expand all

#include <signal.h>
#include <stdlib.h>
#include <stdio.h>

#define fatal_error() abort()

void handler(int signum) {
    if (signal(signum, SIG_DFL) == SIG_ERR) {
        perror("SIGINT handler");
    }
}

int func(void) {
    if (signal(SIGINT, handler) == SIG_ERR) {
        /* Handle error */
        fatal_error();
    }
    /* Program code */
    if (raise(SIGINT) != 0) {
        /* Handle error */
        fatal_error();
    }
    return 0;
}

In this example, the function handler is called to handle the SIGINT signal. In the body of handler, the signal function is called. Following this call, the value of errno is indeterminate. The checker raises a defect when the perror function is called because perror relies on the value of errno.

Correction — Avoid Reading errno After signal Call

One possible correction is to not read errno after calling the signal function in a signal handler. The corrected code here calls the abort function via the fatal_error macro instead of the perror function.

#include <signal.h>
#include <stdlib.h>
#include <stdio.h>

#define fatal_error() abort()

void handler(int signum) {
    if (signal(signum, SIG_DFL) == SIG_ERR) {
        fatal_error();
    }
} 

int func(void) {
    if (signal(SIGINT, handler) == SIG_ERR) {
        /* Handle error */
        fatal_error();
    }
    /* Program code */
    if (raise(SIGINT) != 0) {
        /* Handle error */
        fatal_error();
    }
    return 0;
}

Result Information

Group: Programming
Language: C | C++
Default: On for handwritten code, off for generated code
Command-Line Syntax: SIG_HANDLER_ERRNO_MISUSE
Impact: Medium

Version History

Introduced in R2018a