Coverage Report

Created: 2025-10-13 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/selinux/libselinux/fuzz/selabel_file_compiled-fuzzer.c
Line
Count
Source
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.4k
#define MEMFD_FILE_NAME "file_contexts"
14
9.08k
#define CTRL_PARTIAL  (1U << 0)
15
9.08k
#define CTRL_FIND_ALL (1U << 1)
16
9.08k
#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
418
{
22
418
  return 0;
23
418
}
24
25
static int validate_context(char **ctxp)
26
3.74k
{
27
3.74k
  assert(strcmp(*ctxp, "<<none>>") != 0);
28
29
3.74k
  if (*ctxp[0] == '\0') {
30
10
    errno = EINVAL;
31
10
    return -1;
32
10
  }
33
34
3.73k
  return 0;
35
3.74k
}
36
37
static int write_full(int fd, const void *data, size_t size)
38
5.21k
{
39
5.21k
  ssize_t rc;
40
5.21k
  const unsigned char *p = data;
41
42
10.4k
  while (size > 0) {
43
5.21k
    rc = write(fd, p, size);
44
5.21k
    if (rc == -1) {
45
0
      if (errno == EINTR)
46
0
        continue;
47
48
0
      return -1;
49
0
    }
50
51
5.21k
    p += rc;
52
5.21k
    size -= rc;
53
5.21k
  }
54
55
5.21k
  return 0;
56
5.21k
}
57
58
static FILE* convert_data(const uint8_t *data, size_t size)
59
5.21k
{
60
5.21k
  FILE* stream;
61
5.21k
  int fd, rc;
62
63
5.21k
  fd = memfd_create(MEMFD_FILE_NAME, MFD_CLOEXEC);
64
5.21k
  if (fd == -1)
65
0
    return NULL;
66
67
5.21k
  rc = write_full(fd, data, size);
68
5.21k
  if (rc == -1) {
69
0
    close(fd);
70
0
    return NULL;
71
0
  }
72
73
5.21k
  stream = fdopen(fd, "r");
74
5.21k
  if (!stream) {
75
0
    close(fd);
76
0
    return NULL;
77
0
  }
78
79
5.21k
  rc = fseek(stream, 0L, SEEK_SET);
80
5.21k
  if (rc == -1) {
81
0
    fclose(stream);
82
0
    return NULL;
83
0
  }
84
85
5.21k
  return stream;
86
5.21k
}
87
88
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
89
4.54k
{
90
4.54k
  struct selabel_handle rec;
91
4.54k
  struct saved_data sdata = {};
92
4.54k
  struct spec_node *root = NULL;
93
4.54k
  FILE* fp = NULL;
94
4.54k
  struct lookup_result *result = NULL;
95
4.54k
  uint8_t control;
96
4.54k
  uint8_t *fcontext_data1 = NULL, *fcontext_data2 = NULL, *fcontext_data3 = NULL;
97
4.54k
  char *key = NULL;
98
4.54k
  size_t fcontext_data1_len, fcontext_data2_len = 0, fcontext_data3_len = 0, key_len;
99
4.54k
  bool partial, find_all;
100
4.54k
  mode_t mode;
101
4.54k
  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.54k
  if (size == 0)
107
0
    return 0;
108
109
4.54k
  control = data[0];
110
4.54k
  data++;
111
4.54k
  size--;
112
113
4.54k
  if (control & ~(CTRL_PARTIAL | CTRL_FIND_ALL | CTRL_MODE))
114
10
    return 0;
115
116
4.53k
  partial  = control & CTRL_PARTIAL;
117
4.53k
  find_all = control & CTRL_FIND_ALL;
118
  /* S_IFSOCK has the highest integer value */
119
4.53k
  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.53k
  const unsigned char separator[4] = { 0xde, 0xad, 0xbe, 0xef };
128
4.53k
  const uint8_t *sep = memmem(data, size, separator, 4);
129
4.53k
  if (!sep || sep == data)
130
9
    return 0;
131
132
4.52k
  fcontext_data1_len = sep - data;
133
4.52k
  fcontext_data1 = malloc(fcontext_data1_len);
134
4.52k
  if (!fcontext_data1)
135
0
    goto cleanup;
136
137
4.52k
  memcpy(fcontext_data1, data, fcontext_data1_len);
138
4.52k
  data += fcontext_data1_len + 4;
139
4.52k
  size -= fcontext_data1_len + 4;
140
141
4.52k
  sep = memmem(data, size, separator, 4);
142
4.52k
  if (sep) {
143
617
    fcontext_data2_len = sep - data;
144
617
    if (fcontext_data2_len) {
145
580
      fcontext_data2 = malloc(fcontext_data2_len);
146
580
      if (!fcontext_data2)
147
0
        goto cleanup;
148
149
580
      memcpy(fcontext_data2, data, fcontext_data2_len);
150
580
    }
151
152
617
    data += fcontext_data2_len + 4;
153
617
    size -= fcontext_data2_len + 4;
154
617
  }
155
156
4.52k
  sep = memmem(data, size, separator, 4);
157
4.52k
  if (sep) {
158
151
    fcontext_data3_len = sep - data;
159
151
    if (fcontext_data3_len) {
160
150
      fcontext_data3 = malloc(fcontext_data3_len);
161
150
      if (!fcontext_data3)
162
0
        goto cleanup;
163
164
150
      memcpy(fcontext_data3, data, fcontext_data3_len);
165
150
    }
166
167
151
    data += fcontext_data3_len + 4;
168
151
    size -= fcontext_data3_len + 4;
169
151
  }
170
171
4.52k
  key_len = size;
172
4.52k
  key = malloc(key_len + 1);
173
4.52k
  if (!key)
174
0
    goto cleanup;
175
176
4.52k
  memcpy(key, data, key_len);
177
4.52k
  key[key_len] = '\0';
178
179
180
  /*
181
   * Mock selabel handle
182
   */
183
4.52k
  rec = (struct selabel_handle) {
184
4.52k
    .backend = SELABEL_CTX_FILE,
185
4.52k
    .validating = 1,
186
4.52k
    .data = &sdata,
187
4.52k
  };
188
189
4.52k
  selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) { .func_log = &null_log });
190
  /* validate to pre-compile regular expressions */
191
4.52k
  selinux_set_callback(SELINUX_CB_VALIDATE, (union selinux_callback) { .func_validate = &validate_context });
192
193
4.52k
  root = calloc(1, sizeof(*root));
194
4.52k
  if (!root)
195
0
    goto cleanup;
196
197
4.52k
  sdata.root = root;
198
199
4.52k
  fp = convert_data(fcontext_data1, fcontext_data1_len);
200
4.52k
  if (!fp)
201
0
    goto cleanup;
202
203
4.52k
  errno = 0;
204
4.52k
  rc = load_mmap(fp, fcontext_data1_len, &rec, MEMFD_FILE_NAME, 0);
205
4.52k
  if (rc) {
206
2.00k
    assert(errno != 0);
207
2.00k
    goto cleanup;
208
2.00k
  }
209
210
2.52k
  fclose(fp);
211
2.52k
  fp = NULL;
212
213
2.52k
  if (fcontext_data2_len) {
214
553
    fp = convert_data(fcontext_data2, fcontext_data2_len);
215
553
    if (!fp)
216
0
      goto cleanup;
217
218
553
    errno = 0;
219
553
    rc = load_mmap(fp, fcontext_data2_len, &rec, MEMFD_FILE_NAME, 1);
220
553
    if (rc) {
221
50
      assert(errno != 0);
222
50
      goto cleanup;
223
50
    }
224
225
503
    fclose(fp);
226
503
    fp = NULL;
227
503
  }
228
229
2.47k
  if (fcontext_data3_len) {
230
129
    fp = convert_data(fcontext_data3, fcontext_data3_len);
231
129
    if (!fp)
232
0
      goto cleanup;
233
234
129
    errno = 0;
235
129
    rc = load_mmap(fp, fcontext_data3_len, &rec, MEMFD_FILE_NAME, 2);
236
129
    if (rc) {
237
37
      assert(errno != 0);
238
37
      goto cleanup;
239
37
    }
240
241
92
    fclose(fp);
242
92
    fp = NULL;
243
92
  }
244
245
2.43k
  sort_specs(&sdata);
246
247
2.43k
  assert(cmp(&rec, &rec) == SELABEL_EQUAL);
248
249
2.43k
  errno = 0;
250
2.43k
  result = lookup_all(&rec, key, mode, partial, find_all, NULL);
251
252
2.43k
  if (!result)
253
2.43k
    assert(errno != 0);
254
255
3.62k
  for (const struct lookup_result *res = result; res; res = res->next) {
256
1.19k
    assert(res->regex_str);
257
1.19k
    assert(res->regex_str[0] != '\0');
258
1.19k
    assert(res->lr->ctx_raw);
259
1.19k
    assert(res->lr->ctx_raw[0] != '\0');
260
1.19k
    assert(strcmp(res->lr->ctx_raw, "<<none>>") != 0);
261
1.19k
    assert(!res->lr->ctx_trans);
262
1.19k
    assert(res->lr->validated);
263
1.19k
    assert(res->prefix_len <= strlen(res->regex_str));
264
1.19k
  }
265
266
267
4.52k
cleanup:
268
4.52k
  free_lookup_result(result);
269
4.52k
  if (fp)
270
2.09k
    fclose(fp);
271
4.52k
  if (sdata.root) {
272
4.52k
    free_spec_node(sdata.root);
273
4.52k
    free(sdata.root);
274
4.52k
  }
275
276
4.52k
  {
277
4.52k
    struct mmap_area *area, *last_area;
278
279
4.52k
    area = sdata.mmap_areas;
280
7.64k
    while (area) {
281
3.11k
      rc = munmap(area->addr, area->len);
282
3.11k
      assert(rc == 0);
283
3.11k
      last_area = area;
284
3.11k
      area = area->next;
285
3.11k
      free(last_area);
286
3.11k
    }
287
4.52k
  }
288
289
4.52k
  free(key);
290
4.52k
  free(fcontext_data3);
291
4.52k
  free(fcontext_data2);
292
4.52k
  free(fcontext_data1);
293
294
  /* Non-zero return values are reserved for future use. */
295
4.52k
  return 0;
296
4.52k
}