Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Updated references from C11->C23

...

The C Standard, 7.14.1.1, paragraph 5 [ISO/IEC 9899:20112024], states that if the signal occurs other than as the result of calling the abort() or raise() function, the behavior is undefined if if

...If the signal handler calls any function in the standard library occurs other than the abort function, the _Exit function, the quick_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.

Implementations may define a list of additional asynchronous-safe functions. These functions can also be called within a signal handler. This restriction applies to library functions as well as application-defined functions.

According to the C Rationale, 7.14.1.1 [C99 Rationale 2003],

When a signal occurs, the normal flow of control of a program is interrupted. If a signal occurs that is being trapped by a signal handler, that handler is invoked. When it is finished, execution continues at the point at which the signal occurred. This arrangement can cause problems if the signal handler invokes a library function that was being executed at the time of the signal.

In general, it is not safe to invoke I/O functions from within signal handlers. Programmers should ensure a function is included in the list of an implementation's asynchronous-safe functions for all implementations the code will run on before using them in signal handlers.

Noncompliant Code Example

In this noncompliant example, the C standard library functions fputs() and free() are called from the signal handler via the function log_message(). Neither function is asynchronous-safe.

as the result of calling the abort or raise function, the behavior is undefined if the signal handler refers to any object with static or thread storage duration that is not a lock-free atomic object and that is not declared with the constexpr storage-class specifier other than by assigning a value to an object declared as volatile sig_atomic_t, or the signal handler calls any function in the standard library other than

—  the abort function,

—  the _Exit function,

—  the quick_exit function,

—  the functions in <stdatomic.h> (except where explicitly stated otherwise) when the atomic arguments are lock-free,

—  the atomic_is_lock_free function with any atomic argument, or

—  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.294)

Implementations may define a list of additional asynchronous-safe functions. These functions can also be called within a signal handler. This restriction applies to library functions as well as application-defined functions.

According to the C Rationale, 7.14.1.1 [C99 Rationale 2003],

When a signal occurs, the normal flow of control of a program is interrupted. If a signal occurs that is being trapped by a signal handler, that handler is invoked. When it is finished, execution continues at the point at which the signal occurred. This arrangement can cause problems if the signal handler invokes a library function that was being executed at the time of the signal.

In general, it is not safe to invoke I/O functions from within signal handlers. Programmers should ensure a function is included in the list of an implementation's asynchronous-safe functions for all implementations the code will run on before using them in signal handlers.

Noncompliant Code Example

In this noncompliant example, the C standard library functions fputs() and free() are called from the signal handler via the function log_message(). Neither function is asynchronous-safe.

Code Block
bgColor#FFcccc
langc
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>

enum { MAXLINE = 1024 };
char *info = NULL;

void log_message(void) {
  fputs(info, stderr);
}

void handler(int signum) {
  log_message();
  free(info);
  info = NULL;
}

int main(void) {
  if (signal(SIGINT, handler) == SIG_ERR) {
    /* Handle error
Code Block
bgColor#FFcccc
langc
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>

enum { MAXLINE = 1024 };
char *info = NULL;

void log_message(void) {
  fputs(info, stderr);
}

void handler(int signum) {
  log_message();
  free(info);
  info = NULL;
}

int main(void) {
  if (signal(SIGINT, handler) == SIG_ERR) {
    /* Handle error */
  }
  info = (char *)malloc(MAXLINE);
  if (info == NULL) {
    /* Handle Error */
  }

  while (1) {
    /* Main loop program code */

    log_message();

    /* More program code */
  }
  info return 0;
}

Compliant Solution

Signal handlers should be as concise as possible—ideally by unconditionally setting a flag and returning. This compliant solution sets a flag of type volatile sig_atomic_t and returns; the log_message() and free() functions are called directly from main():

= (char *)malloc(MAXLINE);
  if (info == NULL) {
    /* Handle Error */
  }

  while (1) {
    /* Main loop program code */

    log_message();

    /* More program code */
  }
  return 0;
}

Compliant Solution

Signal handlers should be as concise as possible—ideally by unconditionally setting a flag and returning. This compliant solution sets a flag of type volatile sig_atomic_t and returns; the log_message() and free() functions are called directly from main():

Code Block
bgColor#ccccff
langc
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>

enum { MAXLINE = 1024 };
volatile sig_atomic_t eflag = 0;
char *info = NULL;

void log_message(void) {
  fputs(info, stderr);
}

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

int main(void) {
  if (signal(SIGINT, handler) == SIG_ERR) {
    /* Handle error */
  }
  info = (char *)malloc(MAXLINE);
  if (info == NULL) {
Code Block
bgColor#ccccff
langc
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>

enum { MAXLINE = 1024 };
volatile sig_atomic_t eflag = 0;
char *info = NULL;

void log_message(void) {
  fputs(info, stderr);
}

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

int main(void) {
  if (signal(SIGINT, handler) == SIG_ERR) {
    /* Handle error */
  }
  info = (char *)malloc(MAXLINE);
  if (info == NULL) {
    /* Handle error */
  }

  while (!eflag) {
    /* Main loop program code */

    log_message();

    /* MoreHandle program codeerror */
  }

  while log_message(!eflag); {
   free( /* Main loop program code */

    log_message();

    /* More program code */
  }

  log_message();
  free(info);
  info = NULL;

  return 0;
}

...

_Exit()

fexecve()

posix_trace_event()

sigprocmask()

_exit()

fork()

pselect()

sigqueue()

abort()

fstat()

pthread_kill()

sigset()

accept()

fstatat()

pthread_self()

sigsuspend()

access()

fsync()

pthread_sigmask()

sleep()

aio_error()

ftruncate()

raise()

sockatmark()

aio_return()

futimens()

read()

socket()

aio_suspend()

getegid()

readlink()

socketpair()

alarm()

geteuid()

readlinkat()

stat()

bind()

getgid()

recv()

symlink()

cfgetispeed()

getgroups()

recvfrom()

symlinkat()

cfgetospeed()

getpeername()

recvmsg()

tcdrain()

cfsetispeed()

getpgrp()

rename()

tcflow()

cfsetospeed()

getpid()

renameat()

tcflush()

chdir()

getppid()

rmdir()

tcgetattr()

chmod()

getsockname()

select()

tcgetpgrp()

chown()

getsockopt()

sem_post()

tcsendbreak()

clock_gettime()

getuid()

send()

tcsetattr()

close()

kill()

sendmsg()

tcsetpgrp()

connect()

link()

sendto()

time()

creat()

linkat()

setgid()

timer_getoverrun()

dup()

listen()

setpgid()

timer_gettime()

dup2()

lseek()

setsid()

timer_settime()

execl()

lstat()

setsockopt()

times()

execle()

mkdir()

setuid()

umask()

execv()

mkdirat()

shutdown()

uname()

execve()

mkfifo()

sigaction()

unlink()

faccessat()

mkfifoat()

sigaddset()

unlinkat()

fchdir()

mknod()

sigdelset()

utime()

fchmod()

mknodat()

sigemptyset()

utimensat()

fchmodat()

open()

sigfillset()

utimes()

fchown()

openat()

sigismember()

wait()

fchownat()

pause()

signal()

waitpid()

fcntl()

pipe()

sigpause()

write()

fdatasync()

poll()

sigpending()

 

All functions not listed in this table are considered to be unsafe with respect to signals. In the presence of signals, all POSIX functions behave as defined when called from or interrupted by a signal handler, with a single exception: when a signal interrupts an unsafe function and the signal handler calls an unsafe function, the behavior is undefined.

The C Standard, 7.14.1.1, paragraph 4 [ISO/IEC 9899:2011], states

If the signal occurs as the result of calling the abort or raise function, the signal handler shall not call the raise function.

However, in the description of signal(), POSIX [IEEE Std 1003.1:2013] states

This restriction does not apply to POSIX applications, as POSIX.1-2008 requires raise() to be async-signal-safe.

See also undefined behavior 131. 

OpenBSD

The OpenBSD signal() manual page lists a few additional functions that are asynchronous-safe in OpenBSD but "probably not on other systems" [OpenBSD], including snprintf()vsnprintf(), and syslog_r() but only when the syslog_data struct is initialized as a local variable.

Risk Assessment

Invoking functions that are not asynchronous-safe from within a signal handler is undefined behavior.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

SIG30-C

High

Likely

Medium

P18

L1

Automated Detection

()

execl()

lstat()

setsockopt()

times()

execle()

mkdir()

setuid()

umask()

execv()

mkdirat()

shutdown()

uname()

execve()

mkfifo()

sigaction()

unlink()

faccessat()

mkfifoat()

sigaddset()

unlinkat()

fchdir()

mknod()

sigdelset()

utime()

fchmod()

mknodat()

sigemptyset()

utimensat()

fchmodat()

open()

sigfillset()

utimes()

fchown()

openat()

sigismember()

wait()

fchownat()

pause()

signal()

waitpid()

fcntl()

pipe()

sigpause()

write()

fdatasync()

poll()

sigpending()

 


All functions not listed in this table are considered to be unsafe with respect to signals. In the presence of signals, all POSIX functions behave as defined when called from or interrupted by a signal handler, with a single exception: when a signal interrupts an unsafe function and the signal handler calls an unsafe function, the behavior is undefined.

The C Standard, 7.14.1.1, paragraph 4 [ISO/IEC 9899:2011], states

If the signal occurs as the result of calling the abort or raise function, the signal handler shall not call the raise function.

However, in the description of signal(), POSIX [IEEE Std 1003.1:2013] states

This restriction does not apply to POSIX applications, as POSIX.1-2008 requires raise() to be async-signal-safe.

See also undefined behavior 131. 

OpenBSD

The OpenBSD signal() manual page lists a few additional functions that are asynchronous-safe in OpenBSD but "probably not on other systems" [OpenBSD], including snprintf()vsnprintf(), and syslog_r() but only when the syslog_data struct is initialized as a local variable.

Risk Assessment

Invoking functions that are not asynchronous-safe from within a signal handler is undefined behavior.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

SIG30-C

High

Likely

Medium

P18

L1

Automated Detection

Tool

Version

Checker

Description

Astrée
Include Page
Astrée_V
Astrée_V
signal-handler-unsafe-callPartially checked
Axivion Bauhaus Suite

Include Page
Axivion Bauhaus Suite_V
Axivion Bauhaus Suite_V

CertC-SIG30
CodeSonar
Include Page
CodeSonar_V
CodeSonar_V

BADFUNC.SIGNAL

Use of signal

Compass/ROSE

Can detect violations of the rule for single-file programs
Cppcheck Premium

Include Page
Cppcheck Premium_V
Cppcheck Premium_V

premium-cert-sig30-cFully implemented
Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

C2028, C2030
LDRA tool suite
Include Page
LDRA_V
LDRA_V

88 D, 89 D 

Partially implemented

Parasoft C/C++test

Include Page
Parasoft_V
Parasoft_V

CERT_C-SIG30-a

Properly define signal handlers

PC-lint Plus

Include Page
PC-lint Plus_V
PC-lint Plus_V

2670, 2761

Fully supported

Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C: Rule SIG30-C


Checks for function called from signal handler not asynchronous-safe (rule fully covered)

RuleChecker

Include Page
RuleChecker_V
RuleChecker_V

signal-handler-unsafe-callPartially checked

Tool

Version

Checker

Description

Astrée
Include Page
Astrée_VAstrée_VSupported, but no explicit checkerCompass/ROSECan detect violations of the rule for single-file programsLDRA tool suite
Include Page
LDRA_VLDRA_V

88 D, 89 D 

Partially implemented
Splint
Include Page
Splint_V
Splint_V



...

[C99 Rationale 2003]Subclause 5.2.3, "Signals and Interrupts"
Subclause 7.14.1.1, "The signal Function"
[Dowd 2006]Chapter 13, "Synchronization and State"
[Greenman 1997]
[IEEE Std 1003.1:2013] XSH, System Interfaces, longjmp
XSH, System Interfaces, raise
[ISO/IEC 9899:20112024]7.14.1.1, "The signal Function"
[OpenBSD]signal() Man Page
[VU #834865]
[Zalewski 2001]"Delivering Signals for Fun and Profit"

...