Safeguard
Application Security•

CWE-828 Signal Handlers That Are Not Asynchronous-Safe

A signal handler can interrupt your program at any instruction. If it calls unsafe functions, state gets corrupted. Here is how CWE-828 works and how to fix it.

Safeguard Security Team
4 min read

CWE-828 describes a signal handler that contains code which is not asynchronous-safe: the functionality is not reentrant, or it can be interrupted. MITRE lists C and says the weakness is not language specific in principle.

It matters for daemons, network servers and anything that handles SIGTERM, SIGHUP, SIGALRM or SIGCHLD and does real work inside the handler.

How it happens

A signal handler interrupts normal execution, or even another signal, to tell the process an event happened. It can fire at almost any point, including the middle of a call to malloc or printf. If the handler uses global or static variables, or calls functions that depend on such state, it can corrupt what normal code was in the middle of changing.

// Vulnerable: malloc and syslog are not async-signal-safe
void on_term(int sig) {
    char *msg = malloc(64);
    snprintf(msg, 64, "got signal %d", sig);
    syslog(LOG_INFO, "%s", msg);
    free(msg);
    exit(0);
}

If the signal lands while the main code is inside malloc, the heap's internal bookkeeping is corrupted.

MITRE's observed examples include:

  • CVE-2008-4109: a handler uses functions that ultimately call unsafe syslog, malloc and printf style functions, leading to denial of service via multiple login attempts.
  • CVE-2006-5051: a chain where a handler contains too much functionality, introducing a race condition that leads to a double free.
  • CVE-2001-1349: unsafe calls to library functions from a signal handler.
  • CVE-2004-0794: SIGURG can be used to remotely interrupt a signal handler.

What an attacker gains

The most common result is corruption of product state, often ending in a crash or exit. MITRE adds that if the handler works on state variables that matter for security, the attacker may gain more: effects on integrity, confidentiality and availability, up to executing unauthorized code. Denial of service is frequent, but not the only outcome.

How to find it

MITRE names automated static analysis, which can model data flow in source or compiled code and spot calls from handler code to non-reentrant functions. A manual review can be even more direct: list every function registered with signal() or sigaction(), then read every call it makes, including indirect ones. Treat anything not documented as async-signal-safe as suspect.

How to fix it

MITRE gives two mitigations.

  1. Eliminate non-reentrant functionality inside signal handlers. Replace non-reentrant library calls with reentrant ones. MITRE rates this effectiveness high, and notes it may not always be possible and can mean rewriting large parts of the product.
  2. Where non-reentrant functionality must be used, block or mask signals appropriately. That includes blocking other signals inside the handler, and blocking all signals that rely on the same functionality while normal code accesses or modifies it.

The widely used pattern that follows from the first point is to do almost nothing in the handler.

#include <signal.h>

static volatile sig_atomic_t got_term = 0;

void on_term(int sig) { got_term = 1; }   // set a flag, nothing else

int main(void) {
    struct sigaction sa = {0};
    sa.sa_handler = on_term;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGTERM, &sa, NULL);

    while (!got_term) {
        do_work();
    }
    cleanup();   // the heavy lifting happens in normal context
    return 0;
}

The handler sets a flag of type sig_atomic_t. The main loop notices it and shuts down in a safe context.

Quick check

Why is printf risky inside a signal handler?

It depends on internal buffers and locks. If the signal interrupts the main code while it holds the same state, the handler can corrupt it or deadlock.

What is the safest handler design?

Set a flag and return. Do the real work in the main flow after checking the flag. If you must do more, block overlapping signals while shared state is touched.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.