Coverage Report

Created: 2026-09-01 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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