/src/selinux/checkpolicy/fuzz/checkpolicy-fuzzer.c
Line | Count | Source |
1 | | #include <assert.h> |
2 | | #include <setjmp.h> |
3 | | #include <unistd.h> |
4 | | #include <sys/mman.h> |
5 | | |
6 | | #include <sepol/debug.h> |
7 | | #include <sepol/kernel_to_cil.h> |
8 | | #include <sepol/kernel_to_conf.h> |
9 | | #include <sepol/module_to_cil.h> |
10 | | #include <sepol/policydb/policydb.h> |
11 | | #include <sepol/policydb/hierarchy.h> |
12 | | #include <sepol/policydb/expand.h> |
13 | | #include <sepol/policydb/link.h> |
14 | | |
15 | | #include "module_compiler.h" |
16 | | #include "queue.h" |
17 | | |
18 | | extern int policydb_validate(sepol_handle_t *handle, const policydb_t *p); |
19 | | extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); |
20 | | |
21 | | extern int mlspol; |
22 | | extern policydb_t *policydbp; |
23 | | extern queue_t id_queue; |
24 | | extern unsigned int policydb_errors; |
25 | | |
26 | | extern int yynerrs; |
27 | | extern FILE *yyin; |
28 | | extern void init_parser(int pass, const char *input_name); |
29 | | extern int yyparse(void); |
30 | | extern void yyrestart(FILE *); |
31 | | extern int yylex_destroy(void); |
32 | | |
33 | | jmp_buf fuzzing_pre_parse_stack_state; |
34 | | |
35 | | // Set to 1 for verbose libsepol logging |
36 | | #ifndef VERBOSE |
37 | 11.5k | #define VERBOSE 0 |
38 | | #endif |
39 | | |
40 | | static ssize_t full_write(int fd, const void *buf, size_t count) |
41 | 8.35k | { |
42 | 8.35k | ssize_t written = 0; |
43 | | |
44 | 16.7k | while (count > 0) { |
45 | 8.35k | ssize_t ret = write(fd, buf, count); |
46 | 8.35k | if (ret < 0) { |
47 | 0 | if (errno == EINTR) |
48 | 0 | continue; |
49 | | |
50 | 0 | return ret; |
51 | 0 | } |
52 | | |
53 | 8.35k | if (ret == 0) |
54 | 0 | break; |
55 | | |
56 | 8.35k | written += ret; |
57 | 8.35k | buf = (const unsigned char *)buf + (size_t)ret; |
58 | 8.35k | count -= (size_t)ret; |
59 | 8.35k | } |
60 | | |
61 | 8.35k | return written; |
62 | 8.35k | } |
63 | | |
64 | | static int read_source_policy(policydb_t *p, const uint8_t *data, size_t size) |
65 | 8.35k | { |
66 | 8.35k | int fd, rc; |
67 | 8.35k | ssize_t wr; |
68 | | |
69 | 8.35k | fd = memfd_create("fuzz-input", MFD_CLOEXEC); |
70 | 8.35k | if (fd < 0) |
71 | 0 | return -1; |
72 | | |
73 | 8.35k | wr = full_write(fd, data, size); |
74 | 8.35k | if (wr < 0 || (size_t)wr != size) { |
75 | 0 | close(fd); |
76 | 0 | return -1; |
77 | 0 | } |
78 | | |
79 | 8.35k | fsync(fd); |
80 | | |
81 | 8.35k | yynerrs = 0; |
82 | | |
83 | 8.35k | yyin = fdopen(fd, "r"); |
84 | 8.35k | if (!yyin) { |
85 | 0 | close(fd); |
86 | 0 | return -1; |
87 | 0 | } |
88 | | |
89 | 8.35k | rewind(yyin); |
90 | | |
91 | 8.35k | id_queue = queue_create(); |
92 | 8.35k | if (id_queue == NULL) { |
93 | 0 | fclose(yyin); |
94 | 0 | yylex_destroy(); |
95 | 0 | return -1; |
96 | 0 | } |
97 | | |
98 | 8.35k | policydbp = p; |
99 | 8.35k | mlspol = p->mls; |
100 | | |
101 | 8.35k | init_parser(1, "fuzz-input-1"); |
102 | | |
103 | 8.35k | if (setjmp(fuzzing_pre_parse_stack_state) != 0) { |
104 | 2 | queue_destroy(id_queue); |
105 | 2 | fclose(yyin); |
106 | 2 | yylex_destroy(); |
107 | 2 | return -1; |
108 | 2 | } |
109 | | |
110 | 8.35k | rc = yyparse(); |
111 | 8.35k | if (rc || policydb_errors) { |
112 | 3.29k | queue_destroy(id_queue); |
113 | 3.29k | fclose(yyin); |
114 | 3.29k | yylex_destroy(); |
115 | 3.29k | return -1; |
116 | 3.29k | } |
117 | | |
118 | 5.06k | rewind(yyin); |
119 | 5.06k | init_parser(2, "fuzz-input-2"); |
120 | 5.06k | yyrestart(yyin); |
121 | | |
122 | 5.06k | rc = yyparse(); |
123 | 5.06k | if (rc || policydb_errors) { |
124 | 3.46k | queue_destroy(id_queue); |
125 | 3.46k | fclose(yyin); |
126 | 3.46k | yylex_destroy(); |
127 | 3.46k | return -1; |
128 | 3.46k | } |
129 | | |
130 | 1.59k | queue_destroy(id_queue); |
131 | 1.59k | fclose(yyin); |
132 | 1.59k | yylex_destroy(); |
133 | | |
134 | 1.59k | return 0; |
135 | 5.06k | } |
136 | | |
137 | | static int write_binary_policy(FILE *outfp, policydb_t *p) |
138 | 400 | { |
139 | 400 | struct policy_file pf; |
140 | | |
141 | 400 | policy_file_init(&pf); |
142 | 400 | pf.type = PF_USE_STDIO; |
143 | 400 | pf.fp = outfp; |
144 | 400 | return policydb_write(p, &pf); |
145 | 400 | } |
146 | | |
147 | | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) |
148 | 8.39k | { |
149 | 8.39k | policydb_t parsepolicydb = {}; |
150 | 8.39k | policydb_t kernpolicydb = {}; |
151 | 8.39k | policydb_t *finalpolicydb; |
152 | 8.39k | sidtab_t sidtab = {}; |
153 | 8.39k | FILE *devnull = NULL; |
154 | 8.39k | int mls, platform, policyvers; |
155 | | |
156 | 8.39k | sepol_debug(VERBOSE); |
157 | | |
158 | | /* |
159 | | * Take the first byte whether to generate a SELinux or Xen policy, |
160 | | * the second byte whether to parse as MLS policy, |
161 | | * and the third byte as policy version. |
162 | | */ |
163 | 8.39k | if (size < 3) |
164 | 2 | return 0; |
165 | 8.39k | switch (data[0]) { |
166 | 8.08k | case 'S': |
167 | 8.08k | platform = SEPOL_TARGET_SELINUX; |
168 | | #if VERBOSE |
169 | | printf("target: SELinux\n"); |
170 | | #endif |
171 | 8.08k | break; |
172 | 299 | case 'X': |
173 | 299 | platform = SEPOL_TARGET_XEN; |
174 | | #if VERBOSE |
175 | | printf("target: Xen\n"); |
176 | | #endif |
177 | 299 | break; |
178 | 7 | default: |
179 | 7 | return 0; |
180 | 8.39k | } |
181 | 8.38k | switch (data[1]) { |
182 | 3.28k | case '0': |
183 | 3.28k | mls = 0; |
184 | | #if VERBOSE |
185 | | printf("MLS-disabled\n"); |
186 | | #endif |
187 | 3.28k | break; |
188 | 5.10k | case '1': |
189 | 5.10k | mls = 1; |
190 | | #if VERBOSE |
191 | | printf("MLS-enabled\n"); |
192 | | #endif |
193 | 5.10k | break; |
194 | 1 | default: |
195 | 1 | return 0; |
196 | 8.38k | } |
197 | 8.38k | static_assert(0x7F - 'A' >= POLICYDB_VERSION_MAX, |
198 | 8.38k | "Max policy version should be representable"); |
199 | 8.38k | policyvers = data[2] - 'A'; |
200 | 8.38k | if (policyvers < POLICYDB_VERSION_MIN || |
201 | 8.37k | policyvers > POLICYDB_VERSION_MAX) |
202 | 15 | return 0; |
203 | 8.37k | if (platform == SEPOL_TARGET_XEN && |
204 | 297 | policyvers != POLICYDB_VERSION_XEN_DEVICETREE && |
205 | 101 | policyvers != POLICYDB_VERSION_BOUNDARY) |
206 | 14 | return 0; |
207 | | #if VERBOSE |
208 | | printf("policyvers=%d\n", policyvers); |
209 | | #endif |
210 | 8.35k | data += 3; |
211 | 8.35k | size -= 3; |
212 | | |
213 | 8.35k | if (policydb_init(&parsepolicydb)) |
214 | 0 | goto exit; |
215 | | |
216 | 8.35k | parsepolicydb.policy_type = POLICY_BASE; |
217 | 8.35k | parsepolicydb.mls = mls; |
218 | 8.35k | parsepolicydb.handle_unknown = DENY_UNKNOWN; |
219 | 8.35k | parsepolicydb.policyvers = policyvers; |
220 | 8.35k | policydb_set_target_platform(&parsepolicydb, platform); |
221 | | |
222 | 8.35k | if (read_source_policy(&parsepolicydb, data, size)) |
223 | 6.76k | goto exit; |
224 | | |
225 | 1.59k | if (parsepolicydb.policy_type == POLICY_BASE) { |
226 | 1.59k | if (link_modules(NULL, &parsepolicydb, NULL, 0, VERBOSE)) |
227 | 0 | goto exit; |
228 | | |
229 | 1.59k | if (policydb_init(&kernpolicydb)) |
230 | 0 | goto exit; |
231 | | |
232 | 1.59k | if (expand_module(NULL, &parsepolicydb, &kernpolicydb, VERBOSE, |
233 | 1.59k | /*check_assertions=*/0)) |
234 | 1.19k | goto exit; |
235 | | |
236 | 400 | (void)check_assertions( |
237 | 400 | NULL, &kernpolicydb, |
238 | 400 | kernpolicydb.global->branch_list->avrules); |
239 | 400 | (void)hierarchy_check_constraints(NULL, &kernpolicydb); |
240 | | |
241 | 400 | kernpolicydb.policyvers = policyvers; |
242 | | |
243 | 400 | assert(kernpolicydb.policy_type == POLICY_KERN); |
244 | 400 | assert(kernpolicydb.handle_unknown == SEPOL_DENY_UNKNOWN); |
245 | 400 | assert(kernpolicydb.mls == mls); |
246 | 400 | assert(kernpolicydb.target_platform == platform); |
247 | | |
248 | 400 | finalpolicydb = &kernpolicydb; |
249 | 400 | } else { |
250 | 0 | assert(parsepolicydb.policy_type == POLICY_MOD); |
251 | 0 | assert(parsepolicydb.handle_unknown == SEPOL_DENY_UNKNOWN); |
252 | 0 | assert(parsepolicydb.mls == mls); |
253 | 0 | assert(parsepolicydb.target_platform == platform); |
254 | |
|
255 | 0 | finalpolicydb = &parsepolicydb; |
256 | 0 | } |
257 | | |
258 | 400 | if (policydb_load_isids(finalpolicydb, &sidtab)) |
259 | 0 | goto exit; |
260 | | |
261 | 400 | if (finalpolicydb->policy_type == POLICY_KERN && |
262 | 400 | policydb_optimize(finalpolicydb)) |
263 | 0 | goto exit; |
264 | | |
265 | 400 | if (policydb_sort_ocontexts(finalpolicydb)) |
266 | 0 | goto exit; |
267 | | |
268 | 400 | if (policydb_validate(NULL, finalpolicydb)) |
269 | | /* never generate an invalid policy */ |
270 | 0 | abort(); |
271 | | |
272 | 400 | devnull = fopen("/dev/null", "we"); |
273 | 400 | if (devnull == NULL) |
274 | 0 | goto exit; |
275 | | |
276 | 400 | if (write_binary_policy(devnull, finalpolicydb)) |
277 | 0 | abort(); |
278 | | |
279 | 400 | if (finalpolicydb->policy_type == POLICY_KERN && |
280 | 400 | sepol_kernel_policydb_to_conf(devnull, finalpolicydb)) |
281 | 0 | abort(); |
282 | | |
283 | 400 | if (finalpolicydb->policy_type == POLICY_KERN && |
284 | 400 | sepol_kernel_policydb_to_cil(devnull, finalpolicydb)) |
285 | 0 | abort(); |
286 | | |
287 | 400 | if (finalpolicydb->policy_type == POLICY_MOD && |
288 | 0 | sepol_module_policydb_to_cil(devnull, finalpolicydb, /*linked=*/0)) |
289 | 0 | abort(); |
290 | | |
291 | 8.35k | exit: |
292 | 8.35k | if (devnull != NULL) |
293 | 400 | fclose(devnull); |
294 | | |
295 | 8.35k | sepol_sidtab_destroy(&sidtab); |
296 | 8.35k | policydb_destroy(&kernpolicydb); |
297 | 8.35k | policydb_destroy(&parsepolicydb); |
298 | | |
299 | 8.35k | id_queue = NULL; |
300 | 8.35k | policydbp = NULL; |
301 | 8.35k | module_compiler_reset(); |
302 | | |
303 | | /* Non-zero return values are reserved for future use. */ |
304 | 8.35k | return 0; |
305 | 400 | } |
306 | | |
307 | | #ifdef DEFINEMAIN |
308 | | #include <sys/stat.h> |
309 | | |
310 | | int main(int argc, char **argv) |
311 | | { |
312 | | if (argc < 2) { |
313 | | fprintf(stderr, "usage: %s fuzzer-input-file\n", argv[0]); |
314 | | exit(1); |
315 | | } |
316 | | |
317 | | FILE *fp = fopen(argv[1], "rb"); |
318 | | if (!fp) { |
319 | | perror(argv[1]); |
320 | | exit(1); |
321 | | } |
322 | | |
323 | | struct stat sb; |
324 | | int rc; |
325 | | |
326 | | rc = fstat(fileno(fp), &sb); |
327 | | if (rc < 0) { |
328 | | perror("fstat"); |
329 | | exit(1); |
330 | | } |
331 | | |
332 | | void *address = mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, |
333 | | MAP_PRIVATE, fileno(fp), 0); |
334 | | if (address == MAP_FAILED) { |
335 | | perror("mmap"); |
336 | | exit(1); |
337 | | } |
338 | | |
339 | | return LLVMFuzzerTestOneInput(address, sb.st_size); |
340 | | } |
341 | | #endif |