Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: REM Cost Reform

With one exception, accessing Accessing or modifying shared objects in signal handlers can lead to race conditions, opening up security holes.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. (See undefined behavior 131.)

The need for the volatile keyword is described in DCL22-C. Use volatile for data that cannot be cached.

The type sig_atomic_t is the integer type of an object that can be accessed as an atomic entity even in the presence of asynchronous interrupts. The type of sig_atomic_t is implementation-defined, though it provides some guarantees. Integer values ranging from SIG_ATOMIC_MIN through SIG_ATOMIC_MAX, inclusive, may be safely stored to a variable of the type. In addition, when sig_atomic_t is a signed integer type, SIG_ATOMIC_MIN must be no greater than −127 and SIG_ATOMIC_MAX no less than 127. Otherwise, SIG_ATOMIC_MIN must be 0 and SIG_ATOMIC_MAX must be no less than 255. The macros SIG_ATOMIC_MIN and SIG_ATOMIC_MAX are defined in the header <stdint.h>.

According to the C99 Rationale [C99 Rationale 2003]According to the "Signals and Interrupts" section of the C99 Rationale, other than calling a limited, prescribed set of library functions:,

The the C89 Committee concluded that about the only thing a strictly conforming program can do in a signal handler is to assign a value to a volatile static variable which can be written uninterruptedly and promptly return.

The C99 standard dictates the use of However, this issue was discussed at the April 2008 meeting of ISO/IEC WG14, and it was agreed that there are no known implementations in which it would be an error to read a value from a volatile sig_atomic_t. The type of sig_atomic_t is implementation-defined, although there are bounding constraints. Only assign integer values from 0 through 127 to a variable of type variable, and the original intent of the committee was that both reading and writing variables of volatile sig_atomic_t to would be fully portablestrictly conforming.

The signal handler may also call the a handful of functions, including abort() function, the _Exit(), function, or the signal() function with the first argument equal to the signal number corresponding to the signal that caused the invocation of the handler. This may be necessary to ensure that the handler persists (see SIG01-A. Understand implementation-specific details regarding signal handler persistence).

Non-Compliant Code Example

(See SIG30-C. Call only asynchronous-safe functions within signal handlers for more information.)

Noncompliant Code Example

In this noncompliant code example, err_msg is updated to reflect indicate that the SIGINT signal that was encountered. Issues will occur if a SIGINT is generated prior to the malloc of err_msg finishingdelivered.  The err_msg variable is a character pointer and not a variable of type volatile sig_atomic_t.

Code Block
bgColor#FFcccc
langc

#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.");
}

int main(void) {
  signal(SIGINT, handler);

  err_msg = (char *)malloc(24MAX_MSG_SIZE);
  if (err_msg == NULL) {
    /* handleHandle error condition */
  }
  strcpy(err_msg, "No errors yet.");

  /* mainMain code loop */

  return 0;
}

Compliant Solution

...

(Writing volatile sig_atomic_t)

For maximum portabilityTo be safe, signal handlers should only unconditionally set a flag variable of type volatile sig_atomic_t and return., as in this compliant solution:

Code Block
bgColor#ccccff
langc

#include <signal.h>
#include <stdlib.h>
#include <string.h>

char *err_msgenum { MAX_MSG_SIZE = 24 };
volatile sig_atomic_t e_flag = 0;

void handler(int signum) {
  e_flag = 1;
}

int main(void) {
  signal(SIGINT, handler);

  char *err_msg = (char *)malloc(24MAX_MSG_SIZE);
  if (err_msg == NULL) {
    /* handleHandle error condition */
  }

  signal(SIGINT, handler);
 strcpy strcpy(err_msg, "No errors yet.");

  /* mainMain code loop */
  if (e_flag) {
    strcpy(err_msg, "SIGINT received.");
  }
 
  return return 0;
}

Risk Assessment

Compliant Solution (Lock-Free Atomic Access)

Signal handlers can refer to objects with static or thread storage durations that are lock-free atomic objects, as in this compliant solution:

Code Block
bgColor#ccccff
langc
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <stdatomic.h>
 
#ifdef __STDC_NO_ATOMICS__
#error "Atomics are not supported"
#elif ATOMIC_INT_LOCK_FREE == 0
#error "int is never lock-free"
#endif

atomic_int e_flag = ATOMIC_VAR_INIT(0);
 
void handler(int signum) {
  e_flag = 1;
}

int main(void) {
  enum { MAX_MSG_SIZE = 24 };
  char err_msg[MAX_MSG_SIZE];
#if ATOMIC_INT_LOCK_FREE == 1
  if (!atomic_is_lock_free(&e_flag)) {
    return EXIT_FAILURE;
  }
#endif
  if (signal(SIGINT, handler) == SIG_ERR) {
    return EXIT_FAILURE;
  }
  strcpy(err_msg, "No errors yet.");
  /* Main code loop */
  if (e_flag) {
    strcpy(err_msg, "SIGINT received.");
  }
  return EXIT_SUCCESS;
}

Exceptions

SIG31-C-EX1:  The C Standard, 7.14.1.1 paragraph 5 [ISO/IEC 9899:2024], makes a special exception for errno when a valid call to the signal() function results in a SIG_ERR return, allowing errno to take an indeterminate value. (See ERR32-C. Do not rely on indeterminate values of errno.)

the signal function with the first argument equal to the signal number corresponding to the signal that caused the invocation of the handler. Furthermore, if such a call to the signal function results in a SIG_ERR return, the object designated by errno has an indeterminate representation.

Risk Assessment

Accessing or modifying shared objects in signal handlers can result in accessing data in an inconsistent state. Michal Zalewski's paper "Delivering Signals for Fun and Profit" [Zalewski 2001] provides some examples of vulnerabilities that can result from violating this and other signal-handling rules Wiki MarkupDepending on the code, this could lead to any number of attacks, many of which could give root access. For an overview of some software vulnerabilities, see \[[Zalewski 06|AA. C References#Zalewski 06]\].

Rule

Severity

Likelihood

Detectable

Remediation Cost

Repairable

Priority

Level

SIG31-C

3 (high)

3 (likely)

1 (high)

P9

L2

Automated Detection

The tool Compass Rose can detect violations of the recommendation for single-file programs.

High

Likely

Yes

No

P18

L1

Automated Detection

Tool

Version

Checker

Description

Astrée
Include Page
Astrée_V
Astrée_V
signal-handler-shared-accessPartially checked
Axivion Bauhaus Suite

Include Page
Axivion Bauhaus Suite_V
Axivion Bauhaus Suite_V

CertC-SIG31
CodeSonar
Include Page
CodeSonar_V
CodeSonar_V
CONCURRENCY.DATARACEData race
Compass/ROSE



Can detect violations of this rule for single-file programs

Cppcheck Premium
24.9.0

premium-cert-sig31-c


Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

C2029, C2030

C++3854, C++3855


LDRA tool suite
Include Page
LDRA_V
LDRA_V

87 D

Fully implemented

Parasoft C/C++test

Include Page
Parasoft_V
Parasoft_V

CERT_C-SIG31-aProperly define signal handlers
PC-lint Plus

Include Page
PC-lint Plus_V
PC-lint Plus_V

2765

Fully supported

Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C: Rule SIG31-CChecks for shared data access within signal handler (rule partially covered)
RuleChecker

Include Page
RuleChecker_V
RuleChecker_V

signal-handler-shared-accessPartially checked

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Mitigation Strategies

Static Analysis

Compliance with this rule can be checked using structural static analysis checkers using the following algorithm:

  1. Traverse the abstract syntax tree (AST) to identify function calls to the signal function signal(int, void (*f)(int)).
  2. At each function call to signal(int, void (*f)(int)) get the second argument from the argument list. To make sure that this is not an overloaded function the function type signature is evaluated and/or the location of the declaration of the function is verified to be from the correct file (because this is not a link-time analysis it is not possible to test the library implementation). Any definition for signal() in the application is suspicious, because it should be in a library.
  3. Perform a nested query to identify all referenced objects with static storage duration. Verify that none of these objects are referenced as an rvalue, and that for each object referenced as an lvalue, the underlying type is sig_atomic_t.
  4. Report any violations detected.

References

Wiki Markup
\[[Dowd 06|AA. C References#Dowd 06]\] Chapter 13, Synchronization and State
\[[ISO/IEC 03|AA. C References#ISO/IEC 03]\] "Signals and Interrupts"
\[[Open Group 04|AA. C References#Open Group 04]\] [longjmp|http://www.opengroup.org/onlinepubs/000095399/functions/longjmp.html]
\[OpenBSD\] [{{signal()}} Man Page|http://www.openbsd.org/cgi-bin/man.cgi?query=signal]
\[Zalewski\] [http://lcamtuf.coredump.cx/signals.txt]

Related Guidelines

Key here (explains table format and definitions)

Taxonomy

Taxonomy item

Relationship

ISO/IEC TS 17961:2013Accessing shared objects in signal handlers [accsig]Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11CWE-662, Improper Synchronization2017-07-10: CERT: Rule subset of CWE
CWE 2.11CWE-828, Signal Handler with Functionality that is not Asynchronous-Safe

2017-10-30:MITRE:Unspecified Relationship

2018-10-19:CERT:Rule subset of CWE

CERT-CWE Mapping Notes

Key here for mapping notes

CWE-662 and SIG31-C

CWE-662 = Union( SIG31-C, list) where list =

  • Improper synchronization of shared objects between threads
  • Improper synchronization of files between programs (enabling TOCTOU race conditions

CWE-828 and SIG31-C

CWE-828 = SIG31-C + non-async-safe things besides shared objects.

Bibliography

[C99 Rationale 2003]5.2.3, "Signals and Interrupts"
[ISO/IEC 9899:2024]Subclause 7.14.1.1, "The signal Function"
[Zalewski 2001]


...

Image Added Image Added Image AddedImage Removed      12. Signals (SIG)       SIG32-C. Do not call longjmp() from inside a signal handler