CERT.SIG.SIG_HANDLER.SHARED_OBJ.MIGHT

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.MIGHT looks for access to non-local variables with atomic type that are not guaranteed to be lock-free in signal handlers.

This checker triggers on reads or writes to atomic objects when lock-free behavior cannot be proven. If the type-specific ATOMIC_*_LOCK_FREE macro is known to be 0, the base checker reports CERT.SIG.SIG_HANDLER.SHARED_OBJ.

  • If the macro is 1 (implementation-defined lock-freedom), the checker looks for a matching call to atomic_is_lock_free(&var). If found, no issue is reported; otherwise, MIGHT is reported.

  • If the macro value cannot be determined, the checker conservatively reports MIGHT.

  • If the macro is 2 (always lock-free), no issue is reported.

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 or write to a non-local variable of volatile sig_atomic_t type

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

Vulnerable code example

Copy
#include <signal.h>
#include <stdatomic.h>

atomic_int e_flag = ATOMIC_VAR_INIT(0);

void handler(int signum) {
  e_flag = 1; // CERT.SIG.SIG_HANDLER.SHARED_OBJ.MIGHT
}

int main(void) {
  // If ATOMIC_INT_LOCK_FREE == 1, consider confirming at runtime:
  // if (!atomic_is_lock_free(&e_flag)) { /* handle not lock-free */ }
  signal(SIGINT, handler); // CERT.SIG.SIG_HANDLER.SHARED_OBJ.MIGHT
  return 0;
}

This code is flagged as MIGHT because it writes to an atomic object (e_flag) inside a signal handler without confirming that the atomic type is lock-free.

Fixed code example

Copy
#include &lt;stdatomic.h&gt;

atomic_int e_flag = ATOMIC_VAR_INIT(0);

void handler(int signum) {
  e_flag = 1; // CERT.SIG.SIG_HANDLER.SHARED_OBJ.MIGHT
}

int main(void) {
  // If ATOMIC_INT_LOCK_FREE == 1, consider confirming at runtime:
  if (!atomic_is_lock_free(&amp;e_flag)) { /* handle not lock-free */ }
  signal(SIGINT, handler); // CERT.SIG.SIG_HANDLER.SHARED_OBJ.MIGHT
  return 0;

The code verifies atomic_is_lock_free, so it satisfies the requirement.

If additional assurance is desired, add a compile-time check to guarantee the atomic remains lock-free on all supported platforms:

Copy
#include &lt;stdatomic.h&gt;

atomic_int e_flag = ATOMIC_VAR_INIT(0);

// The handler doesn't do anything and is therefore compliant but not useful
void safe_handler(int signum) {};

void handler(int signum) {
  e_flag = 1; // CERT.SIG.SIG_HANDLER.SHARED_OBJ.MIGHT
}

int main(void) {

#if definfed(ATOMIC_INT_LOCK_FREE) && (ATOMIC_INT_LOCK_FREE == 1)
    if (!atomic_is_lock_free(&amp;e_flag)) {  signal(SIGINT, safe_handler);  }
    else {signal(SIGINT, handler);}
#if definfed(ATOMIC_INT_LOCK_FREE) && (ATOMIC_INT_LOCK_FREE == 2)
  signal(SIGINT, handler);
#else
  signal(SIGINT, safe_handler);
#endif

  return 0;