CERT.SIG.SIG_HANDLER.SHARED_OBJ

Do not access shared object in signal handlers

This checker is applicable only to the modern engine.

The checker CERT.SIG.SIG_HANDLER.SHARED_OBJ looks for signal handlers accessing non-local variables.

This checker triggers when a signal handler reads or writes data with static storage duration, including:

  • Global or static variables

  • Pointers that reference heap-allocated storage treated as shared in this context

Vulnerability and risk

Accessing or modifying shared objects in signal handlers can result in race conditions that can leave data in an inconsistent state. The two exceptions (C Standard, 5.1.2.3, paragraph 5) to this rule are the ability to read from and write to lock-free atomic objects and variables of type volatile sig_atomic_t. Accessing any other type of object from a signal handler is undefined behavior.

Mitigation and prevention

Do not access or modify shared objects in signal handlers. If so, ensure that the shared objects access falls into the exceptions:

  • read and write to a non-local variable of volatile sig_atomic_t

  • read or write to a non-local variable with atomic type that is lock-free

Vulnerable code example

Copy
#include <signal.h>
#include <stdlib.h>
#include <string.h>
 
enum { MAX_MSG_SIZE = 24 };
char *err_msg;
 
void handler(int signum) {
  strcpy(err_msg, "SIGINT encountered.");        // Non-compliant
}
 
int main(void) {
  signal(SIGINT, handler);
 
  err_msg = (char *)malloc(MAX_MSG_SIZE);
  if (err_msg == NULL) {
    /* Handle error */
  }
  strcpy(err_msg, "No errors yet.");
  /* Main code loop */
  return 0;
}

The code is non-compliant because the signal handler calls strcpy() to modify a shared object (err_msg), and strcpy() is not guaranteed to be asynchronous-signal-safe, which leads to undefined behavior.

Fixed code example

Copy
#include <signal.h>
#include <stdlib.h>
#include <string.h>
 
enum { MAX_MSG_SIZE = 24 };
volatile sig_atomic_t e_flag = 0;
 
void handler(int signum) {
  e_flag = 1;
}
 
int main(void) {
  char *err_msg = (char *)malloc(MAX_MSG_SIZE);
  if (err_msg == NULL) {
    /* Handle error */
  }
 
  signal(SIGINT, handler);
  strcpy(err_msg, "No errors yet.");
  /* Main code loop */
  if (e_flag) {
    strcpy(err_msg, "SIGINT received.");
  }
  return 0;
}

The code is now compliant because the signal handler access a shared object of type sig_atomic_t with the volatile qualifier.