VNX-432: Dangerous Signal Handler not Disabled During Sensitive Operations
Reports SA_NODEFER on a C/C++ sigaction whose handler does non-reentrant work (free, malloc, printf, syslog, exit, ...), so a second delivery of the same signal re-enters the handler while it runs.
| Field | Value |
|---|---|
| Rule ID | VNX-432 |
| Severity | Low |
| SARIF level | note |
| Kind | sast |
| Languages | C, C++ |
| CWE | CWE-432 |
| CAPEC | CAPEC-66 |
| ATT&CK | T1190 |
| CWSS vector | CWSS:1.0/TI:L/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:L/DI:H/EX:L/EC:N/P:L |
| Tags | dangerous-signal signal-handler cwe-432 |
| Source | internal/sast/rules/vnx-432.rego |
Overview
VNX-432 maps to CWE-432: Dangerous Signal Handler not Disabled During Sensitive Operations. By default the kernel blocks a signal while its own handler runs, so the handler cannot be re-entered by the same signal. SA_NODEFER (and the old Linux spelling SA_NOMASK) switches that off. If the handler does non-reentrant work, a second delivery re-enters it half way through: a global freed twice, a stdio buffer or a lock left half updated.
What Gets Flagged
The line that puts SA_NODEFER/SA_NOMASK into a struct sigaction’s sa_flags (sa.sa_flags = ..., sa.sa_flags |= ..., or a designated initialiser { .sa_flags = SA_NODEFER }), when:
- the same struct’s
sa_handler/sa_sigactionis a function defined in the same file whose body calls something that is not async-signal-safe (free,malloc, theprintffamily,syslog,exit,strtok,localtime,strerror,longjmp,pthread_mutex_lock,system,new/delete,std::cout/std::cerr, …), and - the struct is passed to
sigaction()for at least one asynchronous signal within thirty lines.
static void on_hup(int sig)
{
syslog(LOG_NOTICE, "reloading on signal %d", sig);
free(config_buf);
}
sa.sa_handler = on_hup;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_NODEFER | SA_RESTART; // FLAGGED
sigaction(SIGHUP, &sa, NULL);
What Is Not Flagged
SA_NODEFERtogether withSA_RESETHAND: the second delivery takes the default action instead of re-entering the handler.- Handlers installed only for synchronous fault signals (
SIGSEGV,SIGBUS,SIGFPE,SIGILL,SIGABRT,SIGTRAP,SIGSYS), the usual crash-reporter design. - Handlers that only set a
volatile sig_atomic_tflag or callwrite/_exit, handlers defined in another file,SIG_IGN/SIG_DFL, comments and strings.
One handler installed for two or more signals with no mask is reported by VNX-831, and each non-async-signal-safe call inside a handler is reported by VNX-828. This rule reports only the flag that disables the handler’s blocking of its own signal. A handler that writes an unprotected global is VNX-364.
Remediation
- Remove
SA_NODEFERso the signal stays blocked while its handler runs, or addSA_RESETHANDif the handler is meant to run only once. - Fill
sa_mask(sigfillset(&sa.sa_mask)) to block other signals as well. - Keep handlers minimal: set a
volatile sig_atomic_tflag and do the work in the main loop.
References
Suppressing this rule
Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):
# nosec vnx-432
// nosec vnx-432
Or record an ignore rule: vulnetix ignore add --rule vnx-432 --reason "...". See Suppressing findings.