Coverage Report

Created: 2025-07-23 06:32

/src/selinux/libselinux/fuzz/selabel_file_compiled-fuzzer.c
Line
Count
Source (jump to first uncovered line)
1
#include <errno.h>
2
#include <stdint.h>
3
#include <stdio.h>
4
#include <sys/mman.h>
5
#include <unistd.h>
6
7
#include <selinux/label.h>
8
9
#include "../src/label_file.h"
10
11
extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
12
13
10.3k
#define MEMFD_FILE_NAME "file_contexts"
14
9.04k
#define CTRL_PARTIAL  (1U << 0)
15
9.04k
#define CTRL_FIND_ALL (1U << 1)
16
9.04k
#define CTRL_MODE     (1U << 2)
17
18
19
__attribute__ ((format(printf, 2, 3)))
20
static int null_log(int type __attribute__((unused)), const char *fmt __attribute__((unused)), ...)
21
401
{
22
401
  return 0;
23
401
}
24
25
static int validate_context(char **ctxp)
26
3.82k
{
27
3.82k
  assert(strcmp(*ctxp, "<<none>>") != 0);
28
29
3.82k
  if (*ctxp[0] == '\0') {
30
9
    errno = EINVAL;
31
9
    return -1;
32
9
  }
33
34
3.81k
  return 0;
35
3.82k
}
36
37
static int write_full(int fd, const void *data, size_t size)
38
5.19k
{
39
5.19k
  ssize_t rc;
40
5.19k
  const unsigned char *p = data;
41
42
10.3k
  while (size > 0) {
43
5.19k
    rc = write(fd, p, size);
44
5.19k
    if (rc == -1) {
45
0
      if (errno == EINTR)
46
0
        continue;
47
48
0
      return -1;
49
0
    }
50
51
5.19k
    p += rc;
52
5.19k
    size -= rc;
53
5.19k
  }
54
55
5.19k
  return 0;
56
5.19k
}
57
58
static FILE* convert_data(const uint8_t *data, size_t size)
59
5.19k
{
60
5.19k
  FILE* stream;
61
5.19k
  int fd, rc;
62
63
5.19k
  fd = memfd_create(MEMFD_FILE_NAME, MFD_CLOEXEC);
64
5.19k
  if (fd == -1)
65
0
    return NULL;
66
67
5.19k
  rc = write_full(fd, data, size);
68
5.19k
  if (rc == -1) {
69
0
    close(fd);
70
0
    return NULL;
71
0
  }
72
73
5.19k
  stream = fdopen(fd, "r");
74
5.19k
  if (!stream) {
75
0
    close(fd);
76
0
    return NULL;
77
0
  }
78
79
5.19k
  rc = fseek(stream, 0L, SEEK_SET);
80
5.19k
  if (rc == -1) {
81
0
    fclose(stream);
82
0
    return NULL;
83
0
  }
84
85
5.19k
  return stream;
86
5.19k
}
87
88
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
89
4.52k
{
90
4.52k
  struct selabel_handle rec;
91
4.52k
  struct saved_data sdata = {};
92
4.52k
  struct spec_node *root = NULL;
93
4.52k
  FILE* fp = NULL;
94
4.52k
  struct lookup_result *result = NULL;
95
4.52k
  uint8_t control;
96
4.52k
  uint8_t *fcontext_data1 = NULL, *fcontext_data2 = NULL, *fcontext_data3 = NULL;
97
4.52k
  char *key = NULL;
98
4.52k
  size_t fcontext_data1_len, fcontext_data2_len = 0, fcontext_data3_len = 0, key_len;
99
4.52k
  bool partial, find_all;
100
4.52k
  mode_t mode;
101
4.52k
  int rc;
102
103
  /*
104
   * Treat first byte as control byte, whether to use partial mode, find all matches or mode to lookup
105
   */
106
4.52k
  if (size == 0)
107
0
    return 0;
108
109
4.52k
  control = data[0];
110
4.52k
  data++;
111
4.52k
  size--;
112
113
4.52k
  if (control & ~(CTRL_PARTIAL | CTRL_FIND_ALL | CTRL_MODE))
114
8
    return 0;
115
116
4.52k
  partial  = control & CTRL_PARTIAL;
117
4.52k
  find_all = control & CTRL_FIND_ALL;
118
  /* S_IFSOCK has the highest integer value */
119
4.52k
  mode     = (control & CTRL_MODE) ? S_IFSOCK : 0;
120
121
122
  /*
123
   * Split the fuzzer input into up to four pieces: one to three compiled fcontext
124
   * definitions (to mimic file_contexts, file_contexts.homedirs and file_contexts.local,
125
   * and the lookup key
126
   */
127
4.52k
  const unsigned char separator[4] = { 0xde, 0xad, 0xbe, 0xef };
128
4.52k
  const uint8_t *sep = memmem(data, size, separator, 4);
129
4.52k
  if (!sep || sep == data)
130
9
    return 0;
131
132
4.51k
  fcontext_data1_len = sep - data;
133
4.51k
  fcontext_data1 = malloc(fcontext_data1_len);
134
4.51k
  if (!fcontext_data1)
135
0
    goto cleanup;
136
137
4.51k
  memcpy(fcontext_data1, data, fcontext_data1_len);
138
4.51k
  data += fcontext_data1_len + 4;
139
4.51k
  size -= fcontext_data1_len + 4;
140
141
4.51k
  sep = memmem(data, size, separator, 4);
142
4.51k
  if (sep) {
143
606
    fcontext_data2_len = sep - data;
144
606
    if (fcontext_data2_len) {
145
577
      fcontext_data2 = malloc(fcontext_data2_len);
146
577
      if (!fcontext_data2)
147
0
        goto cleanup;
148
149
577
      memcpy(fcontext_data2, data, fcontext_data2_len);
150
577
    }
151
152
606
    data += fcontext_data2_len + 4;
153
606
    size -= fcontext_data2_len + 4;
154
606
  }
155
156
4.51k
  sep = memmem(data, size, separator, 4);
157
4.51k
  if (sep) {
158
158
    fcontext_data3_len = sep - data;
159
158
    if (fcontext_data3_len) {
160
157
      fcontext_data3 = malloc(fcontext_data3_len);
161
157
      if (!fcontext_data3)
162
0
        goto cleanup;
163
164
157
      memcpy(fcontext_data3, data, fcontext_data3_len);
165
157
    }
166
167
158
    data += fcontext_data3_len + 4;
168
158
    size -= fcontext_data3_len + 4;
169
158
  }
170
171
4.51k
  key_len = size;
172
4.51k
  key = malloc(key_len + 1);
173
4.51k
  if (!key)
174
0
    goto cleanup;
175
176
4.51k
  memcpy(key, data, key_len);
177
4.51k
  key[key_len] = '\0';
178
179
180
  /*
181
   * Mock selabel handle
182
   */
183
4.51k
  rec = (struct selabel_handle) {
184
4.51k
    .backend = SELABEL_CTX_FILE,
185
4.51k
    .validating = 1,
186
4.51k
    .data = &sdata,
187
4.51k
  };
188
189
4.51k
  selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) { .func_log = &null_log });
190
  /* validate to pre-compile regular expressions */
191
4.51k
  selinux_set_callback(SELINUX_CB_VALIDATE, (union selinux_callback) { .func_validate = &validate_context });
192
193
4.51k
  root = calloc(1, sizeof(*root));
194
4.51k
  if (!root)
195
0
    goto cleanup;
196
197
4.51k
  sdata.root = root;
198
199
4.51k
  fp = convert_data(fcontext_data1, fcontext_data1_len);
200
4.51k
  if (!fp)
201
0
    goto cleanup;
202
203
4.51k
  errno = 0;
204
4.51k
  rc = load_mmap(fp, fcontext_data1_len, &rec, MEMFD_FILE_NAME, 0);
205
4.51k
  if (rc) {
206
2.05k
    assert(errno != 0);
207
2.05k
    goto cleanup;
208
2.05k
  }
209
210
2.45k
  fclose(fp);
211
2.45k
  fp = NULL;
212
213
2.45k
  if (fcontext_data2_len) {
214
544
    fp = convert_data(fcontext_data2, fcontext_data2_len);
215
544
    if (!fp)
216
0
      goto cleanup;
217
218
544
    errno = 0;
219
544
    rc = load_mmap(fp, fcontext_data2_len, &rec, MEMFD_FILE_NAME, 1);
220
544
    if (rc) {
221
53
      assert(errno != 0);
222
53
      goto cleanup;
223
53
    }
224
225
491
    fclose(fp);
226
491
    fp = NULL;
227
491
  }
228
229
2.40k
  if (fcontext_data3_len) {
230
136
    fp = convert_data(fcontext_data3, fcontext_data3_len);
231
136
    if (!fp)
232
0
      goto cleanup;
233
234
136
    errno = 0;
235
136
    rc = load_mmap(fp, fcontext_data3_len, &rec, MEMFD_FILE_NAME, 2);
236
136
    if (rc) {
237
36
      assert(errno != 0);
238
36
      goto cleanup;
239
36
    }
240
241
100
    fclose(fp);
242
100
    fp = NULL;
243
100
  }
244
245
2.36k
  sort_specs(&sdata);
246
247
2.36k
  assert(cmp(&rec, &rec) == SELABEL_EQUAL);
248
249
2.36k
  errno = 0;
250
2.36k
  result = lookup_all(&rec, key, mode, partial, find_all, NULL);
251
252
2.36k
  if (!result)
253
1.81k
    assert(errno != 0);
254
255
3.39k
  for (const struct lookup_result *res = result; res; res = res->next) {
256
1.03k
    assert(res->regex_str);
257
1.03k
    assert(res->regex_str[0] != '\0');
258
1.03k
    assert(res->lr->ctx_raw);
259
1.03k
    assert(res->lr->ctx_raw[0] != '\0');
260
1.03k
    assert(strcmp(res->lr->ctx_raw, "<<none>>") != 0);
261
1.03k
    assert(!res->lr->ctx_trans);
262
1.03k
    assert(res->lr->validated);
263
1.03k
    assert(res->prefix_len <= strlen(res->regex_str));
264
1.03k
  }
265
266
267
4.51k
cleanup:
268
4.51k
  free_lookup_result(result);
269
4.51k
  if (fp)
270
2.14k
    fclose(fp);
271
4.51k
  if (sdata.root) {
272
4.51k
    free_spec_node(sdata.root);
273
4.51k
    free(sdata.root);
274
4.51k
  }
275
276
4.51k
  {
277
4.51k
    struct mmap_area *area, *last_area;
278
279
4.51k
    area = sdata.mmap_areas;
280
7.55k
    while (area) {
281
3.04k
      rc = munmap(area->addr, area->len);
282
3.04k
      assert(rc == 0);
283
3.04k
      last_area = area;
284
3.04k
      area = area->next;
285
3.04k
      free(last_area);
286
3.04k
    }
287
4.51k
  }
288
289
4.51k
  free(key);
290
4.51k
  free(fcontext_data3);
291
4.51k
  free(fcontext_data2);
292
4.51k
  free(fcontext_data1);
293
294
  /* Non-zero return values are reserved for future use. */
295
4.51k
  return 0;
296
4.51k
}