xref: /aosp_15_r20/external/selinux/checkpolicy/fuzz/checkpolicy-fuzzer.c (revision 2d543d20722ada2425b5bdab9d0d1d29470e7bba)
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