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