-
Notifications
You must be signed in to change notification settings - Fork 242
Expand file tree
/
Copy pathpost-mortem.c
More file actions
169 lines (138 loc) · 4.17 KB
/
post-mortem.c
File metadata and controls
169 lines (138 loc) · 4.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include "pids.h"
#include "shm.h"
#include "taint.h"
#include "trinity.h"
#include "syscall.h"
#include "post-mortem.h"
#include "utils.h"
struct child_sort_entry {
unsigned int idx;
struct timespec tp;
};
static int cmp_timespec(const void *a, const void *b)
{
const struct child_sort_entry *ea = a;
const struct child_sort_entry *eb = b;
if (ea->tp.tv_sec != eb->tp.tv_sec)
return (ea->tp.tv_sec < eb->tp.tv_sec) ? -1 : 1;
if (ea->tp.tv_nsec != eb->tp.tv_nsec)
return (ea->tp.tv_nsec < eb->tp.tv_nsec) ? -1 : 1;
return 0;
}
static bool ts_before(const struct timespec *a, const struct timespec *b)
{
if (a->tv_sec != b->tv_sec)
return a->tv_sec < b->tv_sec;
return a->tv_nsec < b->tv_nsec;
}
static void dump_syscall_rec(FILE *fp, struct syscallrecord *rec)
{
switch (rec->state) {
case UNKNOWN:
/* new child, so nothing to dump. */
break;
case PREP:
/* haven't finished setting up, so don't dump. */
break;
case BEFORE:
fprintf(fp, "%.*s\n", PREBUFFER_LEN, rec->prebuffer);
break;
case AFTER:
case GOING_AWAY:
fprintf(fp, "%.*s%.*s\n", PREBUFFER_LEN, rec->prebuffer,
POSTBUFFER_LEN, rec->postbuffer);
break;
}
}
static void dump_syscall_records(const struct timespec *taint_tp,
struct syscallrecord *snapshots)
{
FILE *fd;
unsigned int i, nr_active;
struct child_sort_entry *entries;
bool taint_marked = false;
fd = fopen("trinity-post-mortem.log", "w");
if (!fd) {
outputerr("Failed to write post mortem log (%s)\n", strerror(errno));
return;
}
/* If snapshot allocation failed, fall back to live records
* (racy but better than no dump at all). */
if (snapshots == NULL) {
outputerr("post-mortem: snapshot alloc failed, using live records\n");
}
entries = malloc(max_children * sizeof(*entries));
if (!entries) {
outputerr("Failed to allocate sort buffer\n");
fclose(fd);
return;
}
nr_active = 0;
for_each_child(i) {
struct syscallrecord *rec;
rec = snapshots ? &snapshots[i] : &shm->children[i]->syscall;
if (rec->state == UNKNOWN || rec->state == PREP)
continue;
entries[nr_active].idx = i;
entries[nr_active].tp = rec->tp;
nr_active++;
}
qsort(entries, nr_active, sizeof(*entries), cmp_timespec);
for (i = 0; i < nr_active; i++) {
struct syscallrecord *rec;
rec = snapshots ? &snapshots[entries[i].idx]
: &shm->children[entries[i].idx]->syscall;
if (!taint_marked && ts_before(taint_tp, &entries[i].tp)) {
fprintf(fd, "--- taint detected at %ld.%09ld ---\n",
(long) taint_tp->tv_sec, taint_tp->tv_nsec);
taint_marked = true;
}
fprintf(fd, "[child %u @ %ld.%09ld] ", entries[i].idx,
(long) rec->tp.tv_sec, rec->tp.tv_nsec);
dump_syscall_rec(fd, rec);
fprintf(fd, "\n");
}
if (!taint_marked) {
fprintf(fd, "--- taint detected at %ld.%09ld (after all recorded syscalls) ---\n",
(long) taint_tp->tv_sec, taint_tp->tv_nsec);
}
free(entries);
fclose(fd);
}
void tainted_postmortem(void)
{
int taint = get_taint();
struct timespec taint_tp;
unsigned int i;
struct syscallrecord *snapshots;
__atomic_store_n(&shm->postmortem_in_progress, true, __ATOMIC_RELAXED);
clock_gettime(CLOCK_MONOTONIC, &taint_tp);
/* Snapshot all syscall records BEFORE calling panic().
* panic() sets exit_reason which causes children to exit their
* main loops. Children still in-flight can modify their
* syscallrecord (state, prebuffer, postbuffer) between the
* syscall return and the exit check. Snapshotting first gives
* us a consistent view for the dump. */
snapshots = malloc(max_children * sizeof(struct syscallrecord));
if (snapshots != NULL) {
for_each_child(i)
snapshots[i] = shm->children[i]->syscall;
}
panic(EXIT_KERNEL_TAINTED);
output(0, "kernel became tainted! (%d/%d) Last seed was %u\n",
taint, kernel_taint_initial, shm->seed);
openlog("trinity", LOG_CONS|LOG_PERROR, LOG_USER);
syslog(LOG_CRIT, "Detected kernel tainting. Last seed was %u\n", shm->seed);
closelog();
dump_syscall_records(&taint_tp, snapshots);
free(snapshots);
__atomic_store_n(&shm->postmortem_in_progress, false, __ATOMIC_RELAXED);
}