Skip to content

Commit 1c3ad1c

Browse files
authored
Merge pull request #258 from mbaluda-org/SignalHandlers
Package SignalHandlers
2 parents 8a9c3be + c098321 commit 1c3ad1c

28 files changed

+1525
-30
lines changed

c/cert/src/rules/ERR32-C/DoNotRelyOnIndeterminateValuesOfErrno.ql

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,9 @@
1313
import cpp
1414
import codingstandards.c.cert
1515
import codingstandards.c.Errno
16+
import codingstandards.c.Signal
1617
import semmle.code.cpp.controlflow.Guards
1718

18-
/**
19-
* A call to function `signal`
20-
*/
21-
class SignalCall extends FunctionCall {
22-
SignalCall() { this.getTarget().hasGlobalName("signal") }
23-
}
24-
25-
/**
26-
* A call to `abort` or `_Exit`
27-
*/
28-
class AbortCall extends FunctionCall {
29-
AbortCall() { this.getTarget().hasGlobalName(["abort", "_Exit"]) }
30-
}
3119

3220
/**
3321
* A check on `signal` call return value
@@ -47,22 +35,16 @@ class SignalCheckOperation extends EqualityOperation, GuardCondition {
4735
)
4836
}
4937

50-
BasicBlock getCheckedSuccessor() {
51-
result != errorSuccessor and result = this.getASuccessor()
52-
}
38+
BasicBlock getCheckedSuccessor() { result != errorSuccessor and result = this.getASuccessor() }
5339

5440
BasicBlock getErrorSuccessor() { result = errorSuccessor }
5541
}
5642

5743
/**
5844
* Models signal handlers that call signal() and return
5945
*/
60-
class SignalCallingHandler extends Function {
61-
SignalCall registration;
62-
46+
class SignalCallingHandler extends SignalHandler {
6347
SignalCallingHandler() {
64-
// is a signal handler
65-
this = registration.getArgument(1).(FunctionAccess).getTarget() and
6648
// calls signal() on the handled signal
6749
exists(SignalCall sCall |
6850
sCall.getEnclosingFunction() = this and
@@ -75,8 +57,6 @@ class SignalCallingHandler extends Function {
7557
)
7658
)
7759
}
78-
79-
SignalCall getCall() { result = registration }
8060
}
8161

8262
/**
@@ -100,7 +80,7 @@ where
10080
not isExcluded(errno, Contracts5Package::doNotRelyOnIndeterminateValuesOfErrnoQuery()) and
10181
exists(SignalCallingHandler handler |
10282
// errno read after the handler returns
103-
handler.getCall() = signal
83+
handler.getRegistration() = signal
10484
or
10585
// errno read inside the handler
10686
signal.getEnclosingFunction() = handler

c/cert/src/rules/SIG30-C/CallOnlyAsyncSafeFunctionsWithinSignalHandlers.md

Lines changed: 377 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/**
2+
* @id c/cert/call-only-async-safe-functions-within-signal-handlers
3+
* @name SIG30-C: Call only asynchronous-safe functions within signal handlers
4+
* @description Call only asynchronous-safe functions within signal handlers.
5+
* @kind problem
6+
* @precision very-high
7+
* @problem.severity error
8+
* @tags external/cert/id/sig30-c
9+
* correctness
10+
* security
11+
* external/cert/obligation/rule
12+
*/
13+
14+
import cpp
15+
import codingstandards.c.cert
16+
import codingstandards.c.Signal
17+
import semmle.code.cpp.dataflow.DataFlow
18+
19+
/**
20+
* Does not access an external variable except
21+
* to assign a value to a volatile static variable of `sig_atomic_t` type
22+
*/
23+
class AsyncSafeVariableAccess extends VariableAccess {
24+
AsyncSafeVariableAccess() {
25+
this.getTarget() instanceof StackVariable
26+
or
27+
this.getTarget().(StaticStorageDurationVariable).getType().(SigAtomicType).isVolatile() and
28+
this.isModified()
29+
}
30+
}
31+
32+
abstract class AsyncSafeFunction extends Function { }
33+
34+
/**
35+
* C standard library ayncronous-safe functions
36+
*/
37+
class CAsyncSafeFunction extends AsyncSafeFunction {
38+
//tion, or the signal function with the first argument equal to the signal number corresponding to the signal that caused the invocation of the handler
39+
CAsyncSafeFunction() { this.hasGlobalName(["abort", "_Exit", "quick_exit", "signal"]) }
40+
}
41+
42+
/**
43+
* POSIX defined ayncronous-safe functions
44+
*/
45+
class PosixAsyncSafeFunction extends AsyncSafeFunction {
46+
PosixAsyncSafeFunction() {
47+
this.hasGlobalName([
48+
"_Exit", "_exit", "abort", "accept", "access", "aio_error", "aio_return", "aio_suspend",
49+
"alarm", "bind", "cfgetispeed", "cfgetospeed", "cfsetispeed", "cfsetospeed", "chdir",
50+
"chmod", "chown", "clock_gettime", "close", "connect", "creat", "dup", "dup2", "execl",
51+
"execle", "execv", "execve", "faccessat", "fchdir", "fchmod", "fchmodat", "fchown",
52+
"fchownat", "fcntl", "fdatasync", "fexecve", "fork", "fstat", "fstatat", "fsync",
53+
"ftruncate", "futimens", "getegid", "geteuid", "getgid", "getgroups", "getpeername",
54+
"getpgrp", "getpid", "getppid", "getsockname", "getsockopt", "getuid", "kill", "link",
55+
"linkat", "listen", "lseek", "lstat", "mkdir", "mkdirat", "mkfifo", "mkfifoat", "mknod",
56+
"mknodat", "open", "openat", "pause", "pipe", "poll", "posix_trace_event", "pselect",
57+
"pthread_kill", "pthread_self", "pthread_sigmask", "raise", "read", "readlink",
58+
"readlinkat", "recv", "recvfrom", "recvmsg", "rename", "renameat", "rmdir", "select",
59+
"sem_post", "send", "sendmsg", "sendto", "setgid", "setpgid", "setsid", "setsockopt",
60+
"setuid", "shutdown", "sigaction", "sigaddset", "sigdelset", "sigemptyset", "sigfillset",
61+
"sigismember", "signal", "sigpause", "sigpending", "sigprocmask", "sigqueue", "sigset",
62+
"sigsuspend", "sleep", "sockatmark", "socket", "socketpair", "stat", "symlink", "symlinkat",
63+
"tcdrain", "tcflow", "tcflush", "tcgetattr", "tcgetpgrp", "tcsendbreak", "tcsetattr",
64+
"tcsetpgrp", "time", "timer_getoverrun", "timer_gettime", "timer_settime", "times", "umask",
65+
"uname", "unlink", "unlinkat", "utime", "utimensat", "utimes", "wait", "waitpid", "write"
66+
])
67+
}
68+
}
69+
70+
/**
71+
* Application defined ayncronous-safe functions
72+
*/
73+
class ApplicationAsyncSafeFunction extends AsyncSafeFunction {
74+
pragma[nomagic]
75+
ApplicationAsyncSafeFunction() {
76+
// Application-defined
77+
this.hasDefinition() and
78+
exists(this.getFile().getRelativePath()) and
79+
// Only references async-safe variables
80+
not exists(VariableAccess va |
81+
this = va.getEnclosingFunction() and not va instanceof AsyncSafeVariableAccess
82+
) and
83+
// Only calls async-safe functions
84+
not exists(Function f | this.calls(f) and not f instanceof AsyncSafeFunction)
85+
}
86+
}
87+
88+
/**
89+
* Call to function `raise` within a signal handler with mismatching signals
90+
* ```
91+
* void int_handler(int signum) {
92+
* raise(SIGTERM);
93+
* }
94+
* int main(void) {
95+
* signal(SIGINT, int_handler);
96+
* }
97+
* ```
98+
*/
99+
class AsyncUnsafeRaiseCall extends FunctionCall {
100+
AsyncUnsafeRaiseCall() {
101+
this.getTarget().hasGlobalName("raise") and
102+
exists(SignalHandler handler |
103+
handler = this.getEnclosingFunction() and
104+
not handler.getRegistration().getArgument(0).getValue() = this.getArgument(0).getValue() and
105+
not DataFlow::localFlow(DataFlow::parameterNode(handler.getParameter(0)),
106+
DataFlow::exprNode(this.getArgument(0)))
107+
)
108+
}
109+
}
110+
111+
from FunctionCall fc, SignalHandler handler
112+
where
113+
not isExcluded(fc, SignalHandlersPackage::callOnlyAsyncSafeFunctionsWithinSignalHandlersQuery()) and
114+
handler = fc.getEnclosingFunction() and
115+
(
116+
not fc.getTarget() instanceof AsyncSafeFunction
117+
or
118+
fc instanceof AsyncUnsafeRaiseCall
119+
)
120+
select fc, "Asyncronous-unsafe function calls within a $@ can lead to undefined behavior.",
121+
handler.getRegistration(), "signal handler"
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
# SIG31-C: Do not access shared objects in signal handlers
2+
3+
This query implements the CERT-C rule SIG31-C:
4+
5+
> Do not access shared objects in signal handlers
6+
7+
8+
## Description
9+
10+
Accessing or modifying shared objects in signal handlers can 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](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See [undefined behavior 131](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_131).)
11+
12+
The need for the `volatile` keyword is described in [DCL22-C. Use volatile for data that cannot be cached](https://wiki.sei.cmu.edu/confluence/display/c/DCL22-C.+Use+volatile+for+data+that+cannot+be+cached).
13+
14+
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](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation-definedbehavior), 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>`.
15+
16+
According to the C99 Rationale \[[C99 Rationale 2003](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-C992003)\], other than calling a limited, prescribed set of library functions,
17+
18+
> the C89 Committee concluded that about the only thing a [strictly conforming](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-strictlyconforming) 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.
19+
20+
21+
However, this issue was discussed at the April 2008 meeting of ISO/IEC WG14, and it was agreed that there are no known [implementations](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation) in which it would be an error to read a value from a `volatile sig_atomic_t` variable, and the original intent of the committee was that both reading and writing variables of `volatile sig_atomic_t` would be strictly conforming.
22+
23+
The signal handler may also call a handful of functions, including `abort().` (See [SIG30-C. Call only asynchronous-safe functions within signal handlers](https://wiki.sei.cmu.edu/confluence/display/c/SIG30-C.+Call+only+asynchronous-safe+functions+within+signal+handlers) for more information.)
24+
25+
## Noncompliant Code Example
26+
27+
In this noncompliant code example, `err_msg` is updated to indicate that the `SIGINT` signal was delivered. The `err_msg` variable is a character pointer and not a variable of type `volatile sig_atomic_t`.
28+
29+
```cpp
30+
#include <signal.h>
31+
#include <stdlib.h>
32+
#include <string.h>
33+
34+
enum { MAX_MSG_SIZE = 24 };
35+
char *err_msg;
36+
37+
void handler(int signum) {
38+
strcpy(err_msg, "SIGINT encountered.");
39+
}
40+
41+
int main(void) {
42+
signal(SIGINT, handler);
43+
44+
err_msg = (char *)malloc(MAX_MSG_SIZE);
45+
if (err_msg == NULL) {
46+
/* Handle error */
47+
}
48+
strcpy(err_msg, "No errors yet.");
49+
/* Main code loop */
50+
return 0;
51+
}
52+
53+
```
54+
55+
## Compliant Solution (Writing volatile sig_atomic_t)
56+
57+
For maximum portability, signal handlers should only unconditionally set a variable of type `volatile sig_atomic_t` and return, as in this compliant solution:
58+
59+
```cpp
60+
#include <signal.h>
61+
#include <stdlib.h>
62+
#include <string.h>
63+
64+
enum { MAX_MSG_SIZE = 24 };
65+
volatile sig_atomic_t e_flag = 0;
66+
67+
void handler(int signum) {
68+
e_flag = 1;
69+
}
70+
71+
int main(void) {
72+
char *err_msg = (char *)malloc(MAX_MSG_SIZE);
73+
if (err_msg == NULL) {
74+
/* Handle error */
75+
}
76+
77+
signal(SIGINT, handler);
78+
strcpy(err_msg, "No errors yet.");
79+
/* Main code loop */
80+
if (e_flag) {
81+
strcpy(err_msg, "SIGINT received.");
82+
}
83+
return 0;
84+
}
85+
86+
```
87+
88+
## Compliant Solution (Lock-Free Atomic Access)
89+
90+
Signal handlers can refer to objects with static or thread storage durations that are lock-free atomic objects, as in this compliant solution:
91+
92+
```cpp
93+
#include <signal.h>
94+
#include <stdlib.h>
95+
#include <string.h>
96+
#include <stdatomic.h>
97+
98+
#ifdef __STDC_NO_ATOMICS__
99+
#error "Atomics are not supported"
100+
#elif ATOMIC_INT_LOCK_FREE == 0
101+
#error "int is never lock-free"
102+
#endif
103+
104+
atomic_int e_flag = ATOMIC_VAR_INIT(0);
105+
106+
void handler(int signum) {
107+
e_flag = 1;
108+
}
109+
110+
int main(void) {
111+
enum { MAX_MSG_SIZE = 24 };
112+
char err_msg[MAX_MSG_SIZE];
113+
#if ATOMIC_INT_LOCK_FREE == 1
114+
if (!atomic_is_lock_free(&e_flag)) {
115+
return EXIT_FAILURE;
116+
}
117+
#endif
118+
if (signal(SIGINT, handler) == SIG_ERR) {
119+
return EXIT_FAILURE;
120+
}
121+
strcpy(err_msg, "No errors yet.");
122+
/* Main code loop */
123+
if (e_flag) {
124+
strcpy(err_msg, "SIGINT received.");
125+
}
126+
return EXIT_SUCCESS;
127+
}
128+
129+
```
130+
131+
## Exceptions
132+
133+
**SIG31-C-EX1:** The C Standard, 7.14.1.1 paragraph 5 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO%2FIEC9899-2011)\], 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](https://wiki.sei.cmu.edu/confluence/display/c/SIG31-C.+Do+not+access+shared+objects+in+signal+handlers#).)
134+
135+
## Risk Assessment
136+
137+
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](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Zalewski01)\] provides some examples of [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) that can result from violating this and other signal-handling rules.
138+
139+
<table> <tbody> <tr> <th> Rule </th> <th> Severity </th> <th> Likelihood </th> <th> Remediation Cost </th> <th> Priority </th> <th> Level </th> </tr> <tr> <td> SIG31-C </td> <td> High </td> <td> Likely </td> <td> High </td> <td> <strong>P9</strong> </td> <td> <strong>L2</strong> </td> </tr> </tbody> </table>
140+
141+
142+
## Automated Detection
143+
144+
<table> <tbody> <tr> <th> Tool </th> <th> Version </th> <th> Checker </th> <th> Description </th> </tr> <tr> <td> <a> Astrée </a> </td> <td> 22.04 </td> <td> <strong>signal-handler-shared-access</strong> </td> <td> Partially checked </td> </tr> <tr> <td> <a> Axivion Bauhaus Suite </a> </td> <td> 7.2.0 </td> <td> <strong>CertC-SIG31</strong> </td> <td> </td> </tr> <tr> <td> <a> CodeSonar </a> </td> <td> 7.2p0 </td> <td> <strong>CONCURRENCY.DATARACE</strong> </td> <td> Data race </td> </tr> <tr> <td> <a> Compass/ROSE </a> </td> <td> </td> <td> </td> <td> Can detect violations of this rule for single-file programs </td> </tr> <tr> <td> <a> Helix QAC </a> </td> <td> 2022.4 </td> <td> <strong>C2029, C2030</strong> <strong>C++3854, C++3855</strong> </td> <td> </td> </tr> <tr> <td> <a> LDRA tool suite </a> </td> <td> 9.7.1 </td> <td> <strong>87 D</strong> </td> <td> Fully implemented </td> </tr> <tr> <td> <a> Parasoft C/C++test </a> </td> <td> 2022.2 </td> <td> <strong>CERT_C-SIG31-a</strong> </td> <td> Properly define signal handlers </td> </tr> <tr> <td> <a> PC-lint Plus </a> </td> <td> 1.4 </td> <td> <strong>2765</strong> </td> <td> Fully supported </td> </tr> <tr> <td> <a> Polyspace Bug Finder </a> </td> <td> R2022b </td> <td> <a> CERT C: Rule SIG31-C </a> </td> <td> Checks for shared data access within signal handler (rule partially covered) </td> </tr> <tr> <td> <a> PRQA QA-C </a> </td> <td> 9.7 </td> <td> <strong>2029, 2030</strong> </td> <td> </td> </tr> <tr> <td> <a> RuleChecker </a> </td> <td> 22.04 </td> <td> <strong>signal-handler-shared-access</strong> </td> <td> Partially checked </td> </tr> </tbody> </table>
145+
146+
147+
## Related Vulnerabilities
148+
149+
Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+SIG31-C).
150+
151+
## Related Guidelines
152+
153+
[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
154+
155+
<table> <tbody> <tr> <th> Taxonomy </th> <th> Taxonomy item </th> <th> Relationship </th> </tr> <tr> <td> <a> ISO/IEC TS 17961:2013 </a> </td> <td> Accessing shared objects in signal handlers \[accsig\] </td> <td> Prior to 2018-01-12: CERT: Unspecified Relationship </td> </tr> <tr> <td> <a> CWE 2.11 </a> </td> <td> <a> CWE-662 </a> , Improper Synchronization </td> <td> 2017-07-10: CERT: Rule subset of CWE </td> </tr> <tr> <td> <a> CWE 2.11 </a> </td> <td> <a> CWE-828 </a> , Signal Handler with Functionality that is not Asynchronous-Safe </td> <td> 2017-10-30:MITRE:Unspecified Relationship 2018-10-19:CERT: Rule subset of CWE </td> </tr> </tbody> </table>
156+
157+
158+
## CERT-CWE Mapping Notes
159+
160+
[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes
161+
162+
**CWE-662 and SIG31-C**
163+
164+
CWE-662 = Union( SIG31-C, list) where list =
165+
166+
* Improper synchronization of shared objects between threads
167+
* Improper synchronization of files between programs (enabling TOCTOU race conditions
168+
**CWE-828 and SIG31-C**
169+
170+
CWE-828 = SIG31-C + non-async-safe things besides shared objects.
171+
172+
## Bibliography
173+
174+
<table> <tbody> <tr> <td> \[ <a> C99 Rationale 2003 </a> \] </td> <td> 5.2.3, "Signals and Interrupts" </td> </tr> <tr> <td> \[ <a> ISO/IEC 9899:2011 </a> \] </td> <td> Subclause 7.14.1.1, "The <code>signal</code> Function" </td> </tr> <tr> <td> \[ <a> Zalewski 2001 </a> \] </td> <td> </td> </tr> </tbody> </table>
175+
176+
177+
## Implementation notes
178+
179+
The implementation does not verify the correct usage of `atomic_is_lock_free`.
180+
181+
## References
182+
183+
* CERT-C: [SIG31-C: Do not access shared objects in signal handlers](https://wiki.sei.cmu.edu/confluence/display/c)

0 commit comments

Comments
 (0)