CWE Rule 663
Description
Rule Description
The product calls a non-reentrant function in a concurrent context in which a competing code sequence (e.g. thread or signal handler) may have an opportunity to call the same function or otherwise influence its state.
Polyspace Implementation
The rule checker checks for these issues:
Function called from signal handler not asynchronous-safe
Function called from signal handler not asynchronous-safe (strict ISO C)
Unsafe standard encryption function
Unsafe standard function
Examples
Function called from signal handler not asynchronous-safe
This issue occurs when a signal handler calls a function that is not asynchronous-safe according to the POSIX standard. An asynchronous-safe function can be interrupted at any point in its execution, then called again without causing an inconsistent state. It can also correctly handle global data that might be in an inconsistent state.
If a signal handler calls another function that calls an asynchronous-unsafe function, the defect appears on the function call in the signal handler. The defect traceback shows the full path from the signal handler to the asynchronous-unsafe function.
When a signal handler is invoked, the execution of the program is interrupted. After the handler is finished, program execution resumes at the point of interruption. If a function is executing at the time of the interruption, calling it from within the signal handler is undefined behavior, unless it is asynchronous-safe.
The POSIX standard defines these functions as asynchronous-safe. You can call these functions from a signal handler.
_exit() | getpgrp() | setsockopt() |
_Exit() | getpid() | setuid() |
abort() | getppid() | shutdown() |
accept() | getsockname() | sigaction() |
access() | getsockopt() | sigaddset() |
aio_error() | getuid() | sigdelset() |
aio_return() | kill() | sigemptyset() |
aio_suspend() | link() | sigfillset() |
alarm() | linkat() | sigismember() |
bind() | listen() | signal() |
cfgetispeed() | lseek() | sigpause() |
cfgetospeed() | lstat() | sigpending() |
cfsetispeed() | mkdir() | sigprocmask() |
cfsetospeed() | mkdirat() | sigqueue() |
chdir() | mkfifo() | sigset() |
chmod() | mkfifoat() | sigsuspend() |
chown() | mknod() | sleep() |
clock_gettime() | mknodat() | sockatmark() |
close() | open() | socket() |
connect() | openat() | socketpair() |
creat() | pathconf() | stat() |
dup() | pause() | symlink() |
dup2() | pipe() | symlinkat() |
execl() | poll() | sysconf() |
execle() | posix_trace_event() | tcdrain() |
execv() | pselect() | tcflow() |
execve() | pthread_kill() | tcflush() |
faccessat() | pthread_self() | tcgetattr() |
fchdir() | pthread_sigmask() | tcgetpgrp() |
fchmod() | quick_exit() | tcsendbreak() |
fchmodat() | raise() | tcsetattr() |
fchown() | read() | tcsetpgrp() |
fchownat() | readlink() | time() |
fcntl() | readlinkat() | timer_getoverrun() |
fdatasync() | recv() | timer_gettime() |
fexecve() | recvfrom() | timer_settime() |
fork() | recvmsg() | times() |
fpathconf() | rename() | umask() |
fstat() | renameat() | uname() |
fstatat() | rmdir() | unlink() |
fsync() | select() | unlinkat() |
ftruncate() | sem_post() | utime() |
futimens() | send() | utimensat() |
getegid() | sendmsg() | utimes() |
geteuid() | sendto() | wait() |
getgid() | setgid() | waitpid() |
getgroups() | setpgid() | write() |
getpeername() | setsid() |
Functions not in the previous table are not asynchronous-safe, and should not be called from a signal hander.
#include <signal.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <setjmp.h> #include <syslog.h> #include <unistd.h> #define SIZE20 20 extern volatile sig_atomic_t e_flag; void display_info(const char *info) { if (info) { (void)fputs(info, stderr); } } void sig_handler(int signum) { /* Call function printf() that is not asynchronous-safe */ printf("signal %d received.", signum); //Noncompliant e_flag = 1; } int main(void) { e_flag = 0; if (signal(SIGINT, sig_handler) == SIG_ERR) { /* Handle error */ } char *info = (char *)calloc(SIZE20, sizeof(char)); if (info == NULL) { /* Handle Error */ } while (!e_flag) { /* Main loop program code */ display_info(info); /* More program code */ } free(info); info = NULL; return 0; }
In this example, sig_handler
calls printf()
when
catching a signal. If the handler catches another signal while printf()
is executing, the behavior of the program is undefined.
Use your signal handler to set only the value of a flag. e_flag
is
of type volatile sig_atomic_t
. sig_handler
can
safely access it asynchronously.
#include <signal.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <setjmp.h> #include <syslog.h> #include <unistd.h> #define SIZE20 20 extern volatile sig_atomic_t e_flag; void display_info(const char *info) { if (info) { (void)fputs(info, stderr); } } void sig_handler1(int signum) { int s0 = signum; e_flag = 1; } int func(void) { e_flag = 0; if (signal(SIGINT, sig_handler1) == SIG_ERR) { /* Handle error */ } char *info = (char *)calloc(SIZE20, 1); if (info == NULL) { /* Handle error */ } while (!e_flag) { /* Main loop program code */ display_info(info); /* More program code */ } free(info); info = NULL; return 0; }
Function called from signal handler not asynchronous-safe (strict ISO C)
This issue occurs when a signal handler calls a function that is not asynchronous-safe according to the C standard. An asynchronous-safe function can be interrupted at any point in its execution, then called again without causing an inconsistent state. It can also correctly handle global data that might be in an inconsistent state.
The C standard defines a stricter subset of functions as asynchronous-safe compared to the set of functions that are asynchronous-safe according to the POSIX standard. Function called from signal handler not asynchronous-safe (strict ISO C) reports a defect when a signal handler calls any function that is not part of that subset, even if the function is asynchronous-safe according to the POSIX standard.
To check for calls to functions that are not asynchronous-safe according to the POSIX standard, enable checker Function called from signal handler not asynchronous-safe.
If a signal handler calls another function that calls an asynchronous-unsafe function, the defect appears on the function call in the signal handler. The defect traceback shows the full path from the signal handler to the asynchronous-unsafe function.
When a signal handler is invoked, the execution of the program is interrupted. After the handler is finished, program execution resumes at the point of interruption. If a function is executing at the time of the interruption, calling it from within the signal handler is undefined behavior, unless it is asynchronous-safe.
The C standard defines the following functions as asynchronous-safe. You can call these functions from a signal handler:
abort()
_Exit()
quick_exit()
signal()
#include <signal.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <setjmp.h> #include <syslog.h> #include <unistd.h> void SIG_ERR_handler(int signum) { int s0 = signum; /* SIGTERM specific handling */ } void sig_handler(int signum) { int s0 = signum; /* Call raise() */ if (raise(SIGTERM) != 0) { //Noncompliant /* Handle error */ } } int finc(void) { if (signal(SIGTERM, SIG_ERR_handler) == SIG_ERR) { /* Handle error */ } if (signal(SIGINT, sig_handler) == SIG_ERR) { /* Handle error */ } /* Program code */ if (raise(SIGINT) != 0) { /* Handle error */ } /* More code */ return 0; }
In this example, sig_handler
calls raise()
when
catching a signal. If the handler catches another signal while raise()
is
executing, the behavior of the program is undefined.
raise()
in Signal HandlerAccording to the C standard, the only functions that you can safely call from a signal
handler are abort()
, _Exit()
,
quick_exit()
, and signal()
.
#include <signal.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <setjmp.h> #include <syslog.h> #include <unistd.h> void SIG_ERR_handler(int signum) { int s0 = signum; /* SIGTERM specific handling */ } void sig_handler(int signum) { int s0 = signum; } int func(void) { if (signal(SIGTERM, SIG_ERR_handler) == SIG_ERR) { /* Handle error */ } if (signal(SIGINT, sig_handler) == SIG_ERR) { /* Handle error */ } /* Program code */ if (raise(SIGINT) != 0) { /* Handle error */ } /* More code */ return 0; }
Unsafe standard encryption function
This issue occurs when a standard encryption function uses a broken or weak cryptographic
algorithm. For example, crypt
is not reentrant and is based on the
risky Data Encryption Standard (DES).
The use of a broken, weak, or nonstandard algorithm can expose sensitive information to an attacker. A determined hacker can access the protected data using various techniques.
If the weak function is nonreentrant, when you use the function in concurrent programs, there is an additional race condition risk.
Avoid functions that use these encryption algorithms. Instead, use a reentrant function that uses a stronger encryption algorithm.
Note
Some implementations of crypt
support additional,
possibly more secure, encryption algorithms.
#define _GNU_SOURCE #include <pwd.h> #include <string.h> #include <crypt.h> volatile int rd = 1; const char *salt = NULL; struct crypt_data input, output; int verif_pwd(const char *pwd, const char *cipher_pwd, int safe) { int r = 0; char *decrypted_pwd = NULL; switch(safe) { case 1: decrypted_pwd = crypt_r(pwd, cipher_pwd, &output); break; case 2: decrypted_pwd = crypt_r(pwd, cipher_pwd, &output); break; default: decrypted_pwd = crypt(pwd, cipher_pwd); //Noncompliant break; } r = (strcmp(cipher_pwd, decrypted_pwd) == 0); return r; }
In this example, crypt_r
and crypt
decrypt
a password. However, crypt
is nonreentrant and
uses the unsafe Data Encryption Standard
algorithm.
crypt_r
One possible correction is to replace crypt
with crypt_r
.
#define _GNU_SOURCE #include <pwd.h> #include <string.h> #include <crypt.h> volatile int rd = 1; const char *salt = NULL; struct crypt_data input, output; int verif_pwd(const char *pwd, const char *cipher_pwd, int safe) { int r = 0; char *decrypted_pwd = NULL; switch(safe) { case 1: decrypted_pwd = crypt_r(pwd, cipher_pwd, &output); break; case 2: decrypted_pwd = crypt_r(pwd, cipher_pwd, &output); break; default: decrypted_pwd = crypt_r(pwd, cipher_pwd, &output); break; } r = (strcmp(cipher_pwd, decrypted_pwd) == 0); return r; }
Unsafe standard function
This issue occurs when you use standard functions that are unsafe and must not be used for security-related programming. Functions can be unsafe for many reasons. Some functions are unsafe because they are nonreentrant. Other functions change behavior depending on the target or platform, making some implementations unsafe.
Some unsafe functions are not reentrant, meaning that the contents of the function are not locked during a call. So, an attacker can change the values midstream.
getlogin
specifically can be unsafe
depending on the implementation. Some implementations of getlogin
return
only the first eight characters of a log-in name. An attacker can
use a different login with the same first eight characters to gain
entry and manipulate the program.
Avoid unsafe functions for security-related
purposes. If you cannot avoid unsafe
functions, use a safer version of the function instead. For getlogin
,
use getlogin_r
.
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <pwd.h> #include <string.h> #include <stdlib.h> volatile int rd = 1; int login_name_check(char *user) { int r = -2; char *name = getlogin(); //Noncompliant if (name != NULL) { if (strcmp(name, user) == 0) { r = 0; } else r = -1; } return r; }
This example uses getlogin
to compare
the user name of the current user to the given user name. However,
getlogin
can return something other than the current user name
because a parallel process can change the string.
getlogin_r
One possible correction is to use getlogin_r
instead
of getlogin
. getlogin_r
is reentrant,
so you can trust the result.
#define _POSIX_C_SOURCE 199506L // use of getlogin_r #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <pwd.h> #include <string.h> #include <stdlib.h> volatile int rd = 1; enum { NAME_MAX_SIZE=64 }; int login_name_check(char *user) { int r; char name[NAME_MAX_SIZE]; if (getlogin_r(name, sizeof(name)) == 0) { if ((strlen(user) < sizeof(name)) && (strncmp(name, user, strlen(user)) == 0)) { r = 0; } else r = -1; } else r = -2; return r; }
Check Information
Category: Concurrency Issues |
Version History
Introduced in R2024a
See Also
External Websites
MATLAB Command
You clicked a link that corresponds to this MATLAB command:
Run the command by entering it in the MATLAB Command Window. Web browsers do not support MATLAB commands.
Select a Web Site
Choose a web site to get translated content where available and see local events and offers. Based on your location, we recommend that you select: .
You can also select a web site from the following list
How to Get Best Site Performance
Select the China site (in Chinese or English) for best site performance. Other MathWorks country sites are not optimized for visits from your location.
Americas
- América Latina (Español)
- Canada (English)
- United States (English)
Europe
- Belgium (English)
- Denmark (English)
- Deutschland (Deutsch)
- España (Español)
- Finland (English)
- France (Français)
- Ireland (English)
- Italia (Italiano)
- Luxembourg (English)
- Netherlands (English)
- Norway (English)
- Österreich (Deutsch)
- Portugal (English)
- Sweden (English)
- Switzerland
- United Kingdom (English)
Asia Pacific
- Australia (English)
- India (English)
- New Zealand (English)
- 中国
- 日本Japanese (日本語)
- 한국Korean (한국어)