Coverage Report

Created: 2025-07-23 06:25

/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
}