Calling the signal()
function in a multithreaded program is undefined behavior according to C11 (Section 7.14.1.1, paragraph 7).
This rule is a specific instance of SIG02-C. Avoid using signals to implement normal functionality.
Noncompliant Code Example
This code sets a signal handler, while also setting a child thread to do work. This results in undefined behavior.
volatile sig_atomic_t flag = 0; void handler(int signum) { flag = 1; } int func(void *data) { /* keep running until user sends SIGUSR1 */ while (!flag) { /* ... */ } return 0; } int main(void) { signal(SIGUSR1, handler); /* Undefined! */ int result; thrd_t tid; if ((result = thrd_create(&tid, func, NULL)) != thrd_success) { /* Handle Error */ } /* ... */ return 0; }
Compliant Solution
This code dispenses with the signal handler, and uses an atomic_flag
to indicate when the child thread should terminate its loop.
atomic_flag flag = ATOMIC_VAR_INIT(0); int func(void *data) { /* keep running until user sends SIGUSR1 */ while (!flag) { /* ... */ } return 0; } int main(void) { int result; thrd_t tid; if ((result = thrd_create(&tid, func, NULL)) != thrd_success) { /* Handle Error */ } /* ... */ /* Set flag when done */ while (!atomic_flag_test_and_set( &flag)) { /* try again */ } return 0; }
Exceptions
CON37:EX0: Platforms that provide defined behavior when multithreaded programs use custom signal handlers are exempt from this rule. This would include POSIX, for example.
Risk Assessment
Mixing signals and threads causes undefined behavior.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
CON37-C | low | probable | low | P6 | L2 |