Skip to content

Commit 04cae50

Browse files
authored
Merge pull request #4 from trailofbits/dm/signal-handler
Tentative query for CVE-2024-6387
2 parents f6a32a9 + c1e5c1a commit 04cae50

File tree

11 files changed

+355
-7
lines changed

11 files changed

+355
-7
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ codeql database analyze database.db --format=sarif-latest --output=./tob.sarif -
3636
|[Memory leak related to custom allocator](./cpp/src/docs/crypto/CustomAllocatorLeak.md)|Finds memory leaks from custom allocated memory|warning|medium|
3737
|[Memory use after free related to custom allocator](./cpp/src/docs/crypto/CustomAllocatorUseAfterFree.md)|Finds use-after-frees related to custom allocators like `BN_new`|warning|medium|
3838
|[Missing OpenSSL engine initialization](./cpp/src/docs/crypto/MissingEngineInit.md)|Finds created OpenSSL engines that may not be properly initialized|warning|medium|
39-
|[Missing error handling](./cpp/src/docs/crypto/ErrorHandling.md)|Checks if returned error codes are properly checked|warning|high|
39+
|[Missing error handling](./cpp/src/docs/crypto/MissingErrorHandling.md)|Checks if returned error codes are properly checked|warning|high|
4040
|[Missing zeroization of potentially sensitive random BIGNUM](./cpp/src/docs/crypto/MissingZeroization.md)|Determines if random bignums are properly zeroized|warning|medium|
4141
|[Random buffer too small](./cpp/src/docs/crypto/RandomBufferTooSmall.md)|Finds buffer overflows in calls to CSPRNGs|warning|high|
4242
|[Use of legacy cryptographic algorithm](./cpp/src/docs/crypto/UseOfLegacyAlgorithm.md)|Detects potential instantiations of legacy cryptographic algorithms|warning|medium|
@@ -45,6 +45,7 @@ codeql database analyze database.db --format=sarif-latest --output=./tob.sarif -
4545

4646
| Name | Description | Severity | Precision |
4747
| --- | ----------- | :----: | :--------: |
48+
|[Async unsafe signal handler](./cpp/src/docs/security/AsyncUnsafeSignalHandler/AsyncUnsafeSignalHandler.md)|Async unsafe signal handler (like the one used in CVE-2024-6387)|warning|high|
4849
|[Invalid string size passed to string manipulation function](./cpp/src/docs/security/CStrnFinder/CStrnFinder.md)|Finds calls to functions that take as input a string and its size as separate arguments (e.g., `strncmp`, `strncat`, ...) and the size argument is wrong|error|low|
4950
|[Missing null terminator](./cpp/src/docs/security/NoNullTerminator/NoNullTerminator.md)|This query finds incorrectly initialized strings that are passed to functions expecting null-byte-terminated strings|error|high|
5051
|[Unsafe implicit integer conversion](./cpp/src/docs/security/UnsafeImplicitConversions/UnsafeImplicitConversions.md)|Finds implicit integer casts that may overflow or be truncated, with false positive reduction via Value Range Analysis|warning|low|
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Async unsafe signal handler
2+
This is a CodeQL query constructed to find signal handlers that are performing async unsafe operations.
3+
4+
The kernel defines a list of async-safe signal functions in its [man page](https://man7.org/linux/man-pages/man7/signal-safety.7.html). Any signal handler that performs operations that are not safe asynchronously may be vulnerable.
5+
6+
7+
## Recommendation
8+
Attempt to keep signal handlers as simple as possible. Only call async-safe functions from signal handlers.
9+
10+
11+
## Example
12+
13+
```c
14+
#include <signal.h>
15+
#include <stdio.h>
16+
#include <stdlib.h>
17+
18+
enum { MAXLINE = 1024 };
19+
volatile sig_atomic_t eflag = 0;
20+
char *info = NULL;
21+
22+
void log_message(void) {
23+
fputs(info, stderr);
24+
}
25+
26+
void correct_handler(int signum) {
27+
eflag = 1;
28+
}
29+
30+
int main(void) {
31+
if (signal(SIGINT, correct_handler) == SIG_ERR) {
32+
/* Handle error */
33+
}
34+
info = (char *)malloc(MAXLINE);
35+
if (info == NULL) {
36+
/* Handle error */
37+
}
38+
39+
while (!eflag) {
40+
/* Main loop program code */
41+
42+
log_message();
43+
44+
/* More program code */
45+
}
46+
47+
log_message();
48+
free(info);
49+
info = NULL;
50+
51+
return 0;
52+
}
53+
```
54+
In this example, while both syntatically valid, a correct handler is defined in the `correct_handler` function and sets a flag. The function calls `log_message`, a async unsafe function, within the main loop.
55+
56+
57+
## References
58+
* [SEI CERT C Coding Standard "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)
59+
* [regreSSHion: RCE in OpenSSH's server, on glibc-based Linux systems](https://www.qualys.com/2024/07/01/cve-2024-6387/regresshion.txt)
60+
* [signal-safety - async-signal-safe functions](https://man7.org/linux/man-pages/man7/signal-safety.7.html)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#include <signal.h>
2+
#include <stdio.h>
3+
#include <stdlib.h>
4+
5+
enum { MAXLINE = 1024 };
6+
volatile sig_atomic_t eflag = 0;
7+
char *info = NULL;
8+
9+
void log_message(void) {
10+
fputs(info, stderr);
11+
}
12+
13+
void correct_handler(int signum) {
14+
eflag = 1;
15+
}
16+
17+
int main(void) {
18+
if (signal(SIGINT, correct_handler) == SIG_ERR) {
19+
/* Handle error */
20+
}
21+
info = (char *)malloc(MAXLINE);
22+
if (info == NULL) {
23+
/* Handle error */
24+
}
25+
26+
while (!eflag) {
27+
/* Main loop program code */
28+
29+
log_message();
30+
31+
/* More program code */
32+
}
33+
34+
log_message();
35+
free(info);
36+
info = NULL;
37+
38+
return 0;
39+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>
7+
This is a CodeQL query constructed to find signal handlers that are performing async unsafe operations.
8+
</p>
9+
10+
<p>
11+
The kernel defines a list of async-safe signal functions in its <a href="https://man7.org/linux/man-pages/man7/signal-safety.7.html">man page</a>.
12+
Any signal handler that performs operations that are not safe asynchronously may be vulnerable.
13+
</p>
14+
</overview>
15+
16+
<recommendation>
17+
<p>
18+
Attempt to keep signal handlers as simple as possible. Only call async-safe functions from signal handlers.
19+
</p>
20+
</recommendation>
21+
22+
<example>
23+
<sample src="AsyncUnsafeSignalHandler.c" />
24+
25+
<p>
26+
In this example, while both syntatically valid, a correct handler is defined in the <code>correct_handler</code> function and sets a flag. The function calls <code>log_message</code>, a async unsafe function, within the main loop.
27+
</p>
28+
</example>
29+
30+
<references>
31+
<li>
32+
<a href="https://wiki.sei.cmu.edu/confluence/display/c/SIG30-C.+Call+only+asynchronous-safe+functions+within+signal+handlers">SEI CERT C Coding Standard "SIG30-C. Call only asynchronous-safe functions within signal handlers"</a>
33+
</li>
34+
<li>
35+
<a href="https://www.qualys.com/2024/07/01/cve-2024-6387/regresshion.txt">regreSSHion: RCE in OpenSSH's server, on glibc-based Linux systems</a>
36+
</li>
37+
<li>
38+
<a href="https://man7.org/linux/man-pages/man7/signal-safety.7.html">signal-safety - async-signal-safe functions</a>
39+
</li>
40+
</references>
41+
42+
</qhelp>
43+
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* @name Async unsafe signal handler
3+
* @id tob/cpp/async-unsafe-signal-handler
4+
* @description Async unsafe signal handler (like the one used in CVE-2024-6387)
5+
* @kind problem
6+
* @tags security
7+
* @problem.severity warning
8+
* @precision high
9+
* @security-severity 7.0
10+
* @group security
11+
*/
12+
13+
import cpp
14+
15+
/* List from https://man7.org/linux/man-pages/man3/stdio.3.html */
16+
class StdioFunction extends Function {
17+
StdioFunction() {
18+
this.getName() in [
19+
"fopen", "freopen", "fflush", "fclose", "fread", "fwrite", "fgetc", "fgets", "fputc",
20+
"fputs", "getc", "getchar", "gets", "putc", "putchar", "puts", "ungetc", "fprintf",
21+
"fscanf", "printf", "scanf", "sprintf", "sscanf", "vprintf", "vfprintf", "vsprintf",
22+
"fgetpos", "fsetpos", "ftell", "fseek", "rewind", "clearerr", "feof", "ferror", "perror",
23+
"setbuf", "setvbuf", "remove", "rename", "tmpfile", "tmpnam",
24+
]
25+
}
26+
}
27+
28+
/* List from https://man7.org/linux/man-pages/man3/syslog.3.html */
29+
class SyslogFunction extends Function {
30+
SyslogFunction() { this.getName() in ["syslog", "vsyslog",] }
31+
}
32+
33+
/* List from https://man7.org/linux/man-pages/man0/stdlib.h.0p.html */
34+
class StdlibFunction extends Function {
35+
StdlibFunction() { this.getName() in ["malloc", "calloc", "realloc", "free",] }
36+
}
37+
38+
predicate isAsyncUnsafe(Function signalHandler) {
39+
exists(Function f |
40+
signalHandler.calls+(f) and
41+
(
42+
f instanceof StdioFunction or
43+
f instanceof SyslogFunction or
44+
f instanceof StdlibFunction
45+
)
46+
)
47+
}
48+
49+
predicate isSignalHandlerField(FieldAccess fa) {
50+
fa.getTarget().getName() in ["__sa_handler", "__sa_sigaction", "sa_handler", "sa_sigaction"]
51+
}
52+
53+
54+
from FunctionCall fc, Function signalHandler, FieldAccess fa
55+
where
56+
fc.getTarget().getName() = "signal" and
57+
signalHandler.getName() = fc.getArgument(1).toString() and
58+
isAsyncUnsafe(signalHandler)
59+
or
60+
fc.getTarget().getName() = "sigaction" and
61+
isSignalHandlerField(fa) and
62+
signalHandler = fa.getTarget().getAnAssignedValue().(AddressOfExpr).getAddressable() and
63+
isAsyncUnsafe(signalHandler)
64+
select signalHandler,
65+
"is a non-trivial signal handler that may be using functions that are not async-safe."

cpp/test/include/libc/signal.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#ifndef USE_HEADERS
2+
3+
#ifndef HEADER_SIGNAL_STUB_H
4+
#define HEADER_SIGNAL_STUB_H
5+
6+
#ifdef __cplusplus
7+
extern "C" {
8+
#endif
9+
10+
#define SIGALRM 14
11+
#define SIGSEGV 11
12+
#define SIG_ERR -1
13+
#define EXIT_FAILURE 2
14+
#define SA_SIGINFO 0x00000004
15+
16+
typedef void (*sighandler_t)(int);
17+
extern int signal(int, sighandler_t);
18+
19+
typedef struct {
20+
unsigned long sig[64];
21+
} sigset_t;
22+
typedef struct {
23+
int si_signo; /* Signal number */
24+
int si_errno; /* An errno value */
25+
int si_code; /* Signal code */
26+
} siginfo_t;
27+
struct sigaction {
28+
void (*sa_handler)(int);
29+
void (*sa_sigaction)(int, siginfo_t *, void *);
30+
sigset_t sa_mask;
31+
int sa_flags;
32+
void (*sa_restorer)(void);
33+
};
34+
35+
extern int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
36+
extern int kill(int pid, int sig);
37+
38+
#ifdef __cplusplus
39+
}
40+
#endif
41+
42+
#endif
43+
44+
#else // --- else USE_HEADERS
45+
46+
#include <signal.h>
47+
48+
#endif // --- end USE_HEADERS

cpp/test/include/libc/stdlib.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
#ifndef HEADER_STDLIB_STUB_H
44
#define HEADER_STDLIB_STUB_H
55

6-
# ifdef __cplusplus
6+
#ifdef __cplusplus
77
extern "C" {
8-
# endif
8+
#endif
99

1010
int rand(void) {
1111
return 42;
@@ -15,9 +15,14 @@ long lrand48(void) {
1515
return 42;
1616
}
1717

18-
# ifdef __cplusplus
18+
void _Exit(int);
19+
20+
void free(void*);
21+
void *malloc(unsigned long);
22+
23+
#ifdef __cplusplus
1924
}
20-
# endif
25+
#endif
2126

2227
#endif
2328

cpp/test/include/libc/string_stubs.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
#ifndef USE_HEADERS
22

3+
#ifndef HEADER_STRINGSTUB_STUB_H
4+
#define HEADER_STRINGSTUB_STUB_H
5+
6+
#ifdef __cplusplus
7+
extern "C" {
8+
#endif
9+
310
#ifndef NULL
411
#define NULL 0
512
#endif
@@ -10,11 +17,18 @@ extern int _mbsncat(char* dst, char* src, int count);
1017
extern int _mbsncmp(char* dst, char* src, int count);
1118
extern int _memicmp(char* a, char* b, long count);
1219
extern int _mbsnbcmp(char* a, char* b, int count);
13-
extern void *printf(const char * format, ...);
14-
extern int strlen(const char *s);
20+
extern int printf(const char * format, ...);
21+
extern unsigned long strlen(const char *s);
1522
extern char* strcpy(char * dst, const char * src);
1623
extern int wprintf(const wchar_t * format, ...);
1724
extern wchar_t* wcscpy(wchar_t * s1, const wchar_t * s2);
25+
extern void perror(const char *s);
26+
27+
#ifdef __cplusplus
28+
}
29+
#endif
30+
31+
#endif
1832

1933
#else
2034

0 commit comments

Comments
 (0)