-
Notifications
You must be signed in to change notification settings - Fork 242
Expand file tree
/
Copy pathcmp_hints.c
More file actions
158 lines (128 loc) · 3.81 KB
/
cmp_hints.c
File metadata and controls
158 lines (128 loc) · 3.81 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
/*
* KCOV comparison operand collection and hint pool management.
*
* Parses KCOV_TRACE_CMP trace buffers to extract constants that the
* kernel compared syscall-derived values against. These constants
* are stored in per-syscall hint pools and used during argument
* generation to produce values more likely to pass kernel validation.
*
* Buffer format (each record is 4 x u64):
* [0] type - KCOV_CMP_CONST | KCOV_CMP_SIZE(n)
* [1] arg1 - first comparison operand
* [2] arg2 - second comparison operand
* [3] ip - instruction pointer (unused here)
*/
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include "cmp_hints.h"
#include "kcov.h"
#include "random.h"
#include "syscall.h"
#include "trinity.h"
#include "utils.h"
/* From uapi/linux/kcov.h */
#define KCOV_CMP_CONST (1U << 0)
/* Words per comparison record in the trace buffer. */
#define WORDS_PER_CMP 4
struct cmp_hints_shared *cmp_hints_shm = NULL;
void cmp_hints_init(void)
{
if (kcov_shm == NULL)
return;
cmp_hints_shm = alloc_shared(sizeof(struct cmp_hints_shared));
memset(cmp_hints_shm, 0, sizeof(struct cmp_hints_shared));
output(0, "KCOV: CMP hint pool allocated (%lu KB)\n",
(unsigned long) sizeof(struct cmp_hints_shared) / 1024);
}
/*
* Filter out uninteresting comparison values.
* Skip 0, 1, -1, and very small values that are likely to be
* boolean/flag checks rather than meaningful constants.
*/
static bool interesting_value(unsigned long val)
{
if (val == 0 || val == 1)
return false;
if (val == (unsigned long) -1)
return false;
if (val < 4)
return false;
return true;
}
/*
* Add a value to the hint pool for a given syscall number.
* Deduplicates and overwrites a random slot when full.
*/
static void pool_add(struct cmp_hint_pool *pool, unsigned long val)
{
unsigned int i;
unsigned int spins = 0;
while (__atomic_test_and_set(&pool->lock, __ATOMIC_ACQUIRE)) {
if (++spins > 1000000) {
pid_t owner = pool->locker_pid;
if (owner != 0 && kill(owner, 0) == -1 && errno == ESRCH)
__atomic_clear(&pool->lock, __ATOMIC_RELEASE);
spins = 0;
}
}
pool->locker_pid = getpid();
for (i = 0; i < pool->count && i < CMP_HINTS_PER_SYSCALL; i++) {
if (pool->values[i] == val)
goto out;
}
if (pool->count < CMP_HINTS_PER_SYSCALL) {
pool->values[pool->count] = val;
pool->count++;
} else {
pool->values[rand() % CMP_HINTS_PER_SYSCALL] = val;
}
out:
pool->locker_pid = 0;
__atomic_clear(&pool->lock, __ATOMIC_RELEASE);
}
void cmp_hints_collect(unsigned long *trace_buf, unsigned int nr)
{
unsigned long count;
unsigned long i;
struct cmp_hint_pool *pool;
if (cmp_hints_shm == NULL || trace_buf == NULL)
return;
if (nr >= MAX_NR_SYSCALL)
return;
pool = &cmp_hints_shm->pools[nr];
count = __atomic_load_n(&trace_buf[0], __ATOMIC_RELAXED);
if (count > (KCOV_TRACE_SIZE - 1) / WORDS_PER_CMP)
count = (KCOV_TRACE_SIZE - 1) / WORDS_PER_CMP;
for (i = 0; i < count; i++) {
unsigned long type = trace_buf[1 + i * WORDS_PER_CMP];
unsigned long arg1 = trace_buf[1 + i * WORDS_PER_CMP + 1];
unsigned long arg2 = trace_buf[1 + i * WORDS_PER_CMP + 2];
/* We only care about comparisons where one side is a
* compile-time constant — those reveal what the kernel
* actually checks for. */
if (!(type & KCOV_CMP_CONST))
continue;
if (interesting_value(arg1))
pool_add(pool, arg1);
if (interesting_value(arg2))
pool_add(pool, arg2);
}
}
unsigned long cmp_hints_get(unsigned int nr)
{
struct cmp_hint_pool *pool;
if (cmp_hints_shm == NULL || nr >= MAX_NR_SYSCALL)
return 0;
pool = &cmp_hints_shm->pools[nr];
if (pool->count == 0)
return 0;
return pool->values[rand() % pool->count];
}
bool cmp_hints_available(unsigned int nr)
{
if (cmp_hints_shm == NULL || nr >= MAX_NR_SYSCALL)
return false;
return cmp_hints_shm->pools[nr].count > 0;
}