/src/selinux/checkpolicy/fuzz/checkpolicy-fuzzer.c
Line | Count | Source (jump to first uncovered line) |
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 | 10.2k | #define VERBOSE 0 |
37 | | |
38 | | static ssize_t full_write(int fd, const void *buf, size_t count) |
39 | 8.42k | { |
40 | 8.42k | ssize_t written = 0; |
41 | | |
42 | 16.8k | while (count > 0) { |
43 | 8.42k | ssize_t ret = write(fd, buf, count); |
44 | 8.42k | if (ret < 0) { |
45 | 0 | if (errno == EINTR) |
46 | 0 | continue; |
47 | | |
48 | 0 | return ret; |
49 | 0 | } |
50 | | |
51 | 8.42k | if (ret == 0) |
52 | 0 | break; |
53 | | |
54 | 8.42k | written += ret; |
55 | 8.42k | buf = (const unsigned char *)buf + (size_t)ret; |
56 | 8.42k | count -= (size_t)ret; |
57 | 8.42k | } |
58 | | |
59 | 8.42k | return written; |
60 | 8.42k | } |
61 | | |
62 | | static int read_source_policy(policydb_t *p, const uint8_t *data, size_t size) |
63 | 8.42k | { |
64 | 8.42k | int fd, rc; |
65 | 8.42k | ssize_t wr; |
66 | | |
67 | 8.42k | fd = memfd_create("fuzz-input", MFD_CLOEXEC); |
68 | 8.42k | if (fd < 0) |
69 | 0 | return -1; |
70 | | |
71 | 8.42k | wr = full_write(fd, data, size); |
72 | 8.42k | if (wr < 0 || (size_t)wr != size) { |
73 | 0 | close(fd); |
74 | 0 | return -1; |
75 | 0 | } |
76 | | |
77 | 8.42k | fsync(fd); |
78 | | |
79 | 8.42k | yynerrs = 0; |
80 | | |
81 | 8.42k | yyin = fdopen(fd, "r"); |
82 | 8.42k | if (!yyin) { |
83 | 0 | close(fd); |
84 | 0 | return -1; |
85 | 0 | } |
86 | | |
87 | 8.42k | rewind(yyin); |
88 | | |
89 | 8.42k | id_queue = queue_create(); |
90 | 8.42k | if (id_queue == NULL) { |
91 | 0 | fclose(yyin); |
92 | 0 | yylex_destroy(); |
93 | 0 | return -1; |
94 | 0 | } |
95 | | |
96 | 8.42k | policydbp = p; |
97 | 8.42k | mlspol = p->mls; |
98 | | |
99 | 8.42k | init_parser(1, "fuzz-input-1"); |
100 | | |
101 | 8.42k | if (setjmp(fuzzing_pre_parse_stack_state) != 0) { |
102 | 4 | queue_destroy(id_queue); |
103 | 4 | fclose(yyin); |
104 | 4 | yylex_destroy(); |
105 | 4 | return -1; |
106 | 4 | } |
107 | | |
108 | 8.42k | rc = yyparse(); |
109 | | // TODO: drop global variable policydb_errors if proven to be redundant |
110 | 8.42k | assert(rc || !policydb_errors); |
111 | 8.42k | if (rc || policydb_errors) { |
112 | 4.01k | queue_destroy(id_queue); |
113 | 4.01k | fclose(yyin); |
114 | 4.01k | yylex_destroy(); |
115 | 4.01k | return -1; |
116 | 4.01k | } |
117 | | |
118 | 4.40k | rewind(yyin); |
119 | 4.40k | init_parser(2, "fuzz-input-2"); |
120 | 4.40k | yyrestart(yyin); |
121 | | |
122 | 4.40k | rc = yyparse(); |
123 | 4.40k | assert(rc || !policydb_errors); |
124 | 4.40k | if (rc || policydb_errors) { |
125 | 3.52k | queue_destroy(id_queue); |
126 | 3.52k | fclose(yyin); |
127 | 3.52k | yylex_destroy(); |
128 | 3.52k | return -1; |
129 | 3.52k | } |
130 | | |
131 | 887 | queue_destroy(id_queue); |
132 | 887 | fclose(yyin); |
133 | 887 | yylex_destroy(); |
134 | | |
135 | 887 | return 0; |
136 | 4.40k | } |
137 | | |
138 | | static int write_binary_policy(FILE *outfp, policydb_t *p) |
139 | 876 | { |
140 | 876 | struct policy_file pf; |
141 | | |
142 | 876 | policy_file_init(&pf); |
143 | 876 | pf.type = PF_USE_STDIO; |
144 | 876 | pf.fp = outfp; |
145 | 876 | return policydb_write(p, &pf); |
146 | 876 | } |
147 | | |
148 | | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) |
149 | 8.45k | { |
150 | 8.45k | policydb_t parsepolicydb = {}; |
151 | 8.45k | policydb_t kernpolicydb = {}; |
152 | 8.45k | policydb_t *finalpolicydb; |
153 | 8.45k | sidtab_t sidtab = {}; |
154 | 8.45k | FILE *devnull = NULL; |
155 | 8.45k | int mls, platform, policyvers; |
156 | | |
157 | 8.45k | sepol_debug(VERBOSE); |
158 | | |
159 | | /* |
160 | | * Take the first byte whether to generate a SELinux or Xen policy, |
161 | | * the second byte whether to parse as MLS policy, |
162 | | * and the second byte as policy version. |
163 | | */ |
164 | 8.45k | if (size < 3) |
165 | 2 | return 0; |
166 | 8.45k | switch (data[0]) { |
167 | 7.65k | case 'S': |
168 | 7.65k | platform = SEPOL_TARGET_SELINUX; |
169 | 7.65k | break; |
170 | 791 | case 'X': |
171 | 791 | platform = SEPOL_TARGET_XEN; |
172 | 791 | break; |
173 | 8 | default: |
174 | 8 | return 0; |
175 | 8.45k | } |
176 | 8.44k | switch (data[1]) { |
177 | 4.42k | case '0': |
178 | 4.42k | mls = 0; |
179 | 4.42k | break; |
180 | 4.01k | case '1': |
181 | 4.01k | mls = 1; |
182 | 4.01k | break; |
183 | 1 | default: |
184 | 1 | return 0; |
185 | 8.44k | } |
186 | 8.44k | static_assert(0x7F - 'A' >= POLICYDB_VERSION_MAX, "Max policy version should be representable"); |
187 | 8.44k | policyvers = data[2] - 'A'; |
188 | 8.44k | if (policyvers < POLICYDB_VERSION_MIN || policyvers > POLICYDB_VERSION_MAX) |
189 | 12 | return 0; |
190 | 8.42k | data += 3; |
191 | 8.42k | size -= 3; |
192 | | |
193 | 8.42k | if (policydb_init(&parsepolicydb)) |
194 | 0 | goto exit; |
195 | | |
196 | 8.42k | parsepolicydb.policy_type = POLICY_BASE; |
197 | 8.42k | parsepolicydb.mls = mls; |
198 | 8.42k | parsepolicydb.handle_unknown = DENY_UNKNOWN; |
199 | 8.42k | parsepolicydb.policyvers = policyvers; |
200 | 8.42k | policydb_set_target_platform(&parsepolicydb, platform); |
201 | | |
202 | 8.42k | if (read_source_policy(&parsepolicydb, data, size)) |
203 | 7.54k | goto exit; |
204 | | |
205 | 887 | if (parsepolicydb.policy_type == POLICY_BASE) { |
206 | 887 | if (link_modules(NULL, &parsepolicydb, NULL, 0, VERBOSE)) |
207 | 0 | goto exit; |
208 | | |
209 | 887 | if (policydb_init(&kernpolicydb)) |
210 | 0 | goto exit; |
211 | | |
212 | 887 | if (expand_module(NULL, &parsepolicydb, &kernpolicydb, VERBOSE, /*check_assertions=*/0)) |
213 | 3 | goto exit; |
214 | | |
215 | 884 | (void) check_assertions(NULL, &kernpolicydb, kernpolicydb.global->branch_list->avrules); |
216 | 884 | (void) hierarchy_check_constraints(NULL, &kernpolicydb); |
217 | | |
218 | 884 | kernpolicydb.policyvers = policyvers; |
219 | | |
220 | 884 | assert(kernpolicydb.policy_type == POLICY_KERN); |
221 | 884 | assert(kernpolicydb.handle_unknown == SEPOL_DENY_UNKNOWN); |
222 | 884 | assert(kernpolicydb.mls == mls); |
223 | 884 | assert(kernpolicydb.target_platform == platform); |
224 | | |
225 | 884 | finalpolicydb = &kernpolicydb; |
226 | 884 | } else { |
227 | 0 | assert(parsepolicydb.policy_type == POLICY_MOD); |
228 | 0 | assert(parsepolicydb.handle_unknown == SEPOL_DENY_UNKNOWN); |
229 | 0 | assert(parsepolicydb.mls == mls); |
230 | 0 | assert(parsepolicydb.target_platform == platform); |
231 | | |
232 | 0 | finalpolicydb = &parsepolicydb; |
233 | 0 | } |
234 | | |
235 | 884 | if (policydb_load_isids(finalpolicydb, &sidtab)) |
236 | 0 | goto exit; |
237 | | |
238 | 884 | if (finalpolicydb->policy_type == POLICY_KERN && policydb_optimize(finalpolicydb)) |
239 | 8 | goto exit; |
240 | | |
241 | 876 | if (policydb_sort_ocontexts(finalpolicydb)) |
242 | 0 | goto exit; |
243 | | |
244 | 876 | if (policydb_validate(NULL, finalpolicydb)) |
245 | | /* never generate an invalid policy */ |
246 | 0 | abort(); |
247 | | |
248 | 876 | devnull = fopen("/dev/null", "we"); |
249 | 876 | if (devnull == NULL) |
250 | 0 | goto exit; |
251 | | |
252 | 876 | if (write_binary_policy(devnull, finalpolicydb)) |
253 | 0 | abort(); |
254 | | |
255 | 876 | if (finalpolicydb->policy_type == POLICY_KERN && sepol_kernel_policydb_to_conf(devnull, finalpolicydb)) |
256 | 0 | abort(); |
257 | | |
258 | 876 | if (finalpolicydb->policy_type == POLICY_KERN && sepol_kernel_policydb_to_cil(devnull, finalpolicydb)) |
259 | 0 | abort(); |
260 | | |
261 | 876 | if (finalpolicydb->policy_type == POLICY_MOD && sepol_module_policydb_to_cil(devnull, finalpolicydb, /*linked=*/0)) |
262 | 0 | abort(); |
263 | | |
264 | 8.42k | exit: |
265 | 8.42k | if (devnull != NULL) |
266 | 876 | fclose(devnull); |
267 | | |
268 | 8.42k | sepol_sidtab_destroy(&sidtab); |
269 | 8.42k | policydb_destroy(&kernpolicydb); |
270 | 8.42k | policydb_destroy(&parsepolicydb); |
271 | | |
272 | 8.42k | id_queue = NULL; |
273 | 8.42k | policydbp = NULL; |
274 | 8.42k | module_compiler_reset(); |
275 | | |
276 | | /* Non-zero return values are reserved for future use. */ |
277 | 8.42k | return 0; |
278 | 876 | } |