Skip to content

Commit 4e7e937

Browse files
committed
better qlhelp, ms_mask only changes msg
1 parent f4598ef commit 4e7e937

File tree

8 files changed

+160
-19
lines changed

8 files changed

+160
-19
lines changed

cpp/src/security/AsyncUnsafeSignalHandler/AsyncUnsafeSignalHandler.qhelp

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,48 @@
77
This is a CodeQL query constructed to find signal handlers that are performing async unsafe operations.
88
</p>
99

10+
<p>
11+
Because a signal may be delivered at any moment, e.g., in the middle of a malloc, the handler shouldn't touch
12+
the program's internal state.
13+
</p>
14+
1015
<p>
1116
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.
17+
Any signal handler that performs operations that are not safe for asynchronous execution may be vulnerable.
18+
</p>
19+
20+
<p>
21+
Moreover, signal handlers may be re-entered. Handlers' logic should take that possibility into account.
22+
</p>
23+
24+
<p>
25+
If the issue is exploitable depends on attacker's ability to deliver the signal.
26+
Remote attacks may be limitted to some signals (like SIGALRM and SIGURG), while local attacks could use all signals.
1327
</p>
1428
</overview>
1529

1630
<recommendation>
1731
<p>
1832
Attempt to keep signal handlers as simple as possible. Only call async-safe functions from signal handlers.
1933
</p>
34+
<p>
35+
Block delivery of new signals inside signal handlers to prevent handler re-entrancy issues.
36+
</p>
2037
</recommendation>
2138

2239
<example>
2340
<sample src="AsyncUnsafeSignalHandler.c" />
2441

2542
<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.
43+
In this example, while both syntatically valid, a correct handler is defined in the <code>correct_handler</code> function and sets a flag.
44+
The function calls <code>log_message</code>, a async unsafe function, within the main loop.
2745
</p>
2846
</example>
2947

3048
<references>
49+
<li>
50+
<a href="https://lcamtuf.coredump.cx/signals.txt">Michal Zalewski, "Delivering Signals for Fun and Profit"</a>
51+
</li>
3152
<li>
3253
<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>
3354
</li>

cpp/src/security/AsyncUnsafeSignalHandler/AsyncUnsafeSignalHandler.ql

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ predicate isSignal(FunctionCall signalCall, Function signalHandler) {
5757
* struct sigaction sigactVar = ...
5858
* sigaction(SIGX, &sigactVar, ...)
5959
*/
60-
predicate isSigaction(FunctionCall sigactionCall, Function signalHandler) {
60+
predicate isSigaction(FunctionCall sigactionCall, Function signalHandler, boolean isReentrancyBlocked) {
6161
exists(Variable sigactVar, Struct sigactStruct, Field handlerField |
6262
sigactionCall.getTarget().getName() = "sigaction"
6363
and sigactionCall.getArgument(1).getAChild*() = sigactVar.getAnAccess()
@@ -84,25 +84,40 @@ predicate isSigaction(FunctionCall sigactionCall, Function signalHandler) {
8484
varDec.getVariable() = sigactVar
8585
and sigactVar.getInitializer().getExpr() = init
8686
and signalHandler.getAnAccess() = init.getAFieldExpr(handlerField).getAChild*()
87-
// ignore if signals are blocked via sa_mask
88-
and not exists(Field mask | mask.getName() = "sa_mask" and exists(init.getAFieldExpr(mask)))
87+
88+
// new signals are blocked via sa_mask
89+
and if exists(Field mask | mask.getName() = "sa_mask" and exists(init.getAFieldExpr(mask))) then
90+
isReentrancyBlocked = true
91+
else
92+
isReentrancyBlocked = false
8993
)
9094
)
91-
// ignore if signals are blocked via sa_mask
92-
and not exists(ValueFieldAccess dfa |
95+
96+
// new signals are blocked via sa_mask
97+
and if (isReentrancyBlocked = true or exists(ValueFieldAccess dfa |
9398
dfa.getQualifier+() = sigactVar.getAnAccess()
9499
and dfa.getTarget().getName() = "sa_mask"
95-
)
100+
)) then
101+
isReentrancyBlocked = true
102+
else
103+
isReentrancyBlocked = false
96104
)
97105
}
106+
107+
string fmtMsg(boolean isReentrancyBlocked) {
108+
(isReentrancyBlocked = true and result = "")
109+
or
110+
(isReentrancyBlocked = false and result = "Delivery of new signals may be not blocked when the handler executes. ")
111+
}
98112

99-
from FunctionCall fc, Function signalHandler
113+
from FunctionCall fc, Function signalHandler, boolean isReentrancyBlocked
100114
where
101115
isAsyncUnsafe(signalHandler)
102116
and (
103-
isSignal(fc, signalHandler)
117+
(isSignal(fc, signalHandler) and isReentrancyBlocked = false)
104118
or
105-
isSigaction(fc, signalHandler)
119+
isSigaction(fc, signalHandler, isReentrancyBlocked)
106120
)
107-
select signalHandler, "is a non-trivial signal handler that may be using not async-safe functions. Registered by $@", fc, fc.toString()
121+
select signalHandler, "is a non-trivial signal handler that uses not async-safe functions. " + fmtMsg(isReentrancyBlocked) +
122+
"Handler is registered by $@", fc, fc.toString()
108123

cpp/test/include/libc/signal.h

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,29 @@
33
#ifndef HEADER_SIGNAL_STUB_H
44
#define HEADER_SIGNAL_STUB_H
55

6-
#ifdef __cplusplus
7-
extern "C" {
8-
#endif
6+
97

108
#define SIGALRM 14
119
#define SIGSEGV 11
10+
#define SIGTERM 15
1211
#define SIG_ERR -1
1312
#define EXIT_FAILURE 2
14-
#define SA_SIGINFO 0x00000004
13+
#define SA_SIGINFO 4
1514

15+
{} // to silent error from codeql's extractor
1616
typedef void (*sighandler_t)(int);
1717
extern int signal(int, sighandler_t);
1818

1919
typedef struct {
2020
unsigned long sig[64];
2121
} sigset_t;
22+
2223
typedef struct {
2324
int si_signo; /* Signal number */
2425
int si_errno; /* An errno value */
2526
int si_code; /* Signal code */
2627
} siginfo_t;
28+
2729
struct sigaction {
2830
void (*sa_handler)(int);
2931
void (*sa_sigaction)(int, siginfo_t *, void *);
@@ -35,9 +37,6 @@ struct sigaction {
3537
extern int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
3638
extern int kill(int pid, int sig);
3739

38-
#ifdef __cplusplus
39-
}
40-
#endif
4140

4241
#endif
4342

cpp/test/include/libc/stdarg.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#ifndef USE_HEADERS
2+
3+
#ifndef HEADER_STDARG_STUB_H
4+
#define HEADER_STDARG_STUB_H
5+
6+
#ifdef __cplusplus
7+
extern "C" {
8+
#endif
9+
10+
typedef void *va_list;
11+
#define va_start(ap, parmN)
12+
#define va_end(ap)
13+
#define va_arg(ap, type) ((type)0)
14+
15+
#ifdef __cplusplus
16+
}
17+
#endif
18+
19+
#endif
20+
21+
#else // --- else USE_HEADERS
22+
23+
#include <stdarg.h>
24+
25+
#endif // --- end USE_HEADERS

cpp/test/include/libc/stdlib.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ long lrand48(void) {
1616
}
1717

1818
void _Exit(int);
19+
void exit(int);
1920

2021
void free(void*);
2122
void *malloc(unsigned long);

cpp/test/include/libc/string_stubs.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ extern int wprintf(const wchar_t * format, ...);
2424
extern wchar_t* wcscpy(wchar_t * s1, const wchar_t * s2);
2525
extern void perror(const char *s);
2626

27+
extern void openlog(const char*, int, int);
28+
extern void syslog(int, const char*, ...);
29+
extern void closelog(void)
30+
2731
#ifdef __cplusplus
2832
}
2933
#endif
@@ -37,6 +41,7 @@ extern void perror(const char *s);
3741
#include <stdio.h>
3842
#include <stdlib.h>
3943
#include <wchar.h>
44+
#include <syslog.h>
4045

4146
#define strcpy_s strcpy
4247
#define _mbsncat strncat

cpp/test/include/libc/unistd.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#ifndef USE_HEADERS
2+
3+
#ifndef HEADER_UNISTD_STUB_H
4+
#define HEADER_UNISTD_STUB_H
5+
6+
#ifdef __cplusplus
7+
extern "C" {
8+
#endif
9+
10+
void _exit(int);
11+
12+
#ifdef __cplusplus
13+
}
14+
#endif
15+
16+
#endif
17+
18+
#else // --- else USE_HEADERS
19+
20+
#include <unistd.h>
21+
22+
#endif // --- end USE_HEADERS

cpp/test/query-tests/security/AsyncUnsafeSignalHandler/AsyncUnsafeSignalHandler.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#include "../../../include/libc/string_stubs.h"
22
#include "../../../include/libc/signal.h"
33
#include "../../../include/libc/stdlib.h"
4+
#include "../../../include/libc/unistd.h"
5+
#include "../../../include/libc/stdarg.h"
46

57
void transitive_call() {
68
printf("UNSAFE");
@@ -36,6 +38,53 @@ void unsafe_handler3(int signal) {
3638
transitive_call3();
3739
}
3840

41+
// data flow case, regresshion-like
42+
#define sigdie(...) do_logging_and_die(__FILE__, NULL, __VA_ARGS__)
43+
44+
static void do_log(int level, const char *suffix, const char *fmt, va_list args) {
45+
int pri = 0;
46+
openlog(suffix, 1, 2);
47+
// the unsafe call from the handler
48+
syslog(pri, "%.500s", fmt);
49+
closelog();
50+
}
51+
52+
static const int level = 0;
53+
void logv(const char *file, const char *suffix, const char *fmt, va_list args) {
54+
char tmp[] = "sufsuf: ";
55+
suffix = tmp;
56+
if(!args) {
57+
suffix = &tmp[3];
58+
}
59+
do_log(level, suffix, fmt, args);
60+
}
61+
62+
void do_logging_and_die(const char *file, const char *suffix, const char *fmt, ...) {
63+
va_list args;
64+
va_start(args, fmt);
65+
logv(file, suffix, fmt, args);
66+
va_end(args);
67+
_exit(1);
68+
}
69+
70+
static void df_handler(int sig) {
71+
if (2*sig % 5 == 3) {
72+
_exit(1);
73+
}
74+
sigdie("some log %d", sig);
75+
}
76+
77+
sighandler_t register_signal(int signum, sighandler_t handler) {
78+
struct sigaction sa, osa;
79+
memset(&sa, 0, sizeof(sa));
80+
sa.sa_handler = handler;
81+
sigfillset(&sa.sa_mask);
82+
if (sigaction(signum, &sa, &osa) == -1) {
83+
return NULL;
84+
}
85+
return osa.sa_handler;
86+
}
87+
3988
int main() {
4089
// Register the safe signal handler
4190
if (signal(SIGALRM, safe_handler) == SIG_ERR) {
@@ -93,6 +142,10 @@ int main() {
93142
_Exit(EXIT_FAILURE);
94143
}
95144

145+
// indirect handler registration
146+
if(register_signal(SIGALRM, df_handler)) {
147+
_exit(EXIT_FAILURE);
148+
}
96149

97150
return 0;
98151
}

0 commit comments

Comments
 (0)