Coverage Report

Created: 2025-07-18 06:11

/src/selinux/libselinux/src/label_file.h
Line
Count
Source (jump to first uncovered line)
1
#ifndef _SELABEL_FILE_H_
2
#define _SELABEL_FILE_H_
3
4
#include <assert.h>
5
#include <ctype.h>
6
#include <errno.h>
7
#include <pthread.h>
8
#include <stdint.h>
9
#include <stdio.h>
10
#include <string.h>
11
12
#include <sys/stat.h>
13
#include <sys/xattr.h>
14
15
/*
16
 * regex.h/c were introduced to hold all dependencies on the regular
17
 * expression back-end when we started supporting PCRE2. regex.h defines a
18
 * minimal interface required by libselinux, so that the remaining code
19
 * can be agnostic about the underlying implementation.
20
 */
21
#include "regex.h"
22
23
#include "callbacks.h"
24
#include "label_internal.h"
25
#include "selinux_internal.h"
26
27
5.06k
#define SELINUX_MAGIC_COMPILED_FCONTEXT 0xf97cff8a
28
29
/* Version specific changes */
30
#define SELINUX_COMPILED_FCONTEXT_NOPCRE_VERS 1
31
#define SELINUX_COMPILED_FCONTEXT_PCRE_VERS 2
32
#define SELINUX_COMPILED_FCONTEXT_MODE    3
33
#define SELINUX_COMPILED_FCONTEXT_PREFIX_LEN  4
34
#define SELINUX_COMPILED_FCONTEXT_REGEX_ARCH  5
35
4.98k
#define SELINUX_COMPILED_FCONTEXT_TREE_LAYOUT 6
36
37
#define SELINUX_COMPILED_FCONTEXT_MAX_VERS \
38
  SELINUX_COMPILED_FCONTEXT_TREE_LAYOUT
39
40
/* Required selinux_restorecon and selabel_get_digests_all_partial_matches() */
41
0
#define RESTORECON_PARTIAL_MATCH_DIGEST  "security.sehash"
42
43
2.74k
#define LABEL_FILE_KIND_INVALID   255
44
9.11M
#define LABEL_FILE_KIND_ALL   0
45
576
#define LABEL_FILE_KIND_DIR   1
46
238
#define LABEL_FILE_KIND_CHR   2
47
217
#define LABEL_FILE_KIND_BLK   3
48
3.65k
#define LABEL_FILE_KIND_SOCK    4
49
204
#define LABEL_FILE_KIND_FIFO    5
50
513
#define LABEL_FILE_KIND_LNK   6
51
232
#define LABEL_FILE_KIND_REG   7
52
53
/* Only exported for fuzzing */
54
struct lookup_result {
55
  const char *regex_str;
56
  struct selabel_lookup_rec *lr;
57
  uint16_t prefix_len;
58
  uint8_t file_kind;
59
  bool has_meta_chars;
60
  struct lookup_result *next;
61
};
62
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
63
extern int load_mmap(FILE *fp, const size_t len, struct selabel_handle *rec, const char *path, uint8_t inputno);
64
extern int process_text_file(FILE *fp, const char *prefix, struct selabel_handle *rec, const char *path, uint8_t inputno);
65
extern void free_lookup_result(struct lookup_result *result);
66
extern struct lookup_result *lookup_all(struct selabel_handle *rec, const char *key, int type, bool partial, bool find_all, struct lookup_result *buf);
67
extern enum selabel_cmp_result cmp(const struct selabel_handle *h1, const struct selabel_handle *h2);
68
#endif  /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
69
70
/* A path substitution entry */
71
struct selabel_sub {
72
  char *src;        /* source path prefix */
73
  char *dst;        /* substituted path prefix */
74
  uint32_t slen;        /* length of source path prefix */
75
  uint32_t dlen;        /* length of substituted path prefix */
76
};
77
78
/* A regular expression file security context specification */
79
struct regex_spec {
80
  struct selabel_lookup_rec lr;   /* contexts for lookup result */
81
  char *regex_str;      /* original regular expression string for diagnostics */
82
  struct regex_data *regex;   /* backend dependent regular expression data */
83
  pthread_mutex_t regex_lock;   /* lock for lazy compilation of regex */
84
  uint32_t lineno;      /* Line number in source file */
85
  uint16_t prefix_len;      /* length of fixed path prefix */
86
  uint8_t inputno;      /* Input number of source file */
87
  uint8_t file_kind;      /* file type */
88
  bool regex_compiled;      /* whether the regex is compiled */
89
  bool any_matches;     /* whether any pathname match */
90
  bool from_mmap;       /* whether this spec is from an mmap of the data */
91
};
92
93
/* A literal file security context specification */
94
struct literal_spec {
95
  struct selabel_lookup_rec lr;   /* contexts for lookup result */
96
  char *regex_str;      /* original regular expression string for diagnostics */
97
  char *literal_match;      /* simplified string from regular expression */
98
  uint16_t prefix_len;      /* length of fixed path prefix, i.e. length of the literal match */
99
  uint8_t inputno;      /* Input number of source file */
100
  uint8_t file_kind;      /* file type */
101
  bool any_matches;     /* whether any pathname match */
102
  bool from_mmap;       /* whether this spec is from an mmap of the data */
103
};
104
105
/*
106
 * Max depth of specification nodes
107
 *
108
 * Measure before changing:
109
 *   - 2  leads to slower lookup
110
 *   - >4 require more memory (and allocations) for no performance gain
111
 */
112
4.98M
#define SPEC_NODE_MAX_DEPTH 3
113
114
/* A specification node */
115
struct spec_node {
116
  /* stem of the node, or NULL for root node */
117
  char *stem;
118
119
  /* parent node */
120
  struct spec_node *parent;
121
122
  /*
123
   * Array of literal specifications (ordered alphabetically)
124
   */
125
  struct literal_spec *literal_specs;
126
  uint32_t literal_specs_num, literal_specs_alloc;
127
128
  /*
129
   * Array of regular expression specifications (order preserved from input)
130
   */
131
  struct regex_spec *regex_specs;
132
  uint32_t regex_specs_num, regex_specs_alloc;
133
134
  /*
135
   * Array of child nodes (ordered alphabetically)
136
   */
137
  struct spec_node *children;
138
  uint32_t children_num, children_alloc;
139
140
  /* length of the stem (reordered to minimize padding) */
141
  uint16_t stem_len;
142
143
  /* whether this node is from an mmap of the data */
144
  bool from_mmap;
145
};
146
147
/* Where we map the file in during selabel_open() */
148
struct mmap_area {
149
  void *addr;   /* Start addr + len used to release memory at close */
150
  size_t len;
151
  void *next_addr;  /* Incremented by next_entry() */
152
  size_t next_len;  /* Decremented by next_entry() */
153
  struct mmap_area *next;
154
};
155
156
/* Our stored configuration */
157
struct saved_data {
158
  /* Root specification node */
159
  struct spec_node *root;
160
161
  /* Number of file specifications */
162
  uint64_t num_specs;
163
164
  struct mmap_area *mmap_areas;
165
166
  /*
167
   * Array of distribution substitutions
168
   */
169
  struct selabel_sub *dist_subs;
170
  uint32_t dist_subs_num, dist_subs_alloc;
171
172
  /*
173
   * Array of local substitutions
174
   */
175
  struct selabel_sub *subs;
176
  uint32_t subs_num, subs_alloc;
177
};
178
179
void free_spec_node(struct spec_node *node);
180
void sort_spec_node(struct spec_node *node, struct spec_node *parent);
181
182
static inline mode_t string_to_file_kind(const char *mode)
183
2.72k
{
184
2.72k
  if (mode[0] != '-' || mode[1] == '\0' || mode[2] != '\0')
185
21
    return LABEL_FILE_KIND_INVALID;
186
2.69k
  switch (mode[1]) {
187
217
  case 'b':
188
217
    return LABEL_FILE_KIND_BLK;
189
238
  case 'c':
190
238
    return LABEL_FILE_KIND_CHR;
191
576
  case 'd':
192
576
    return LABEL_FILE_KIND_DIR;
193
204
  case 'p':
194
204
    return LABEL_FILE_KIND_FIFO;
195
513
  case 'l':
196
513
    return LABEL_FILE_KIND_LNK;
197
713
  case 's':
198
713
    return LABEL_FILE_KIND_SOCK;
199
232
  case '-':
200
232
    return LABEL_FILE_KIND_REG;
201
6
  default:
202
6
    return LABEL_FILE_KIND_INVALID;
203
2.69k
  }
204
2.69k
}
Unexecuted instantiation: selabel_file_compiled-fuzzer.c:string_to_file_kind
label_file.c:string_to_file_kind
Line
Count
Source
183
2.72k
{
184
2.72k
  if (mode[0] != '-' || mode[1] == '\0' || mode[2] != '\0')
185
21
    return LABEL_FILE_KIND_INVALID;
186
2.69k
  switch (mode[1]) {
187
217
  case 'b':
188
217
    return LABEL_FILE_KIND_BLK;
189
238
  case 'c':
190
238
    return LABEL_FILE_KIND_CHR;
191
576
  case 'd':
192
576
    return LABEL_FILE_KIND_DIR;
193
204
  case 'p':
194
204
    return LABEL_FILE_KIND_FIFO;
195
513
  case 'l':
196
513
    return LABEL_FILE_KIND_LNK;
197
713
  case 's':
198
713
    return LABEL_FILE_KIND_SOCK;
199
232
  case '-':
200
232
    return LABEL_FILE_KIND_REG;
201
6
  default:
202
6
    return LABEL_FILE_KIND_INVALID;
203
2.69k
  }
204
2.69k
}
Unexecuted instantiation: regex.c:string_to_file_kind
Unexecuted instantiation: selabel_file_text-fuzzer.c:string_to_file_kind
205
206
static inline const char* file_kind_to_string(uint8_t file_kind)
207
0
{
208
0
  switch (file_kind) {
209
0
  case LABEL_FILE_KIND_BLK:
210
0
    return "block-device";
211
0
  case LABEL_FILE_KIND_CHR:
212
0
    return "character-device";
213
0
  case LABEL_FILE_KIND_DIR:
214
0
    return "directory";
215
0
  case LABEL_FILE_KIND_FIFO:
216
0
    return "fifo-file";
217
0
  case LABEL_FILE_KIND_LNK:
218
0
    return "symlink";
219
0
  case LABEL_FILE_KIND_SOCK:
220
0
    return "sock-file";
221
0
  case LABEL_FILE_KIND_REG:
222
0
    return "regular-file";
223
0
  case LABEL_FILE_KIND_ALL:
224
0
    return "wildcard";
225
0
  default:
226
0
    return "(invalid)";
227
0
  }
228
0
}
Unexecuted instantiation: selabel_file_compiled-fuzzer.c:file_kind_to_string
Unexecuted instantiation: label_file.c:file_kind_to_string
Unexecuted instantiation: regex.c:file_kind_to_string
Unexecuted instantiation: selabel_file_text-fuzzer.c:file_kind_to_string
229
230
/*
231
 * Determine whether the regular expression specification has any meta characters
232
 * or any unsupported escape sequence.
233
 */
234
static bool regex_has_meta_chars(const char *regex, size_t *prefix_len, const char *path, unsigned int lineno)
235
4.50M
{
236
4.50M
  const char *p = regex;
237
4.50M
  size_t plen = 0;
238
239
11.6M
  for (;*p != '\0'; p++, plen++) {
240
8.64M
    switch(*p) {
241
3.14k
    case '.':
242
30.6k
    case '^':
243
273k
    case '$':
244
274k
    case '?':
245
320k
    case '*':
246
320k
    case '+':
247
1.52M
    case '|':
248
1.52M
    case '[':
249
1.52M
    case '(':
250
1.52M
    case '{':
251
1.53M
    case ']':
252
1.53M
    case ')':
253
1.53M
    case '}':
254
1.53M
      *prefix_len = plen;
255
1.53M
      return true;
256
58.9k
    case '\\':
257
58.9k
      p++;
258
58.9k
      switch (*p) {
259
      /* curated list of supported characters */
260
1.11k
      case '.':
261
1.77k
      case '^':
262
3.36k
      case '$':
263
4.10k
      case '?':
264
6.09k
      case '*':
265
6.44k
      case '+':
266
6.99k
      case '|':
267
7.58k
      case '[':
268
8.63k
      case '(':
269
9.04k
      case '{':
270
9.26k
      case ']':
271
9.53k
      case ')':
272
9.82k
      case '}':
273
16.5k
      case '-':
274
17.9k
      case '_':
275
55.8k
      case ',':
276
55.8k
        continue;
277
3.09k
      default:
278
3.09k
        COMPAT_LOG(SELINUX_INFO, "%s:  line %u has unsupported escaped character %c (%#x) for literal matching, continuing using regex\n",
279
3.09k
             path, lineno, isprint((unsigned char)*p) ? *p : '?', *p);
280
3.09k
        *prefix_len = plen;
281
3.09k
        return true;
282
58.9k
      }
283
8.64M
    }
284
8.64M
  }
285
286
2.97M
  *prefix_len = plen;
287
2.97M
  return false;
288
4.50M
}
Unexecuted instantiation: selabel_file_compiled-fuzzer.c:regex_has_meta_chars
label_file.c:regex_has_meta_chars
Line
Count
Source
235
4.50M
{
236
4.50M
  const char *p = regex;
237
4.50M
  size_t plen = 0;
238
239
11.6M
  for (;*p != '\0'; p++, plen++) {
240
8.64M
    switch(*p) {
241
3.14k
    case '.':
242
30.6k
    case '^':
243
273k
    case '$':
244
274k
    case '?':
245
320k
    case '*':
246
320k
    case '+':
247
1.52M
    case '|':
248
1.52M
    case '[':
249
1.52M
    case '(':
250
1.52M
    case '{':
251
1.53M
    case ']':
252
1.53M
    case ')':
253
1.53M
    case '}':
254
1.53M
      *prefix_len = plen;
255
1.53M
      return true;
256
58.9k
    case '\\':
257
58.9k
      p++;
258
58.9k
      switch (*p) {
259
      /* curated list of supported characters */
260
1.11k
      case '.':
261
1.77k
      case '^':
262
3.36k
      case '$':
263
4.10k
      case '?':
264
6.09k
      case '*':
265
6.44k
      case '+':
266
6.99k
      case '|':
267
7.58k
      case '[':
268
8.63k
      case '(':
269
9.04k
      case '{':
270
9.26k
      case ']':
271
9.53k
      case ')':
272
9.82k
      case '}':
273
16.5k
      case '-':
274
17.9k
      case '_':
275
55.8k
      case ',':
276
55.8k
        continue;
277
3.09k
      default:
278
3.09k
        COMPAT_LOG(SELINUX_INFO, "%s:  line %u has unsupported escaped character %c (%#x) for literal matching, continuing using regex\n",
279
3.09k
             path, lineno, isprint((unsigned char)*p) ? *p : '?', *p);
280
3.09k
        *prefix_len = plen;
281
3.09k
        return true;
282
58.9k
      }
283
8.64M
    }
284
8.64M
  }
285
286
2.97M
  *prefix_len = plen;
287
2.97M
  return false;
288
4.50M
}
Unexecuted instantiation: regex.c:regex_has_meta_chars
Unexecuted instantiation: selabel_file_text-fuzzer.c:regex_has_meta_chars
289
290
static int regex_simplify(const char *regex, size_t len, char **out, const char *path, unsigned int lineno)
291
3.08M
{
292
3.08M
  char *result, *p;
293
3.08M
  size_t i = 0;
294
295
3.08M
  result = malloc(len + 1);
296
3.08M
  if (!result)
297
0
    return -1;
298
299
3.08M
  p = result;
300
11.0M
  while (i < len) {
301
7.96M
    switch(regex[i]) {
302
835
    case '.':
303
1.06k
    case '^':
304
1.50k
    case '$':
305
1.75k
    case '?':
306
12.8k
    case '*':
307
13.0k
    case '+':
308
13.5k
    case '|':
309
13.6k
    case '[':
310
13.9k
    case '(':
311
14.1k
    case '{':
312
14.3k
    case ']':
313
14.5k
    case ')':
314
14.7k
    case '}':
315
14.7k
      free(result);
316
14.7k
      return 0;
317
52.9k
    case '\\':
318
52.9k
      i++;
319
52.9k
      if (i >= len) {
320
448
        COMPAT_LOG(SELINUX_WARNING, "%s:  line %u has unsupported final escape character\n",
321
448
             path, lineno);
322
448
        free(result);
323
448
        return 0;
324
448
      }
325
52.4k
      switch (regex[i]) {
326
      /* curated list of supported characters */
327
1.11k
      case '.':
328
1.75k
      case '^':
329
2.99k
      case '$':
330
3.77k
      case '?':
331
5.14k
      case '*':
332
5.35k
      case '+':
333
5.65k
      case '|':
334
6.22k
      case '[':
335
6.95k
      case '(':
336
7.36k
      case '{':
337
7.59k
      case ']':
338
7.81k
      case ')':
339
8.10k
      case '}':
340
13.2k
      case '-':
341
14.5k
      case '_':
342
52.2k
      case ',':
343
52.2k
        *p++ = regex[i++];
344
52.2k
        break;
345
230
      default:
346
        /* regex_has_meta_chars() reported already the notable occurrences */
347
230
        free(result);
348
230
        return 0;
349
52.4k
      }
350
52.2k
      break;
351
7.89M
    default:
352
7.89M
      *p++ = regex[i++];
353
7.96M
    }
354
7.96M
  }
355
356
3.06M
  *p = '\0';
357
3.06M
  *out = result;
358
3.06M
  return 1;
359
3.08M
}
Unexecuted instantiation: selabel_file_compiled-fuzzer.c:regex_simplify
label_file.c:regex_simplify
Line
Count
Source
291
3.08M
{
292
3.08M
  char *result, *p;
293
3.08M
  size_t i = 0;
294
295
3.08M
  result = malloc(len + 1);
296
3.08M
  if (!result)
297
0
    return -1;
298
299
3.08M
  p = result;
300
11.0M
  while (i < len) {
301
7.96M
    switch(regex[i]) {
302
835
    case '.':
303
1.06k
    case '^':
304
1.50k
    case '$':
305
1.75k
    case '?':
306
12.8k
    case '*':
307
13.0k
    case '+':
308
13.5k
    case '|':
309
13.6k
    case '[':
310
13.9k
    case '(':
311
14.1k
    case '{':
312
14.3k
    case ']':
313
14.5k
    case ')':
314
14.7k
    case '}':
315
14.7k
      free(result);
316
14.7k
      return 0;
317
52.9k
    case '\\':
318
52.9k
      i++;
319
52.9k
      if (i >= len) {
320
448
        COMPAT_LOG(SELINUX_WARNING, "%s:  line %u has unsupported final escape character\n",
321
448
             path, lineno);
322
448
        free(result);
323
448
        return 0;
324
448
      }
325
52.4k
      switch (regex[i]) {
326
      /* curated list of supported characters */
327
1.11k
      case '.':
328
1.75k
      case '^':
329
2.99k
      case '$':
330
3.77k
      case '?':
331
5.14k
      case '*':
332
5.35k
      case '+':
333
5.65k
      case '|':
334
6.22k
      case '[':
335
6.95k
      case '(':
336
7.36k
      case '{':
337
7.59k
      case ']':
338
7.81k
      case ')':
339
8.10k
      case '}':
340
13.2k
      case '-':
341
14.5k
      case '_':
342
52.2k
      case ',':
343
52.2k
        *p++ = regex[i++];
344
52.2k
        break;
345
230
      default:
346
        /* regex_has_meta_chars() reported already the notable occurrences */
347
230
        free(result);
348
230
        return 0;
349
52.4k
      }
350
52.2k
      break;
351
7.89M
    default:
352
7.89M
      *p++ = regex[i++];
353
7.96M
    }
354
7.96M
  }
355
356
3.06M
  *p = '\0';
357
3.06M
  *out = result;
358
3.06M
  return 1;
359
3.08M
}
Unexecuted instantiation: regex.c:regex_simplify
Unexecuted instantiation: selabel_file_text-fuzzer.c:regex_simplify
360
361
static inline int compare_literal_spec(const void *p1, const void *p2)
362
28.8M
{
363
28.8M
  const struct literal_spec *l1 = p1;
364
28.8M
  const struct literal_spec *l2 = p2;
365
28.8M
  int ret;
366
367
28.8M
  ret = strcmp(l1->literal_match, l2->literal_match);
368
28.8M
  if (ret)
369
5.22M
    return ret;
370
371
  /* Order by input number (higher number means added later, means higher priority) */
372
23.6M
  ret = spaceship_cmp(l1->inputno, l2->inputno);
373
23.6M
  return -ret;
374
28.8M
}
Unexecuted instantiation: selabel_file_compiled-fuzzer.c:compare_literal_spec
label_file.c:compare_literal_spec
Line
Count
Source
362
28.8M
{
363
28.8M
  const struct literal_spec *l1 = p1;
364
28.8M
  const struct literal_spec *l2 = p2;
365
28.8M
  int ret;
366
367
28.8M
  ret = strcmp(l1->literal_match, l2->literal_match);
368
28.8M
  if (ret)
369
5.22M
    return ret;
370
371
  /* Order by input number (higher number means added later, means higher priority) */
372
23.6M
  ret = spaceship_cmp(l1->inputno, l2->inputno);
373
23.6M
  return -ret;
374
28.8M
}
Unexecuted instantiation: regex.c:compare_literal_spec
Unexecuted instantiation: selabel_file_text-fuzzer.c:compare_literal_spec
375
376
static inline int compare_spec_node(const void *p1, const void *p2)
377
73.4k
{
378
73.4k
  const struct spec_node *n1 = p1;
379
73.4k
  const struct spec_node *n2 = p2;
380
73.4k
  int rc;
381
382
73.4k
  rc = strcmp(n1->stem, n2->stem);
383
  /* There should not be two nodes with the same stem in the same array */
384
73.4k
  assert(rc != 0);
385
73.4k
  return rc;
386
73.4k
}
Unexecuted instantiation: selabel_file_compiled-fuzzer.c:compare_spec_node
label_file.c:compare_spec_node
Line
Count
Source
377
73.4k
{
378
73.4k
  const struct spec_node *n1 = p1;
379
73.4k
  const struct spec_node *n2 = p2;
380
73.4k
  int rc;
381
382
73.4k
  rc = strcmp(n1->stem, n2->stem);
383
  /* There should not be two nodes with the same stem in the same array */
384
73.4k
  assert(rc != 0);
385
73.4k
  return rc;
386
73.4k
}
Unexecuted instantiation: regex.c:compare_spec_node
Unexecuted instantiation: selabel_file_text-fuzzer.c:compare_spec_node
387
388
static inline void sort_specs(struct saved_data *data)
389
5.24k
{
390
5.24k
  sort_spec_node(data->root, NULL);
391
5.24k
}
selabel_file_compiled-fuzzer.c:sort_specs
Line
Count
Source
389
2.32k
{
390
2.32k
  sort_spec_node(data->root, NULL);
391
2.32k
}
Unexecuted instantiation: label_file.c:sort_specs
Unexecuted instantiation: regex.c:sort_specs
selabel_file_text-fuzzer.c:sort_specs
Line
Count
Source
389
2.92k
{
390
2.92k
  sort_spec_node(data->root, NULL);
391
2.92k
}
392
393
static int compile_regex(struct regex_spec *spec, char *errbuf, size_t errbuf_size)
394
2.19M
{
395
2.19M
  const char *reg_buf;
396
2.19M
  char *anchored_regex, *cp;
397
2.19M
  struct regex_error_data error_data;
398
2.19M
  size_t len;
399
2.19M
  int rc;
400
2.19M
  bool regex_compiled;
401
402
2.19M
  if (!errbuf || errbuf_size == 0) {
403
0
      errno = EINVAL;
404
0
      return -1;
405
0
  }
406
407
2.19M
  *errbuf = '\0';
408
409
  /* We really want pthread_once() here, but since its
410
   * init_routine does not take a parameter, it's not possible
411
   * to use, so we generate the same effect with atomics and a
412
   * mutex */
413
2.19M
#ifdef __ATOMIC_RELAXED
414
2.19M
  regex_compiled =
415
2.19M
    __atomic_load_n(&spec->regex_compiled, __ATOMIC_ACQUIRE);
416
#else
417
  /* GCC <4.7 */
418
  __sync_synchronize();
419
  regex_compiled = spec->regex_compiled;
420
#endif
421
2.19M
  if (regex_compiled) {
422
657k
    return 0; /* already done */
423
657k
  }
424
425
1.53M
  __pthread_mutex_lock(&spec->regex_lock);
426
  /* Check if another thread compiled the regex while we waited
427
   * on the mutex */
428
1.53M
#ifdef __ATOMIC_RELAXED
429
1.53M
  regex_compiled =
430
1.53M
    __atomic_load_n(&spec->regex_compiled, __ATOMIC_ACQUIRE);
431
#else
432
  /* GCC <4.7 */
433
  __sync_synchronize();
434
  regex_compiled = spec->regex_compiled;
435
#endif
436
1.53M
  if (regex_compiled) {
437
0
    __pthread_mutex_unlock(&spec->regex_lock);
438
0
    return 0;
439
0
  }
440
441
1.53M
  reg_buf = spec->regex_str;
442
  /* Anchor the regular expression. */
443
1.53M
  len = strlen(reg_buf);
444
  /* Use a sufficient large upper bound for regular expression lengths
445
   * to limit the compilation time on malformed inputs. */
446
1.53M
  if (len >= 4096) {
447
49
    __pthread_mutex_unlock(&spec->regex_lock);
448
49
    snprintf(errbuf, errbuf_size, "regex of length %zu too long", len);
449
49
    errno = EINVAL;
450
49
    return -1;
451
49
  }
452
1.53M
  cp = anchored_regex = malloc(len + 3);
453
1.53M
  if (!anchored_regex) {
454
0
    __pthread_mutex_unlock(&spec->regex_lock);
455
0
    snprintf(errbuf, errbuf_size, "out of memory");
456
0
    return -1;
457
0
  }
458
459
  /* Create ^...$ regexp.  */
460
1.53M
  *cp++ = '^';
461
1.53M
  memcpy(cp, reg_buf, len);
462
1.53M
  cp += len;
463
1.53M
  *cp++ = '$';
464
1.53M
  *cp = '\0';
465
466
  /* Compile the regular expression. */
467
1.53M
  rc = regex_prepare_data(&spec->regex, anchored_regex, &error_data);
468
1.53M
  free(anchored_regex);
469
1.53M
  if (rc < 0) {
470
154
    regex_format_error(&error_data, errbuf, errbuf_size);
471
154
    __pthread_mutex_unlock(&spec->regex_lock);
472
154
    errno = EINVAL;
473
154
    return -1;
474
154
  }
475
476
  /* Done. */
477
1.53M
#ifdef __ATOMIC_RELAXED
478
1.53M
  __atomic_store_n(&spec->regex_compiled, true, __ATOMIC_RELEASE);
479
#else
480
  /* GCC <4.7 */
481
  spec->regex_compiled = true;
482
  __sync_synchronize();
483
#endif
484
1.53M
  __pthread_mutex_unlock(&spec->regex_lock);
485
1.53M
  return 0;
486
1.53M
}
Unexecuted instantiation: selabel_file_compiled-fuzzer.c:compile_regex
label_file.c:compile_regex
Line
Count
Source
394
2.19M
{
395
2.19M
  const char *reg_buf;
396
2.19M
  char *anchored_regex, *cp;
397
2.19M
  struct regex_error_data error_data;
398
2.19M
  size_t len;
399
2.19M
  int rc;
400
2.19M
  bool regex_compiled;
401
402
2.19M
  if (!errbuf || errbuf_size == 0) {
403
0
      errno = EINVAL;
404
0
      return -1;
405
0
  }
406
407
2.19M
  *errbuf = '\0';
408
409
  /* We really want pthread_once() here, but since its
410
   * init_routine does not take a parameter, it's not possible
411
   * to use, so we generate the same effect with atomics and a
412
   * mutex */
413
2.19M
#ifdef __ATOMIC_RELAXED
414
2.19M
  regex_compiled =
415
2.19M
    __atomic_load_n(&spec->regex_compiled, __ATOMIC_ACQUIRE);
416
#else
417
  /* GCC <4.7 */
418
  __sync_synchronize();
419
  regex_compiled = spec->regex_compiled;
420
#endif
421
2.19M
  if (regex_compiled) {
422
657k
    return 0; /* already done */
423
657k
  }
424
425
1.53M
  __pthread_mutex_lock(&spec->regex_lock);
426
  /* Check if another thread compiled the regex while we waited
427
   * on the mutex */
428
1.53M
#ifdef __ATOMIC_RELAXED
429
1.53M
  regex_compiled =
430
1.53M
    __atomic_load_n(&spec->regex_compiled, __ATOMIC_ACQUIRE);
431
#else
432
  /* GCC <4.7 */
433
  __sync_synchronize();
434
  regex_compiled = spec->regex_compiled;
435
#endif
436
1.53M
  if (regex_compiled) {
437
0
    __pthread_mutex_unlock(&spec->regex_lock);
438
0
    return 0;
439
0
  }
440
441
1.53M
  reg_buf = spec->regex_str;
442
  /* Anchor the regular expression. */
443
1.53M
  len = strlen(reg_buf);
444
  /* Use a sufficient large upper bound for regular expression lengths
445
   * to limit the compilation time on malformed inputs. */
446
1.53M
  if (len >= 4096) {
447
49
    __pthread_mutex_unlock(&spec->regex_lock);
448
49
    snprintf(errbuf, errbuf_size, "regex of length %zu too long", len);
449
49
    errno = EINVAL;
450
49
    return -1;
451
49
  }
452
1.53M
  cp = anchored_regex = malloc(len + 3);
453
1.53M
  if (!anchored_regex) {
454
0
    __pthread_mutex_unlock(&spec->regex_lock);
455
0
    snprintf(errbuf, errbuf_size, "out of memory");
456
0
    return -1;
457
0
  }
458
459
  /* Create ^...$ regexp.  */
460
1.53M
  *cp++ = '^';
461
1.53M
  memcpy(cp, reg_buf, len);
462
1.53M
  cp += len;
463
1.53M
  *cp++ = '$';
464
1.53M
  *cp = '\0';
465
466
  /* Compile the regular expression. */
467
1.53M
  rc = regex_prepare_data(&spec->regex, anchored_regex, &error_data);
468
1.53M
  free(anchored_regex);
469
1.53M
  if (rc < 0) {
470
154
    regex_format_error(&error_data, errbuf, errbuf_size);
471
154
    __pthread_mutex_unlock(&spec->regex_lock);
472
154
    errno = EINVAL;
473
154
    return -1;
474
154
  }
475
476
  /* Done. */
477
1.53M
#ifdef __ATOMIC_RELAXED
478
1.53M
  __atomic_store_n(&spec->regex_compiled, true, __ATOMIC_RELEASE);
479
#else
480
  /* GCC <4.7 */
481
  spec->regex_compiled = true;
482
  __sync_synchronize();
483
#endif
484
1.53M
  __pthread_mutex_unlock(&spec->regex_lock);
485
1.53M
  return 0;
486
1.53M
}
Unexecuted instantiation: regex.c:compile_regex
Unexecuted instantiation: selabel_file_text-fuzzer.c:compile_regex
487
488
4.52M
#define GROW_ARRAY(arr) ({                                                                  \
489
4.52M
  int ret_;                                                                           \
490
4.52M
  if ((arr ## _num) < (arr ## _alloc)) {                                              \
491
4.49M
    ret_ = 0;                                                                   \
492
4.49M
  } else {                                                                            \
493
24.9k
    size_t addedsize_ = ((arr ## _alloc) >> 1) + ((arr ## _alloc >> 4)) + 4;    \
494
24.9k
    size_t newsize_ = addedsize_ + (arr ## _alloc);                             \
495
24.9k
    if (newsize_ < (arr ## _alloc) || newsize_ >= (typeof(arr ## _alloc))-1) {  \
496
0
      errno = EOVERFLOW;                                                  \
497
0
      ret_ = -1;                                                          \
498
24.9k
    } else {                                                                    \
499
24.9k
      typeof(arr) tmp_ = reallocarray(arr, newsize_, sizeof(*(arr)));     \
500
24.9k
      if (!tmp_) {                                                        \
501
0
        ret_ = -1;                                                  \
502
24.9k
      } else {                                                            \
503
24.9k
        (arr) = tmp_;                                               \
504
24.9k
        (arr ## _alloc) = newsize_;                                 \
505
24.9k
        ret_ = 0;                                                   \
506
24.9k
      }                                                                   \
507
24.9k
    }                                                                           \
508
24.9k
  }                                                                                   \
509
4.52M
  ret_;                                                                               \
510
4.52M
})
511
512
static int insert_spec(const struct selabel_handle *rec, struct saved_data *data,
513
           const char *prefix, char *regex, uint8_t file_kind, char *context,
514
           const char *path, uint8_t inputno, uint32_t lineno)
515
4.50M
{
516
4.50M
  size_t prefix_len;
517
4.50M
  bool has_meta;
518
519
4.50M
  if (data->num_specs == UINT64_MAX) {
520
0
    free(regex);
521
0
    free(context);
522
0
    errno = EOVERFLOW;
523
0
    return -1;
524
0
  }
525
526
4.50M
  has_meta = regex_has_meta_chars(regex, &prefix_len, path, lineno);
527
528
  /* Ensured by read_spec_entry() */
529
4.50M
  assert(prefix_len < UINT16_MAX);
530
531
4.50M
  if (has_meta) {
532
1.53M
    struct spec_node *node = data->root;
533
1.53M
    const char *p = regex;
534
1.53M
    uint32_t id;
535
1.53M
    int depth = 0, rc;
536
537
1.63M
    while (depth < SPEC_NODE_MAX_DEPTH) {
538
1.63M
      const char *q;
539
1.63M
      size_t regex_stem_len, stem_len;
540
1.63M
      char *stem = NULL;
541
1.63M
      bool child_found;
542
543
1.63M
      q = strchr(p + 1, '/');
544
1.63M
      if (!q)
545
1.51M
        break;
546
547
114k
      regex_stem_len = q - p - 1;
548
      /* Double slashes */
549
114k
      if (regex_stem_len == 0) {
550
3.17k
        p = q;
551
3.17k
        continue;
552
3.17k
      }
553
554
111k
      rc = regex_simplify(p + 1, regex_stem_len, &stem, path, lineno);
555
111k
      if (rc < 0) {
556
0
        free(regex);
557
0
        free(context);
558
0
        return -1;
559
0
      }
560
111k
      if (rc == 0)
561
15.4k
        break;
562
563
96.1k
      stem_len = strlen(stem);
564
96.1k
      if (stem_len >= UINT16_MAX) {
565
0
        free(stem);
566
0
        break;
567
0
      }
568
569
96.1k
      if (depth == 0 && prefix && strcmp(prefix + 1, stem) != 0) {
570
0
        free(stem);
571
0
        free(regex);
572
0
        free(context);
573
0
        return 0;
574
0
      }
575
576
96.1k
      child_found = false;
577
1.17M
      for (uint32_t i = 0; i < node->children_num; i++) {
578
1.16M
        if (node->children[i].stem_len == stem_len && strncmp(node->children[i].stem, stem, stem_len) == 0) {
579
89.3k
          child_found = true;
580
89.3k
          node = &node->children[i];
581
89.3k
          break;
582
89.3k
        }
583
1.16M
      }
584
585
96.1k
      if (!child_found) {
586
6.83k
        rc = GROW_ARRAY(node->children);
587
6.83k
        if (rc) {
588
0
          free(stem);
589
0
          free(regex);
590
0
          free(context);
591
0
          return -1;
592
0
        }
593
594
6.83k
        id = node->children_num++;
595
6.83k
        node->children[id] = (struct spec_node) {
596
6.83k
          .stem = stem,
597
6.83k
          .stem_len = stem_len,
598
6.83k
        };
599
600
6.83k
        node = &node->children[id];
601
89.3k
      } else {
602
89.3k
        free(stem);
603
89.3k
      }
604
605
96.1k
      p += regex_stem_len + 1;
606
96.1k
      depth++;
607
96.1k
    }
608
609
1.53M
    rc = GROW_ARRAY(node->regex_specs);
610
1.53M
    if (rc) {
611
0
      free(regex);
612
0
      free(context);
613
0
      return -1;
614
0
    }
615
616
1.53M
    id = node->regex_specs_num++;
617
618
1.53M
    node->regex_specs[id] = (struct regex_spec) {
619
1.53M
      .regex_str = regex,
620
1.53M
      .prefix_len = prefix_len,
621
1.53M
      .regex_compiled = false,
622
1.53M
      .regex_lock = PTHREAD_MUTEX_INITIALIZER,
623
1.53M
      .file_kind = file_kind,
624
1.53M
      .any_matches = false,
625
1.53M
      .inputno = inputno,
626
1.53M
      .lineno = lineno,
627
1.53M
      .lr.ctx_raw = context,
628
1.53M
      .lr.ctx_trans = NULL,
629
1.53M
      .lr.lineno = lineno,
630
1.53M
      .lr.validated = false,
631
1.53M
      .lr.lock = PTHREAD_MUTEX_INITIALIZER,
632
1.53M
    };
633
634
1.53M
    data->num_specs++;
635
636
1.53M
    if (rec->validating) {
637
1.53M
      char errbuf[256];
638
639
1.53M
      if (compile_regex(&node->regex_specs[id], errbuf, sizeof(errbuf))) {
640
163
        COMPAT_LOG(SELINUX_ERROR,
641
163
             "%s:  line %u has invalid regex %s:  %s\n",
642
163
             path, lineno, regex, errbuf);
643
163
        return -1;
644
163
      }
645
646
1.53M
      if (strcmp(context, "<<none>>") != 0) {
647
1.53M
        rc = compat_validate(rec, &node->regex_specs[id].lr, path, lineno);
648
1.53M
        if (rc < 0)
649
0
          return rc;
650
1.53M
      }
651
1.53M
    }
652
2.97M
  } else { /* !has_meta */
653
2.97M
    struct spec_node *node = data->root;
654
2.97M
    char *literal_regex = NULL;
655
2.97M
    const char *p;
656
2.97M
    uint32_t id;
657
2.97M
    int depth = 0, rc;
658
659
2.97M
    rc = regex_simplify(regex, strlen(regex), &literal_regex, path, lineno);
660
2.97M
    if (rc != 1) {
661
0
      if (rc == 0) {
662
0
        COMPAT_LOG(SELINUX_ERROR,
663
0
             "%s:  line %u failed to simplify regex %s\n",
664
0
             path, lineno, regex);
665
0
        errno = EINVAL;
666
0
      }
667
0
      free(regex);
668
0
      free(context);
669
0
      return -1;
670
0
    }
671
672
2.97M
    p = literal_regex;
673
674
3.34M
    while (depth < SPEC_NODE_MAX_DEPTH) {
675
3.24M
      const char *q;
676
3.24M
      size_t length;
677
3.24M
      char *stem;
678
3.24M
      bool child_found;
679
680
3.24M
      if (*p != '/')
681
98.9k
        break;
682
683
3.14M
      q = strchr(p + 1, '/');
684
3.14M
      if (!q)
685
2.77M
        break;
686
687
376k
      length = q - p - 1;
688
      /* Double slashes */
689
376k
      if (length == 0) {
690
13.1k
        p = q;
691
13.1k
        continue;
692
13.1k
      }
693
694
      /* Ensured by read_spec_entry() */
695
363k
      assert(length < UINT16_MAX);
696
697
363k
      if (depth == 0 && prefix && strncmp(prefix + 1, p + 1, length) != 0) {
698
0
        free(literal_regex);
699
0
        free(regex);
700
0
        free(context);
701
0
        return 0;
702
0
      }
703
704
363k
      child_found = false;
705
1.61M
      for (uint32_t i = 0; i < node->children_num; i++) {
706
1.60M
        if (node->children[i].stem_len == length && strncmp(node->children[i].stem, p + 1, length) == 0) {
707
353k
          child_found = true;
708
353k
          node = &node->children[i];
709
353k
          break;
710
353k
        }
711
1.60M
      }
712
713
363k
      if (!child_found) {
714
9.81k
        rc = GROW_ARRAY(node->children);
715
9.81k
        if (rc) {
716
0
          free(literal_regex);
717
0
          free(regex);
718
0
          free(context);
719
0
          return -1;
720
0
        }
721
722
9.81k
        stem = strndup(p + 1, length);
723
9.81k
        if (!stem) {
724
0
          free(literal_regex);
725
0
          free(regex);
726
0
          free(context);
727
0
          return -1;
728
0
        }
729
730
9.81k
        id = node->children_num++;
731
9.81k
        node->children[id] = (struct spec_node) {
732
9.81k
          .stem = stem,
733
9.81k
          .stem_len = length,
734
9.81k
        };
735
736
9.81k
        node = &node->children[id];
737
9.81k
      }
738
739
363k
      p = q;
740
363k
      depth++;
741
363k
    }
742
743
2.97M
    rc = GROW_ARRAY(node->literal_specs);
744
2.97M
    if (rc) {
745
0
      free(literal_regex);
746
0
      free(regex);
747
0
      free(context);
748
0
      return -1;
749
0
    }
750
751
2.97M
    id = node->literal_specs_num++;
752
753
2.97M
    assert(prefix_len == strlen(literal_regex));
754
755
2.97M
    node->literal_specs[id] = (struct literal_spec) {
756
2.97M
      .regex_str = regex,
757
2.97M
      .prefix_len = prefix_len,
758
2.97M
      .literal_match = literal_regex,
759
2.97M
      .inputno = inputno,
760
2.97M
      .file_kind = file_kind,
761
2.97M
      .any_matches = false,
762
2.97M
      .lr.ctx_raw = context,
763
2.97M
      .lr.ctx_trans = NULL,
764
2.97M
      .lr.lineno = lineno,
765
2.97M
      .lr.validated = false,
766
2.97M
      .lr.lock = PTHREAD_MUTEX_INITIALIZER,
767
2.97M
    };
768
769
2.97M
    data->num_specs++;
770
771
2.97M
    if (rec->validating && strcmp(context, "<<none>>") != 0) {
772
2.97M
      rc = compat_validate(rec, &node->literal_specs[id].lr, path, lineno);
773
2.97M
      if (rc < 0)
774
0
        return rc;
775
2.97M
    }
776
777
2.97M
  }
778
779
4.50M
  return 0;
780
4.50M
}
Unexecuted instantiation: selabel_file_compiled-fuzzer.c:insert_spec
label_file.c:insert_spec
Line
Count
Source
515
4.50M
{
516
4.50M
  size_t prefix_len;
517
4.50M
  bool has_meta;
518
519
4.50M
  if (data->num_specs == UINT64_MAX) {
520
0
    free(regex);
521
0
    free(context);
522
0
    errno = EOVERFLOW;
523
0
    return -1;
524
0
  }
525
526
4.50M
  has_meta = regex_has_meta_chars(regex, &prefix_len, path, lineno);
527
528
  /* Ensured by read_spec_entry() */
529
4.50M
  assert(prefix_len < UINT16_MAX);
530
531
4.50M
  if (has_meta) {
532
1.53M
    struct spec_node *node = data->root;
533
1.53M
    const char *p = regex;
534
1.53M
    uint32_t id;
535
1.53M
    int depth = 0, rc;
536
537
1.63M
    while (depth < SPEC_NODE_MAX_DEPTH) {
538
1.63M
      const char *q;
539
1.63M
      size_t regex_stem_len, stem_len;
540
1.63M
      char *stem = NULL;
541
1.63M
      bool child_found;
542
543
1.63M
      q = strchr(p + 1, '/');
544
1.63M
      if (!q)
545
1.51M
        break;
546
547
114k
      regex_stem_len = q - p - 1;
548
      /* Double slashes */
549
114k
      if (regex_stem_len == 0) {
550
3.17k
        p = q;
551
3.17k
        continue;
552
3.17k
      }
553
554
111k
      rc = regex_simplify(p + 1, regex_stem_len, &stem, path, lineno);
555
111k
      if (rc < 0) {
556
0
        free(regex);
557
0
        free(context);
558
0
        return -1;
559
0
      }
560
111k
      if (rc == 0)
561
15.4k
        break;
562
563
96.1k
      stem_len = strlen(stem);
564
96.1k
      if (stem_len >= UINT16_MAX) {
565
0
        free(stem);
566
0
        break;
567
0
      }
568
569
96.1k
      if (depth == 0 && prefix && strcmp(prefix + 1, stem) != 0) {
570
0
        free(stem);
571
0
        free(regex);
572
0
        free(context);
573
0
        return 0;
574
0
      }
575
576
96.1k
      child_found = false;
577
1.17M
      for (uint32_t i = 0; i < node->children_num; i++) {
578
1.16M
        if (node->children[i].stem_len == stem_len && strncmp(node->children[i].stem, stem, stem_len) == 0) {
579
89.3k
          child_found = true;
580
89.3k
          node = &node->children[i];
581
89.3k
          break;
582
89.3k
        }
583
1.16M
      }
584
585
96.1k
      if (!child_found) {
586
6.83k
        rc = GROW_ARRAY(node->children);
587
6.83k
        if (rc) {
588
0
          free(stem);
589
0
          free(regex);
590
0
          free(context);
591
0
          return -1;
592
0
        }
593
594
6.83k
        id = node->children_num++;
595
6.83k
        node->children[id] = (struct spec_node) {
596
6.83k
          .stem = stem,
597
6.83k
          .stem_len = stem_len,
598
6.83k
        };
599
600
6.83k
        node = &node->children[id];
601
89.3k
      } else {
602
89.3k
        free(stem);
603
89.3k
      }
604
605
96.1k
      p += regex_stem_len + 1;
606
96.1k
      depth++;
607
96.1k
    }
608
609
1.53M
    rc = GROW_ARRAY(node->regex_specs);
610
1.53M
    if (rc) {
611
0
      free(regex);
612
0
      free(context);
613
0
      return -1;
614
0
    }
615
616
1.53M
    id = node->regex_specs_num++;
617
618
1.53M
    node->regex_specs[id] = (struct regex_spec) {
619
1.53M
      .regex_str = regex,
620
1.53M
      .prefix_len = prefix_len,
621
1.53M
      .regex_compiled = false,
622
1.53M
      .regex_lock = PTHREAD_MUTEX_INITIALIZER,
623
1.53M
      .file_kind = file_kind,
624
1.53M
      .any_matches = false,
625
1.53M
      .inputno = inputno,
626
1.53M
      .lineno = lineno,
627
1.53M
      .lr.ctx_raw = context,
628
1.53M
      .lr.ctx_trans = NULL,
629
1.53M
      .lr.lineno = lineno,
630
1.53M
      .lr.validated = false,
631
1.53M
      .lr.lock = PTHREAD_MUTEX_INITIALIZER,
632
1.53M
    };
633
634
1.53M
    data->num_specs++;
635
636
1.53M
    if (rec->validating) {
637
1.53M
      char errbuf[256];
638
639
1.53M
      if (compile_regex(&node->regex_specs[id], errbuf, sizeof(errbuf))) {
640
163
        COMPAT_LOG(SELINUX_ERROR,
641
163
             "%s:  line %u has invalid regex %s:  %s\n",
642
163
             path, lineno, regex, errbuf);
643
163
        return -1;
644
163
      }
645
646
1.53M
      if (strcmp(context, "<<none>>") != 0) {
647
1.53M
        rc = compat_validate(rec, &node->regex_specs[id].lr, path, lineno);
648
1.53M
        if (rc < 0)
649
0
          return rc;
650
1.53M
      }
651
1.53M
    }
652
2.97M
  } else { /* !has_meta */
653
2.97M
    struct spec_node *node = data->root;
654
2.97M
    char *literal_regex = NULL;
655
2.97M
    const char *p;
656
2.97M
    uint32_t id;
657
2.97M
    int depth = 0, rc;
658
659
2.97M
    rc = regex_simplify(regex, strlen(regex), &literal_regex, path, lineno);
660
2.97M
    if (rc != 1) {
661
0
      if (rc == 0) {
662
0
        COMPAT_LOG(SELINUX_ERROR,
663
0
             "%s:  line %u failed to simplify regex %s\n",
664
0
             path, lineno, regex);
665
0
        errno = EINVAL;
666
0
      }
667
0
      free(regex);
668
0
      free(context);
669
0
      return -1;
670
0
    }
671
672
2.97M
    p = literal_regex;
673
674
3.34M
    while (depth < SPEC_NODE_MAX_DEPTH) {
675
3.24M
      const char *q;
676
3.24M
      size_t length;
677
3.24M
      char *stem;
678
3.24M
      bool child_found;
679
680
3.24M
      if (*p != '/')
681
98.9k
        break;
682
683
3.14M
      q = strchr(p + 1, '/');
684
3.14M
      if (!q)
685
2.77M
        break;
686
687
376k
      length = q - p - 1;
688
      /* Double slashes */
689
376k
      if (length == 0) {
690
13.1k
        p = q;
691
13.1k
        continue;
692
13.1k
      }
693
694
      /* Ensured by read_spec_entry() */
695
363k
      assert(length < UINT16_MAX);
696
697
363k
      if (depth == 0 && prefix && strncmp(prefix + 1, p + 1, length) != 0) {
698
0
        free(literal_regex);
699
0
        free(regex);
700
0
        free(context);
701
0
        return 0;
702
0
      }
703
704
363k
      child_found = false;
705
1.61M
      for (uint32_t i = 0; i < node->children_num; i++) {
706
1.60M
        if (node->children[i].stem_len == length && strncmp(node->children[i].stem, p + 1, length) == 0) {
707
353k
          child_found = true;
708
353k
          node = &node->children[i];
709
353k
          break;
710
353k
        }
711
1.60M
      }
712
713
363k
      if (!child_found) {
714
9.81k
        rc = GROW_ARRAY(node->children);
715
9.81k
        if (rc) {
716
0
          free(literal_regex);
717
0
          free(regex);
718
0
          free(context);
719
0
          return -1;
720
0
        }
721
722
9.81k
        stem = strndup(p + 1, length);
723
9.81k
        if (!stem) {
724
0
          free(literal_regex);
725
0
          free(regex);
726
0
          free(context);
727
0
          return -1;
728
0
        }
729
730
9.81k
        id = node->children_num++;
731
9.81k
        node->children[id] = (struct spec_node) {
732
9.81k
          .stem = stem,
733
9.81k
          .stem_len = length,
734
9.81k
        };
735
736
9.81k
        node = &node->children[id];
737
9.81k
      }
738
739
363k
      p = q;
740
363k
      depth++;
741
363k
    }
742
743
2.97M
    rc = GROW_ARRAY(node->literal_specs);
744
2.97M
    if (rc) {
745
0
      free(literal_regex);
746
0
      free(regex);
747
0
      free(context);
748
0
      return -1;
749
0
    }
750
751
2.97M
    id = node->literal_specs_num++;
752
753
2.97M
    assert(prefix_len == strlen(literal_regex));
754
755
2.97M
    node->literal_specs[id] = (struct literal_spec) {
756
2.97M
      .regex_str = regex,
757
2.97M
      .prefix_len = prefix_len,
758
2.97M
      .literal_match = literal_regex,
759
2.97M
      .inputno = inputno,
760
2.97M
      .file_kind = file_kind,
761
2.97M
      .any_matches = false,
762
2.97M
      .lr.ctx_raw = context,
763
2.97M
      .lr.ctx_trans = NULL,
764
2.97M
      .lr.lineno = lineno,
765
2.97M
      .lr.validated = false,
766
2.97M
      .lr.lock = PTHREAD_MUTEX_INITIALIZER,
767
2.97M
    };
768
769
2.97M
    data->num_specs++;
770
771
2.97M
    if (rec->validating && strcmp(context, "<<none>>") != 0) {
772
2.97M
      rc = compat_validate(rec, &node->literal_specs[id].lr, path, lineno);
773
2.97M
      if (rc < 0)
774
0
        return rc;
775
2.97M
    }
776
777
2.97M
  }
778
779
4.50M
  return 0;
780
4.50M
}
Unexecuted instantiation: regex.c:insert_spec
Unexecuted instantiation: selabel_file_text-fuzzer.c:insert_spec
781
782
/* This will always check for buffer over-runs and either read the next entry
783
 * if buf != NULL or skip over the entry (as these areas are mapped in the
784
 * current buffer). */
785
static inline int next_entry(void *buf, struct mmap_area *fp, size_t bytes)
786
211k
{
787
211k
  if (bytes > fp->next_len)
788
9.39k
    return -1;
789
790
202k
  if (buf)
791
160k
    memcpy(buf, fp->next_addr, bytes);
792
793
202k
  fp->next_addr = (unsigned char *)fp->next_addr + bytes;
794
202k
  fp->next_len -= bytes;
795
202k
  return 0;
796
211k
}
Unexecuted instantiation: selabel_file_compiled-fuzzer.c:next_entry
label_file.c:next_entry
Line
Count
Source
786
199k
{
787
199k
  if (bytes > fp->next_len)
788
9.27k
    return -1;
789
790
190k
  if (buf)
791
154k
    memcpy(buf, fp->next_addr, bytes);
792
793
190k
  fp->next_addr = (unsigned char *)fp->next_addr + bytes;
794
190k
  fp->next_len -= bytes;
795
190k
  return 0;
796
199k
}
regex.c:next_entry
Line
Count
Source
786
12.3k
{
787
12.3k
  if (bytes > fp->next_len)
788
119
    return -1;
789
790
12.1k
  if (buf)
791
6.14k
    memcpy(buf, fp->next_addr, bytes);
792
793
12.1k
  fp->next_addr = (unsigned char *)fp->next_addr + bytes;
794
12.1k
  fp->next_len -= bytes;
795
12.1k
  return 0;
796
12.3k
}
Unexecuted instantiation: selabel_file_text-fuzzer.c:next_entry
797
798
/* This service is used by label_file.c process_file() and
799
 * utils/sefcontext_compile.c */
800
static inline int process_line(struct selabel_handle *rec,
801
             const char *path, const char *prefix,
802
             char *line_buf, size_t nread,
803
             uint8_t inputno, uint32_t lineno)
804
6.56M
{
805
6.56M
  int items;
806
6.56M
  char *regex = NULL, *type = NULL, *context = NULL;
807
6.56M
  struct saved_data *data = rec->data;
808
6.56M
  const char *errbuf = NULL;
809
6.56M
  uint8_t file_kind = LABEL_FILE_KIND_ALL;
810
811
6.56M
  if (prefix) {
812
0
    if (prefix[0] != '/' ||
813
0
        prefix[1] == '\0' ||
814
0
        strchr(prefix + 1, '/') != NULL) {
815
0
      errno = EINVAL;
816
0
      return -1;
817
0
    }
818
0
  }
819
820
6.56M
  items = read_spec_entries(line_buf, nread, &errbuf, 3, &regex, &type, &context);
821
6.56M
  if (items < 0) {
822
25
    if (errbuf) {
823
25
      COMPAT_LOG(SELINUX_ERROR,
824
25
           "%s:  line %u error due to: %s\n", path,
825
25
           lineno, errbuf);
826
25
    } else {
827
0
      COMPAT_LOG(SELINUX_ERROR,
828
0
           "%s:  line %u error due to: %m\n", path,
829
0
           lineno);
830
0
    }
831
25
    free(regex);
832
25
    free(type);
833
25
    free(context);
834
25
    return -1;
835
25
  }
836
837
6.56M
  if (items == 0)
838
2.06M
    return items;
839
840
4.50M
  if (items < 2) {
841
86
    COMPAT_LOG(SELINUX_ERROR,
842
86
         "%s:  line %u is missing fields\n", path,
843
86
         lineno);
844
86
    if (items == 1)
845
86
      free(regex);
846
86
    errno = EINVAL;
847
86
    return -1;
848
86
  }
849
850
4.50M
  if (items == 2) {
851
    /* The type field is optional. */
852
4.50M
    context = type;
853
4.50M
    type = NULL;
854
4.50M
  }
855
856
4.50M
  if (type) {
857
2.72k
    file_kind = string_to_file_kind(type);
858
859
2.72k
    if (file_kind == LABEL_FILE_KIND_INVALID) {
860
27
      COMPAT_LOG(SELINUX_ERROR,
861
27
           "%s:  line %u has invalid file type %s\n",
862
27
           path, lineno, type);
863
27
      free(regex);
864
27
      free(type);
865
27
      free(context);
866
27
      errno = EINVAL;
867
27
      return -1;
868
27
    }
869
870
2.69k
    free(type);
871
2.69k
  }
872
873
4.50M
  return insert_spec(rec, data, prefix, regex, file_kind, context, path, inputno, lineno);
874
4.50M
}
Unexecuted instantiation: selabel_file_compiled-fuzzer.c:process_line
label_file.c:process_line
Line
Count
Source
804
6.56M
{
805
6.56M
  int items;
806
6.56M
  char *regex = NULL, *type = NULL, *context = NULL;
807
6.56M
  struct saved_data *data = rec->data;
808
6.56M
  const char *errbuf = NULL;
809
6.56M
  uint8_t file_kind = LABEL_FILE_KIND_ALL;
810
811
6.56M
  if (prefix) {
812
0
    if (prefix[0] != '/' ||
813
0
        prefix[1] == '\0' ||
814
0
        strchr(prefix + 1, '/') != NULL) {
815
0
      errno = EINVAL;
816
0
      return -1;
817
0
    }
818
0
  }
819
820
6.56M
  items = read_spec_entries(line_buf, nread, &errbuf, 3, &regex, &type, &context);
821
6.56M
  if (items < 0) {
822
25
    if (errbuf) {
823
25
      COMPAT_LOG(SELINUX_ERROR,
824
25
           "%s:  line %u error due to: %s\n", path,
825
25
           lineno, errbuf);
826
25
    } else {
827
0
      COMPAT_LOG(SELINUX_ERROR,
828
0
           "%s:  line %u error due to: %m\n", path,
829
0
           lineno);
830
0
    }
831
25
    free(regex);
832
25
    free(type);
833
25
    free(context);
834
25
    return -1;
835
25
  }
836
837
6.56M
  if (items == 0)
838
2.06M
    return items;
839
840
4.50M
  if (items < 2) {
841
86
    COMPAT_LOG(SELINUX_ERROR,
842
86
         "%s:  line %u is missing fields\n", path,
843
86
         lineno);
844
86
    if (items == 1)
845
86
      free(regex);
846
86
    errno = EINVAL;
847
86
    return -1;
848
86
  }
849
850
4.50M
  if (items == 2) {
851
    /* The type field is optional. */
852
4.50M
    context = type;
853
4.50M
    type = NULL;
854
4.50M
  }
855
856
4.50M
  if (type) {
857
2.72k
    file_kind = string_to_file_kind(type);
858
859
2.72k
    if (file_kind == LABEL_FILE_KIND_INVALID) {
860
27
      COMPAT_LOG(SELINUX_ERROR,
861
27
           "%s:  line %u has invalid file type %s\n",
862
27
           path, lineno, type);
863
27
      free(regex);
864
27
      free(type);
865
27
      free(context);
866
27
      errno = EINVAL;
867
27
      return -1;
868
27
    }
869
870
2.69k
    free(type);
871
2.69k
  }
872
873
4.50M
  return insert_spec(rec, data, prefix, regex, file_kind, context, path, inputno, lineno);
874
4.50M
}
Unexecuted instantiation: regex.c:process_line
Unexecuted instantiation: selabel_file_text-fuzzer.c:process_line
875
876
#endif /* _SELABEL_FILE_H_ */