Coverage Report

Created: 2026-08-05 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/selinux/libselinux/src/label_file.c
Line
Count
Source
1
/*
2
 * File contexts backend for labeling system
3
 *
4
 * Author : Eamon Walsh <ewalsh@tycho.nsa.gov>
5
 * Author : Stephen Smalley <stephen.smalley.work@gmail.com>
6
 * Author : Christian Göttsche <cgzones@googlemail.com>
7
 */
8
9
#include <assert.h>
10
#include <endian.h>
11
#include <fcntl.h>
12
#include <stdarg.h>
13
#include <string.h>
14
#include <stdio.h>
15
#include <ctype.h>
16
#include <errno.h>
17
#include <limits.h>
18
#include <stdint.h>
19
#include <unistd.h>
20
#include <sys/mman.h>
21
#include <sys/types.h>
22
#include <sys/stat.h>
23
24
#include "callbacks.h"
25
#include "label_internal.h"
26
#include "selinux_internal.h"
27
#include "label_file.h"
28
29
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
30
#define FUZZ_EXTERN
31
#else
32
#define FUZZ_EXTERN static
33
#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
34
35
void free_spec_node(struct spec_node *node)
36
44.3k
{
37
2.71M
  for (uint32_t i = 0; i < node->literal_specs_num; i++) {
38
2.67M
    struct literal_spec *lspec = &node->literal_specs[i];
39
40
2.67M
    free(lspec->lr.ctx_raw);
41
2.67M
    free(lspec->lr.ctx_trans);
42
2.67M
    __pthread_mutex_destroy(&lspec->lr.lock);
43
44
2.67M
    if (lspec->from_mmap)
45
10.5k
      continue;
46
47
2.66M
    free(lspec->literal_match);
48
2.66M
    free(lspec->regex_str);
49
2.66M
  }
50
44.3k
  free(node->literal_specs);
51
52
1.42M
  for (uint32_t i = 0; i < node->regex_specs_num; i++) {
53
1.38M
    struct regex_spec *rspec = &node->regex_specs[i];
54
55
1.38M
    free(rspec->lr.ctx_raw);
56
1.38M
    free(rspec->lr.ctx_trans);
57
1.38M
    __pthread_mutex_destroy(&rspec->lr.lock);
58
1.38M
    regex_data_free(rspec->regex);
59
1.38M
    __pthread_mutex_destroy(&rspec->regex_lock);
60
61
1.38M
    if (rspec->from_mmap)
62
6.52k
      continue;
63
64
1.37M
    free(rspec->regex_str);
65
1.37M
  }
66
44.3k
  free(node->regex_specs);
67
68
74.4k
  for (uint32_t i = 0; i < node->children_num; i++)
69
30.0k
    free_spec_node(&node->children[i]);
70
44.3k
  free(node->children);
71
72
44.3k
  if (!node->from_mmap)
73
29.1k
    free(node->stem);
74
44.3k
}
75
76
void sort_spec_node(struct spec_node *node, struct spec_node *parent)
77
30.6k
{
78
  /* A node should not be its own parent */
79
30.6k
  assert(node != parent);
80
  /* Only root node has NULL stem */
81
30.6k
  assert((!parent && !node->stem) ||
82
30.6k
         (parent && node->stem && node->stem[0] != '\0'));
83
  /* A non-root node should not be empty */
84
30.6k
  assert(!parent || (node->literal_specs_num || node->regex_specs_num ||
85
30.6k
         node->children_num));
86
87
30.6k
  node->parent = parent;
88
89
  /*
90
   * Sort for comparison support and binary search lookup,
91
   * except for regex specs which are matched in reverse input order.
92
   */
93
94
30.6k
  if (node->literal_specs_num > 1)
95
2.80k
    qsort(node->literal_specs, node->literal_specs_num,
96
2.80k
          sizeof(struct literal_spec), compare_literal_spec);
97
98
30.6k
  if (node->children_num > 1)
99
2.49k
    qsort(node->children, node->children_num,
100
2.49k
          sizeof(struct spec_node), compare_spec_node);
101
102
55.5k
  for (uint32_t i = 0; i < node->children_num; i++)
103
24.9k
    sort_spec_node(&node->children[i], node);
104
30.6k
}
105
106
/*
107
 * Warn about duplicate specifications.
108
 */
109
static int nodups_spec_node(const struct selabel_handle *rec,
110
          const struct spec_node *node)
111
0
{
112
0
  int rc = 0;
113
114
0
  if (node->literal_specs_num > 1) {
115
0
    for (uint32_t i = 0; i < node->literal_specs_num - 1; i++) {
116
0
      const struct literal_spec *node1 =
117
0
        &node->literal_specs[i];
118
0
      const struct literal_spec *node2 =
119
0
        &node->literal_specs[i + 1];
120
121
0
      if (strcmp(node1->literal_match,
122
0
           node2->literal_match) != 0)
123
0
        continue;
124
125
0
      if (node1->file_kind != LABEL_FILE_KIND_ALL &&
126
0
          node2->file_kind != LABEL_FILE_KIND_ALL &&
127
0
          node1->file_kind != node2->file_kind)
128
0
        continue;
129
130
0
      if (node1->inputno != node2->inputno)
131
0
        continue;
132
133
0
      rc = -1;
134
0
      errno = EINVAL;
135
0
      if (strcmp(node1->lr.ctx_raw, node2->lr.ctx_raw) != 0) {
136
0
        COMPAT_LOG(
137
0
          SELINUX_ERROR,
138
0
          "%s: Multiple different specifications for %s %s  (%s and %s).\n",
139
0
          rec->spec_files[node1->inputno],
140
0
          file_kind_to_string(node1->file_kind),
141
0
          node1->literal_match, node1->lr.ctx_raw,
142
0
          node2->lr.ctx_raw);
143
0
      } else {
144
0
        COMPAT_LOG(
145
0
          SELINUX_ERROR,
146
0
          "%s: Multiple same specifications for %s %s.\n",
147
0
          rec->spec_files[node1->inputno],
148
0
          file_kind_to_string(node1->file_kind),
149
0
          node1->literal_match);
150
0
      }
151
0
    }
152
0
  }
153
154
0
  if (node->regex_specs_num > 1) {
155
0
    for (uint32_t i = 0; i < node->regex_specs_num - 1; i++) {
156
0
      for (uint32_t j = i; j < node->regex_specs_num - 1;
157
0
           j++) {
158
0
        const struct regex_spec *node1 =
159
0
          &node->regex_specs[i];
160
0
        const struct regex_spec *node2 =
161
0
          &node->regex_specs[j + 1];
162
163
0
        if (node1->prefix_len != node2->prefix_len)
164
0
          continue;
165
166
0
        if (strcmp(node1->regex_str,
167
0
             node2->regex_str) != 0)
168
0
          continue;
169
170
0
        if (node1->file_kind != LABEL_FILE_KIND_ALL &&
171
0
            node2->file_kind != LABEL_FILE_KIND_ALL &&
172
0
            node1->file_kind != node2->file_kind)
173
0
          continue;
174
175
0
        if (node1->inputno != node2->inputno)
176
0
          continue;
177
178
0
        rc = -1;
179
0
        errno = EINVAL;
180
0
        if (strcmp(node1->lr.ctx_raw,
181
0
             node2->lr.ctx_raw) != 0) {
182
0
          COMPAT_LOG(
183
0
            SELINUX_ERROR,
184
0
            "%s: Multiple different specifications for %s %s  (%s and %s).\n",
185
0
            rec->spec_files[node1->inputno],
186
0
            file_kind_to_string(
187
0
              node1->file_kind),
188
0
            node1->regex_str,
189
0
            node1->lr.ctx_raw,
190
0
            node2->lr.ctx_raw);
191
0
        } else {
192
0
          COMPAT_LOG(
193
0
            SELINUX_ERROR,
194
0
            "%s: Multiple same specifications for %s %s.\n",
195
0
            rec->spec_files[node1->inputno],
196
0
            file_kind_to_string(
197
0
              node1->file_kind),
198
0
            node1->regex_str);
199
0
        }
200
0
      }
201
0
    }
202
0
  }
203
204
0
  for (uint32_t i = 0; i < node->children_num; i++) {
205
0
    int rc2;
206
207
0
    rc2 = nodups_spec_node(rec, &node->children[i]);
208
0
    if (rc2)
209
0
      rc = rc2;
210
0
  }
211
212
0
  return rc;
213
0
}
214
215
FUZZ_EXTERN int process_text_file(FILE *fp, const char *prefix,
216
          struct selabel_handle *rec, const char *path,
217
          uint8_t inputno)
218
3.54k
{
219
3.54k
  int rc;
220
3.54k
  size_t line_len;
221
3.54k
  ssize_t nread;
222
3.54k
  unsigned int lineno = 0;
223
3.54k
  char *line_buf = NULL;
224
225
5.64M
  while ((nread = getline(&line_buf, &line_len, fp)) > 0) {
226
5.64M
    rc = process_line(rec, path, prefix, line_buf, nread, inputno,
227
5.64M
          ++lineno);
228
5.64M
    if (rc)
229
281
      goto out;
230
5.64M
  }
231
3.26k
  rc = 0;
232
3.54k
out:
233
3.54k
  free(line_buf);
234
3.54k
  return rc;
235
3.26k
}
236
237
static int merge_mmap_spec_nodes(struct spec_node *restrict dest,
238
         struct spec_node *restrict source)
239
2.46k
{
240
  /* Nodes should have the same stem */
241
2.46k
  assert((dest->stem == NULL && source->stem == NULL) ||
242
2.46k
         (dest->stem && source->stem && dest->stem_len &&
243
2.46k
    source->stem_len && strcmp(dest->stem, source->stem) == 0));
244
  /* Source should be loaded from mmap, so we can assume its data is sorted */
245
2.46k
  assert(source->from_mmap);
246
247
  /*
248
   * Merge literal specs
249
   */
250
2.46k
  if (source->literal_specs_num > 0) {
251
694
    if (dest->literal_specs_num > 0) {
252
627
      struct literal_spec *lspecs;
253
627
      uint32_t lspecs_num;
254
255
627
      if (__builtin_add_overflow(dest->literal_specs_num,
256
627
               source->literal_specs_num,
257
627
               &lspecs_num))
258
0
        return -1;
259
260
627
      lspecs = reallocarray(dest->literal_specs, lspecs_num,
261
627
                sizeof(struct literal_spec));
262
627
      if (!lspecs)
263
0
        return -1;
264
265
627
      memcpy(&lspecs[dest->literal_specs_num],
266
627
             source->literal_specs,
267
627
             source->literal_specs_num *
268
627
               sizeof(struct literal_spec));
269
270
627
      dest->literal_specs = lspecs;
271
627
      dest->literal_specs_num = lspecs_num;
272
627
      dest->literal_specs_alloc = lspecs_num;
273
274
      /* Cleanup moved source */
275
1.89k
      for (uint32_t i = 0; i < source->literal_specs_num;
276
1.26k
           i++) {
277
1.26k
        source->literal_specs[i].lr.ctx_raw = NULL;
278
1.26k
        source->literal_specs[i].lr.ctx_trans = NULL;
279
1.26k
      }
280
281
627
    } else {
282
67
      assert(dest->literal_specs == NULL);
283
67
      dest->literal_specs = source->literal_specs;
284
67
      dest->literal_specs_num = source->literal_specs_num;
285
67
      dest->literal_specs_alloc = source->literal_specs_alloc;
286
67
      source->literal_specs = NULL;
287
67
      source->literal_specs_num = 0;
288
67
      source->literal_specs_alloc = 0;
289
67
    }
290
694
  }
291
292
  /*
293
   * Merge regex specs
294
   */
295
2.46k
  if (source->regex_specs_num > 0) {
296
464
    if (dest->regex_specs_num > 0) {
297
413
      struct regex_spec *rspecs;
298
413
      uint32_t rspecs_num;
299
300
413
      if (__builtin_add_overflow(dest->regex_specs_num,
301
413
               source->regex_specs_num,
302
413
               &rspecs_num))
303
0
        return -1;
304
305
413
      rspecs = reallocarray(dest->regex_specs, rspecs_num,
306
413
                sizeof(struct regex_spec));
307
413
      if (!rspecs)
308
0
        return -1;
309
310
413
      memcpy(&rspecs[dest->regex_specs_num],
311
413
             source->regex_specs,
312
413
             source->regex_specs_num *
313
413
               sizeof(struct regex_spec));
314
315
413
      dest->regex_specs = rspecs;
316
413
      dest->regex_specs_num = rspecs_num;
317
413
      dest->regex_specs_alloc = rspecs_num;
318
319
      /* Cleanup moved source */
320
1.43k
      for (uint32_t i = 0; i < source->regex_specs_num; i++) {
321
1.01k
        source->regex_specs[i].lr.ctx_raw = NULL;
322
1.01k
        source->regex_specs[i].lr.ctx_trans = NULL;
323
1.01k
        source->regex_specs[i].regex = NULL;
324
1.01k
        source->regex_specs[i].regex_compiled = false;
325
1.01k
      }
326
413
    } else {
327
51
      assert(dest->regex_specs == NULL);
328
51
      dest->regex_specs = source->regex_specs;
329
51
      dest->regex_specs_num = source->regex_specs_num;
330
51
      dest->regex_specs_alloc = source->regex_specs_alloc;
331
51
      source->regex_specs = NULL;
332
51
      source->regex_specs_num = 0;
333
51
      source->regex_specs_alloc = 0;
334
51
    }
335
464
  }
336
337
  /*
338
   * Merge child nodes
339
   */
340
2.46k
  if (source->children_num > 0) {
341
1.59k
    if (dest->children_num > 0) {
342
1.55k
      struct spec_node *new_children;
343
1.55k
      uint32_t iter_dest, iter_source, new_children_alloc,
344
1.55k
        new_children_num, remaining_dest,
345
1.55k
        remaining_source;
346
347
1.55k
      if (__builtin_add_overflow(dest->children_num,
348
1.55k
               source->children_num,
349
1.55k
               &new_children_alloc))
350
0
        return -1;
351
352
      /* Over-allocate in favor of re-allocating multiple times */
353
1.55k
      new_children = calloc(new_children_alloc,
354
1.55k
                sizeof(struct spec_node));
355
1.55k
      if (!new_children)
356
0
        return -1;
357
358
      /* Since source is loaded from mmap its child nodes are sorted */
359
1.55k
      qsort(dest->children, dest->children_num,
360
1.55k
            sizeof(struct spec_node), compare_spec_node);
361
362
1.55k
      for (iter_dest = 0, iter_source = 0,
363
1.55k
          new_children_num = 0;
364
4.09k
           iter_dest < dest->children_num &&
365
2.67k
           iter_source < source->children_num;) {
366
2.53k
        struct spec_node *child_dest =
367
2.53k
          &dest->children[iter_dest];
368
2.53k
        struct spec_node *child_source =
369
2.53k
          &source->children[iter_source];
370
2.53k
        int r;
371
372
2.53k
        r = strcmp(child_dest->stem,
373
2.53k
             child_source->stem);
374
2.53k
        if (r == 0) {
375
1.95k
          int rc;
376
377
1.95k
          rc = merge_mmap_spec_nodes(
378
1.95k
            child_dest, child_source);
379
1.95k
          if (rc) {
380
0
            free(new_children);
381
0
            return rc;
382
0
          }
383
384
1.95k
          new_children[new_children_num++] =
385
1.95k
            *child_dest;
386
1.95k
          free_spec_node(child_source);
387
1.95k
          iter_dest++;
388
1.95k
          iter_source++;
389
1.95k
        } else if (r < 0) {
390
321
          new_children[new_children_num++] =
391
321
            *child_dest;
392
321
          iter_dest++;
393
321
        } else {
394
261
          new_children[new_children_num++] =
395
261
            *child_source;
396
261
          iter_source++;
397
261
        }
398
2.53k
      }
399
400
1.55k
      remaining_dest = dest->children_num - iter_dest;
401
1.55k
      remaining_source = source->children_num - iter_source;
402
1.55k
      assert(!remaining_dest || !remaining_source);
403
1.55k
      assert(new_children_num + remaining_dest +
404
1.55k
               remaining_source <=
405
1.55k
             new_children_alloc);
406
407
1.55k
      if (remaining_dest > 0) {
408
135
        memcpy(&new_children[new_children_num],
409
135
               &dest->children[iter_dest],
410
135
               remaining_dest *
411
135
                 sizeof(struct spec_node));
412
135
        new_children_num += remaining_dest;
413
135
      }
414
415
1.55k
      if (remaining_source > 0) {
416
136
        memcpy(&new_children[new_children_num],
417
136
               &source->children[iter_source],
418
136
               remaining_source *
419
136
                 sizeof(struct spec_node));
420
136
        new_children_num += remaining_source;
421
136
      }
422
423
1.55k
      free(dest->children);
424
1.55k
      dest->children = new_children;
425
1.55k
      dest->children_alloc = new_children_alloc;
426
1.55k
      dest->children_num = new_children_num;
427
428
1.55k
      free(source->children);
429
1.55k
      source->children = NULL;
430
1.55k
      source->children_alloc = 0;
431
1.55k
      source->children_num = 0;
432
433
1.55k
    } else {
434
34
      assert(dest->children == NULL);
435
34
      dest->children = source->children;
436
34
      dest->children_num = source->children_num;
437
34
      dest->children_alloc = source->children_alloc;
438
34
      source->children = NULL;
439
34
      source->children_num = 0;
440
34
      source->children_alloc = 0;
441
34
    }
442
1.59k
  }
443
444
2.46k
  return 0;
445
2.46k
}
446
447
static inline bool entry_size_check(const struct mmap_area *mmap_area,
448
            size_t nmenb, size_t size)
449
26.1k
{
450
26.1k
  size_t required;
451
452
26.1k
  if (__builtin_mul_overflow(nmenb, size, &required))
453
0
    return true;
454
455
26.1k
  return required > mmap_area->next_len;
456
26.1k
}
457
458
struct context_array {
459
  char **data;
460
  uint32_t size;
461
};
462
463
static void free_context_array(struct context_array *ctx_array)
464
5.06k
{
465
5.06k
  if (!ctx_array->data)
466
609
    return;
467
468
296k
  for (uint32_t i = 0; i < ctx_array->size; i++)
469
291k
    free(ctx_array->data[i]);
470
471
4.45k
  free(ctx_array->data);
472
4.45k
}
473
474
static int load_mmap_ctxarray(struct mmap_area *mmap_area, const char *path,
475
            struct context_array *ctx_array, bool validating)
476
4.53k
{
477
4.53k
  uint32_t data_u32, count;
478
4.53k
  uint16_t data_u16, ctx_len;
479
4.53k
  char *ctx;
480
4.53k
  int rc;
481
482
  /*
483
   * Read number of context definitions
484
   */
485
4.53k
  rc = next_entry(&data_u32, mmap_area, sizeof(uint32_t));
486
4.53k
  if (rc < 0)
487
6
    return -1;
488
4.52k
  count = be32toh(data_u32);
489
490
4.52k
  if (entry_size_check(mmap_area, count, 3 * sizeof(char)))
491
73
    return -1;
492
493
4.45k
  (*ctx_array).data = calloc(count, sizeof(char *));
494
4.45k
  if (!(*ctx_array).data) {
495
0
    (*ctx_array).size = 0;
496
0
    return -1;
497
0
  }
498
4.45k
  (*ctx_array).size = count;
499
500
8.42k
  for (uint32_t i = 0; i < count; i++) {
501
    /*
502
     * Read raw context
503
     * We need to allocate it on the heap since it might get free'd and replaced in a
504
     * SELINUX_CB_VALIDATE callback.
505
     */
506
4.05k
    rc = next_entry(&data_u16, mmap_area, sizeof(uint16_t));
507
4.05k
    if (rc < 0)
508
9
      return -1;
509
4.04k
    ctx_len = be16toh(data_u16);
510
511
4.04k
    if (ctx_len == 0 || ctx_len == UINT16_MAX)
512
18
      return -1;
513
514
4.02k
    if (entry_size_check(mmap_area, ctx_len, sizeof(char)))
515
41
      return -1;
516
517
3.98k
    ctx = malloc(ctx_len + 1);
518
3.98k
    if (!ctx)
519
0
      return -1;
520
521
3.98k
    rc = next_entry(ctx, mmap_area, ctx_len);
522
3.98k
    if (rc < 0) {
523
0
      free(ctx);
524
0
      return -1;
525
0
    }
526
3.98k
    ctx[ctx_len] = '\0';
527
528
3.98k
    if (validating && strcmp(ctx, "<<none>>") != 0) {
529
3.79k
      if (selinux_validate(&ctx) < 0) {
530
9
        selinux_log(SELINUX_ERROR,
531
9
              "%s: context %s is invalid\n", path,
532
9
              ctx);
533
9
        free(ctx);
534
9
        return -1;
535
9
      }
536
3.79k
    }
537
538
3.97k
    (*ctx_array).data[i] = ctx;
539
3.97k
  }
540
541
4.37k
  return 0;
542
4.45k
}
543
544
static int load_mmap_literal_spec(struct mmap_area *mmap_area, bool validating,
545
          uint8_t inputno, struct literal_spec *lspec,
546
          const struct context_array *ctx_array)
547
9.29k
{
548
9.29k
  uint32_t data_u32, ctx_id;
549
9.29k
  uint16_t data_u16, regex_len, lmatch_len;
550
9.29k
  uint8_t data_u8;
551
9.29k
  int rc;
552
553
9.29k
  lspec->from_mmap = true;
554
9.29k
  lspec->inputno = inputno;
555
556
  /*
557
   * Read raw context id
558
   * We need to allocate it on the heap since it might get free'd and replaced in a
559
   * SELINUX_CB_VALIDATE callback.
560
   */
561
9.29k
  rc = next_entry(&data_u32, mmap_area, sizeof(uint32_t));
562
9.29k
  if (rc < 0)
563
2
    return -1;
564
9.29k
  ctx_id = be32toh(data_u32);
565
566
9.29k
  if (ctx_id == 0 || ctx_id == UINT32_MAX || ctx_id > ctx_array->size)
567
143
    return -1;
568
569
9.14k
  lspec->lr.ctx_raw = strdup(ctx_array->data[ctx_id - 1]);
570
9.14k
  if (!lspec->lr.ctx_raw)
571
0
    return -1;
572
573
9.14k
  if (validating)
574
    /* validated in load_mmap_ctxarray() */
575
9.14k
    lspec->lr.validated = true;
576
577
  /*
578
   * Read original regex
579
   */
580
9.14k
  rc = next_entry(&data_u16, mmap_area, sizeof(uint16_t));
581
9.14k
  if (rc < 0)
582
1
    return -1;
583
9.14k
  regex_len = be16toh(data_u16);
584
585
9.14k
  if (regex_len <= 1)
586
7
    return -1;
587
588
9.14k
  lspec->regex_str = mmap_area->next_addr;
589
9.14k
  rc = next_entry(NULL, mmap_area, regex_len);
590
9.14k
  if (rc < 0)
591
19
    return -1;
592
593
9.12k
  if (lspec->regex_str[0] == '\0' ||
594
9.11k
      lspec->regex_str[regex_len - 1] != '\0')
595
26
    return -1;
596
597
  /*
598
   * Read literal match
599
   */
600
9.09k
  rc = next_entry(&data_u16, mmap_area, sizeof(uint16_t));
601
9.09k
  if (rc < 0)
602
2
    return -1;
603
9.09k
  lmatch_len = be16toh(data_u16);
604
605
9.09k
  if (lmatch_len <= 1)
606
22
    return -1;
607
608
9.07k
  lspec->literal_match = mmap_area->next_addr;
609
9.07k
  rc = next_entry(NULL, mmap_area, lmatch_len);
610
9.07k
  if (rc < 0)
611
34
    return -1;
612
613
9.03k
  if (lspec->literal_match[0] == '\0' ||
614
9.03k
      lspec->literal_match[lmatch_len - 1] != '\0')
615
38
    return -1;
616
617
8.99k
  lspec->prefix_len = lmatch_len - 1;
618
619
8.99k
  if (lspec->prefix_len > strlen(lspec->regex_str))
620
19
    return -1;
621
622
  /*
623
   * Read file kind
624
   */
625
8.98k
  rc = next_entry(&data_u8, mmap_area, sizeof(uint8_t));
626
8.98k
  if (rc < 0)
627
4
    return -1;
628
8.97k
  if (data_u8 > LABEL_FILE_KIND_REG)
629
8
    return -1;
630
8.96k
  lspec->file_kind = data_u8;
631
632
8.96k
  return 0;
633
8.97k
}
634
635
static int load_mmap_regex_spec(struct mmap_area *mmap_area, bool validating,
636
        bool do_load_precompregex, uint8_t inputno,
637
        struct regex_spec *rspec,
638
        const struct context_array *ctx_array)
639
5.50k
{
640
5.50k
  uint32_t data_u32, ctx_id, lineno;
641
5.50k
  uint16_t data_u16, regex_len;
642
5.50k
  uint8_t data_u8;
643
5.50k
  int rc;
644
645
5.50k
  rspec->from_mmap = true;
646
647
  /*
648
   * Read raw context id
649
   * We need to allocate it on the heap since it might get free'd and replaced in a
650
   * SELINUX_CB_VALIDATE callback.
651
   */
652
5.50k
  rc = next_entry(&data_u32, mmap_area, sizeof(uint32_t));
653
5.50k
  if (rc < 0)
654
22
    return -1;
655
5.48k
  ctx_id = be32toh(data_u32);
656
657
5.48k
  if (ctx_id == 0 || ctx_id == UINT32_MAX || ctx_id > ctx_array->size)
658
147
    return -1;
659
660
5.34k
  rspec->lr.ctx_raw = strdup(ctx_array->data[ctx_id - 1]);
661
5.34k
  if (!rspec->lr.ctx_raw)
662
0
    return -1;
663
664
5.34k
  if (validating)
665
    /* validated in load_mmap_ctxarray() */
666
5.34k
    rspec->lr.validated = true;
667
668
  /*
669
   * Read line number in source file.
670
   */
671
5.34k
  rc = next_entry(&data_u32, mmap_area, sizeof(uint32_t));
672
5.34k
  if (rc < 0)
673
6
    return -1;
674
5.33k
  lineno = be32toh(data_u32);
675
676
5.33k
  if (lineno == 0 || lineno == UINT32_MAX)
677
3
    return -1;
678
5.33k
  rspec->lineno = lineno;
679
5.33k
  rspec->inputno = inputno;
680
681
  /*
682
   * Read original regex
683
   */
684
5.33k
  rc = next_entry(&data_u16, mmap_area, sizeof(uint16_t));
685
5.33k
  if (rc < 0)
686
5
    return -1;
687
5.32k
  regex_len = be16toh(data_u16);
688
689
5.32k
  if (regex_len <= 1)
690
12
    return -1;
691
692
5.31k
  rspec->regex_str = mmap_area->next_addr;
693
5.31k
  rc = next_entry(NULL, mmap_area, regex_len);
694
5.31k
  if (rc < 0)
695
54
    return -1;
696
697
5.26k
  if (rspec->regex_str[0] == '\0' ||
698
5.25k
      rspec->regex_str[regex_len - 1] != '\0')
699
46
    return -1;
700
701
  /*
702
   * Read prefix length
703
   */
704
5.21k
  rc = next_entry(&data_u16, mmap_area, sizeof(uint16_t));
705
5.21k
  if (rc < 0)
706
4
    return -1;
707
5.21k
  rspec->prefix_len = be16toh(data_u16);
708
709
5.21k
  if (rspec->prefix_len > strlen(rspec->regex_str))
710
19
    return -1;
711
712
  /*
713
   * Read file kind
714
   */
715
5.19k
  rc = next_entry(&data_u8, mmap_area, sizeof(uint8_t));
716
5.19k
  if (rc < 0)
717
13
    return -1;
718
5.17k
  if (data_u8 > LABEL_FILE_KIND_REG)
719
16
    return -1;
720
5.16k
  rspec->file_kind = data_u8;
721
722
  /*
723
   * Read pcre regex related data
724
   */
725
5.16k
  rc = regex_load_mmap(mmap_area, &rspec->regex, do_load_precompregex,
726
5.16k
           &rspec->regex_compiled);
727
5.16k
  if (rc < 0)
728
57
    return -1;
729
730
5.10k
  __pthread_mutex_init(&rspec->regex_lock, NULL);
731
732
5.10k
  return 0;
733
5.16k
}
734
735
static int load_mmap_spec_node(struct mmap_area *mmap_area, const char *path,
736
             bool validating, bool do_load_precompregex,
737
             struct spec_node *node, const unsigned depth,
738
             uint8_t inputno,
739
             const struct context_array *ctx_array)
740
15.2k
{
741
15.2k
  uint32_t data_u32, lspec_num, rspec_num, children_num;
742
15.2k
  uint16_t data_u16, stem_len;
743
15.2k
  const bool is_root = (depth == 0);
744
15.2k
  int rc;
745
746
  /*
747
   * Guard against deep recursion by malicious pre-compiled fcontext
748
   * definitions. The limit of 32 is chosen intuitively and should
749
   * suffice for any real world scenario. See the macro
750
   * SPEC_NODE_MAX_DEPTH for the current value used for tree building.
751
   */
752
15.2k
  if (depth >= 32)
753
1
    return -1;
754
755
15.2k
  node->from_mmap = true;
756
757
  /*
758
   * Read stem
759
   */
760
15.2k
  rc = next_entry(&data_u16, mmap_area, sizeof(uint16_t));
761
15.2k
  if (rc < 0)
762
100
    return -1;
763
15.1k
  stem_len = be16toh(data_u16);
764
765
15.1k
  if (stem_len == 0)
766
22
    return -1;
767
768
15.1k
  if ((stem_len == 1) != is_root)
769
33
    return -1;
770
771
15.1k
  node->stem_len = stem_len - 1;
772
15.1k
  node->stem = mmap_area->next_addr;
773
15.1k
  rc = next_entry(NULL, mmap_area, stem_len);
774
15.1k
  if (rc < 0)
775
20
    return -1;
776
777
15.0k
  if (is_root)
778
4.23k
    node->stem = NULL;
779
10.8k
  else if (node->stem[0] == '\0' || node->stem[stem_len - 1] != '\0')
780
15
    return -1;
781
782
  /*
783
   * Read literal specs
784
   */
785
15.0k
  rc = next_entry(&data_u32, mmap_area, sizeof(uint32_t));
786
15.0k
  if (rc < 0)
787
6
    return -1;
788
15.0k
  lspec_num = be32toh(data_u32);
789
790
15.0k
  if (lspec_num == UINT32_MAX)
791
1
    return -1;
792
793
15.0k
  if (lspec_num > 0) {
794
5.77k
    if (entry_size_check(mmap_area, lspec_num,
795
5.77k
             3 * sizeof(uint16_t) + sizeof(uint32_t) +
796
5.77k
               6 * sizeof(char)))
797
106
      return -1;
798
799
5.67k
    node->literal_specs =
800
5.67k
      calloc(lspec_num, sizeof(struct literal_spec));
801
5.67k
    if (!node->literal_specs)
802
0
      return -1;
803
804
5.67k
    node->literal_specs_num = lspec_num;
805
5.67k
    node->literal_specs_alloc = lspec_num;
806
807
14.6k
    for (uint32_t i = 0; i < lspec_num; i++) {
808
9.29k
      rc = load_mmap_literal_spec(mmap_area, validating,
809
9.29k
                inputno,
810
9.29k
                &node->literal_specs[i],
811
9.29k
                ctx_array);
812
9.29k
      if (rc)
813
325
        return -1;
814
9.29k
    }
815
5.67k
  }
816
817
  /*
818
   * Read regex specs
819
   */
820
14.6k
  rc = next_entry(&data_u32, mmap_area, sizeof(uint32_t));
821
14.6k
  if (rc < 0)
822
13
    return -1;
823
14.6k
  rspec_num = be32toh(data_u32);
824
825
14.6k
  if (rspec_num == UINT32_MAX)
826
1
    return -1;
827
828
14.6k
  if (rspec_num > 0) {
829
3.39k
    if (entry_size_check(mmap_area, rspec_num,
830
3.39k
             sizeof(uint32_t) + 3 * sizeof(uint16_t) +
831
3.39k
               4 * sizeof(char)))
832
101
      return -1;
833
834
3.29k
    node->regex_specs =
835
3.29k
      calloc(rspec_num, sizeof(struct regex_spec));
836
3.29k
    if (!node->regex_specs)
837
0
      return -1;
838
839
3.29k
    node->regex_specs_num = rspec_num;
840
3.29k
    node->regex_specs_alloc = rspec_num;
841
842
8.39k
    for (uint32_t i = 0; i < rspec_num; i++) {
843
5.50k
      rc = load_mmap_regex_spec(mmap_area, validating,
844
5.50k
              do_load_precompregex, inputno,
845
5.50k
              &node->regex_specs[i],
846
5.50k
              ctx_array);
847
5.50k
      if (rc)
848
404
        return -1;
849
5.50k
    }
850
3.29k
  }
851
852
  /*
853
   * Read child nodes
854
   */
855
14.1k
  rc = next_entry(&data_u32, mmap_area, sizeof(uint32_t));
856
14.1k
  if (rc < 0)
857
36
    return -1;
858
14.0k
  children_num = be32toh(data_u32);
859
860
14.0k
  if (children_num == UINT32_MAX)
861
1
    return -1;
862
863
14.0k
  if (children_num > 0) {
864
7.44k
    const char *prev_stem = NULL;
865
866
7.44k
    if (entry_size_check(mmap_area, children_num,
867
7.44k
             3 * sizeof(uint32_t) + sizeof(uint16_t)))
868
134
      return -1;
869
870
7.31k
    node->children = calloc(children_num, sizeof(struct spec_node));
871
7.31k
    if (!node->children)
872
0
      return -1;
873
874
7.31k
    node->children_num = children_num;
875
7.31k
    node->children_alloc = children_num;
876
877
17.2k
    for (uint32_t i = 0; i < children_num; i++) {
878
10.8k
      rc = load_mmap_spec_node(mmap_area, path, validating,
879
10.8k
             do_load_precompregex,
880
10.8k
             &node->children[i], depth + 1,
881
10.8k
             inputno, ctx_array);
882
10.8k
      if (rc)
883
916
        return -1;
884
885
      /* Ensure child nodes are sorted and distinct */
886
9.96k
      if (prev_stem &&
887
3.41k
          strcmp(prev_stem, node->children[i].stem) >= 0)
888
42
        return -1;
889
890
9.92k
      prev_stem = node->children[i].stem;
891
9.92k
    }
892
7.31k
  }
893
894
12.9k
  if (!is_root && lspec_num == 0 && rspec_num == 0 && children_num == 0)
895
7
    return -1;
896
897
12.9k
  return 0;
898
12.9k
}
899
900
FUZZ_EXTERN int load_mmap(FILE *fp, const size_t len,
901
        struct selabel_handle *rec, const char *path,
902
        uint8_t inputno)
903
5.06k
{
904
5.06k
  struct saved_data *data = rec->data;
905
5.06k
  struct spec_node *root = NULL;
906
5.06k
  struct context_array ctx_array = {};
907
5.06k
  int rc;
908
5.06k
  char *addr = NULL, *str_buf = NULL;
909
5.06k
  struct mmap_area *mmap_area = NULL;
910
5.06k
  uint64_t data_u64, num_specs;
911
5.06k
  uint32_t data_u32, pcre_ver_len, pcre_arch_len;
912
5.06k
  const char *reg_arch, *reg_version;
913
5.06k
  bool reg_version_matches = false, reg_arch_matches = false;
914
915
5.06k
  mmap_area = malloc(sizeof(*mmap_area));
916
5.06k
  if (!mmap_area)
917
0
    goto err;
918
919
5.06k
  addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
920
5.06k
  if (addr == MAP_FAILED)
921
0
    goto err;
922
923
5.06k
  rc = madvise(addr, len, MADV_WILLNEED);
924
5.06k
  if (rc == -1)
925
0
    COMPAT_LOG(SELINUX_INFO,
926
5.06k
         "%s:  Failed to advise memory mapping:  %m\n", path);
927
928
  /* save where we mmap'd the file to cleanup on close() */
929
5.06k
  *mmap_area = (struct mmap_area){
930
5.06k
    .addr = addr,
931
5.06k
    .next_addr = addr,
932
5.06k
    .next = NULL,
933
5.06k
    .next_len = len,
934
5.06k
    .len = len,
935
5.06k
  };
936
937
  /* check if this looks like an fcontext file */
938
5.06k
  rc = next_entry(&data_u32, mmap_area, sizeof(uint32_t));
939
5.06k
  if (rc < 0 || be32toh(data_u32) != SELINUX_MAGIC_COMPILED_FCONTEXT)
940
100
    goto err;
941
942
  /* check if this version is higher than we understand */
943
4.96k
  rc = next_entry(&data_u32, mmap_area, sizeof(uint32_t));
944
4.96k
  if (rc < 0 ||
945
4.96k
      be32toh(data_u32) != SELINUX_COMPILED_FCONTEXT_TREE_LAYOUT) {
946
60
    COMPAT_LOG(
947
60
      SELINUX_WARNING,
948
60
      "%s:  Unsupported compiled fcontext version %d, supported is version %d\n",
949
60
      path, be32toh(data_u32),
950
60
      SELINUX_COMPILED_FCONTEXT_TREE_LAYOUT);
951
60
    goto err;
952
60
  }
953
954
4.90k
  reg_version = regex_version();
955
4.90k
  if (!reg_version)
956
0
    goto err;
957
958
4.90k
  reg_arch = regex_arch_string();
959
4.90k
  if (!reg_arch)
960
0
    goto err;
961
962
4.90k
  rc = next_entry(&data_u32, mmap_area, sizeof(uint32_t));
963
4.90k
  if (rc < 0)
964
6
    goto err;
965
4.89k
  pcre_ver_len = be32toh(data_u32);
966
967
  /* Check version lengths */
968
4.89k
  if (strlen(reg_version) != pcre_ver_len) {
969
    /*
970
     * Skip the entry and conclude that we have
971
     * a mismatch, which is not fatal.
972
     */
973
4.34k
    rc = next_entry(NULL, mmap_area, pcre_ver_len);
974
4.34k
    if (rc < 0)
975
63
      goto err;
976
4.28k
    goto end_version_check;
977
4.34k
  }
978
979
549
  if (entry_size_check(mmap_area, pcre_ver_len, sizeof(char)))
980
6
    goto err;
981
982
543
  str_buf = malloc(pcre_ver_len + 1);
983
543
  if (!str_buf)
984
0
    goto err;
985
986
543
  rc = next_entry(str_buf, mmap_area, pcre_ver_len);
987
543
  if (rc < 0)
988
0
    goto err;
989
990
543
  str_buf[pcre_ver_len] = '\0';
991
992
  /* Check for regex version mismatch */
993
543
  if (strcmp(str_buf, reg_version) != 0)
994
458
    COMPAT_LOG(
995
543
      SELINUX_WARNING,
996
543
      "%s:  Regex version mismatch, expected: %s actual: %s\n",
997
543
      path, reg_version, str_buf);
998
85
  else
999
85
    reg_version_matches = true;
1000
1001
543
  free(str_buf);
1002
543
  str_buf = NULL;
1003
1004
4.82k
end_version_check:
1005
1006
4.82k
  rc = next_entry(&data_u32, mmap_area, sizeof(uint32_t));
1007
4.82k
  if (rc < 0)
1008
151
    goto err;
1009
4.67k
  pcre_arch_len = be32toh(data_u32);
1010
1011
  /* Check arch string lengths */
1012
4.67k
  if (strlen(reg_arch) != pcre_arch_len) {
1013
    /*
1014
     * Skip the entry and conclude that we have
1015
     * a mismatch, which is not fatal.
1016
     */
1017
4.27k
    rc = next_entry(NULL, mmap_area, pcre_arch_len);
1018
4.27k
    if (rc < 0)
1019
69
      goto err;
1020
4.20k
    goto end_arch_check;
1021
4.27k
  }
1022
1023
399
  if (entry_size_check(mmap_area, pcre_arch_len, sizeof(char)))
1024
3
    goto err;
1025
1026
396
  str_buf = malloc(pcre_arch_len + 1);
1027
396
  if (!str_buf)
1028
0
    goto err;
1029
1030
396
  rc = next_entry(str_buf, mmap_area, pcre_arch_len);
1031
396
  if (rc < 0)
1032
0
    goto err;
1033
1034
396
  str_buf[pcre_arch_len] = '\0';
1035
1036
  /* Check if arch string mismatch */
1037
396
  if (strcmp(str_buf, reg_arch) != 0)
1038
274
    COMPAT_LOG(
1039
396
      SELINUX_WARNING,
1040
396
      "%s:  Regex architecture mismatch, expected: %s actual: %s\n",
1041
396
      path, reg_arch, str_buf);
1042
122
  else
1043
122
    reg_arch_matches = true;
1044
1045
396
  free(str_buf);
1046
396
  str_buf = NULL;
1047
1048
4.60k
end_arch_check:
1049
1050
  /* Read number of total specifications */
1051
4.60k
  rc = next_entry(&data_u64, mmap_area, sizeof(uint64_t));
1052
4.60k
  if (rc < 0)
1053
71
    goto err;
1054
4.53k
  num_specs = be64toh(data_u64);
1055
1056
4.53k
  if (__builtin_add_overflow(data->num_specs, num_specs, &data_u64))
1057
1
    goto err;
1058
1059
4.53k
  rc = load_mmap_ctxarray(mmap_area, path, &ctx_array, rec->validating);
1060
4.53k
  if (rc)
1061
156
    goto err;
1062
1063
4.37k
  root = calloc(1, sizeof(*root));
1064
4.37k
  if (!root)
1065
0
    goto err;
1066
1067
4.37k
  rc = load_mmap_spec_node(mmap_area, path, rec->validating,
1068
4.37k
         reg_version_matches && reg_arch_matches, root,
1069
4.37k
         0, inputno, &ctx_array);
1070
4.37k
  if (rc)
1071
1.36k
    goto err;
1072
1073
  /*
1074
   * On intermediate failure some data might already have been merged, so always keep the mmap'ed memory.
1075
   */
1076
3.00k
  mmap_area->next = data->mmap_areas;
1077
3.00k
  data->mmap_areas = mmap_area;
1078
3.00k
  mmap_area = NULL;
1079
3.00k
  addr = NULL;
1080
1081
3.00k
  if (data->num_specs == 0) {
1082
2.49k
    free_spec_node(data->root);
1083
2.49k
    free(data->root);
1084
2.49k
    data->root = root;
1085
2.49k
    root = NULL;
1086
2.49k
  } else {
1087
510
    rc = merge_mmap_spec_nodes(data->root, root);
1088
510
    if (rc)
1089
0
      goto err;
1090
1091
510
    free_spec_node(root);
1092
510
    free(root);
1093
510
    root = NULL;
1094
510
  }
1095
1096
  /* Success */
1097
3.00k
  data->num_specs += num_specs;
1098
1099
3.00k
  free_context_array(&ctx_array);
1100
1101
3.00k
  return 0;
1102
1103
2.05k
err:
1104
2.05k
  free_context_array(&ctx_array);
1105
2.05k
  if (root) {
1106
1.36k
    free_spec_node(root);
1107
1.36k
    free(root);
1108
1.36k
  }
1109
2.05k
  free(str_buf);
1110
2.05k
  free(mmap_area);
1111
2.05k
  if (addr && addr != MAP_FAILED)
1112
2.05k
    munmap(addr, len);
1113
2.05k
  if (errno == 0)
1114
2.05k
    errno = EINVAL;
1115
2.05k
  return -1;
1116
3.00k
}
1117
1118
struct file_details {
1119
  const char *suffix;
1120
  struct stat sb;
1121
};
1122
1123
static char *rolling_append(char *current, const char *suffix, size_t max)
1124
0
{
1125
0
  size_t size;
1126
0
  size_t suffix_size;
1127
0
  size_t current_size;
1128
1129
0
  if (!suffix)
1130
0
    return current;
1131
1132
0
  current_size = strlen(current);
1133
0
  suffix_size = strlen(suffix);
1134
1135
0
  size = current_size + suffix_size;
1136
0
  if (size < current_size || size < suffix_size)
1137
0
    return NULL;
1138
1139
  /* ensure space for the '.' and the '\0' characters. */
1140
0
  if (size >= (SIZE_MAX - 2))
1141
0
    return NULL;
1142
1143
0
  size += 2;
1144
1145
0
  if (size > max)
1146
0
    return NULL;
1147
1148
  /* Append any given suffix */
1149
0
  char *to = current + current_size;
1150
0
  *to++ = '.';
1151
0
  strcpy(to, suffix);
1152
1153
0
  return current;
1154
0
}
1155
1156
static int fcontext_is_binary(FILE *fp)
1157
0
{
1158
0
  uint32_t magic;
1159
0
  int rc;
1160
1161
0
  size_t len = fread(&magic, sizeof(magic), 1, fp);
1162
1163
0
  rc = fseek(fp, 0L, SEEK_SET);
1164
0
  if (rc == -1)
1165
0
    return -1;
1166
1167
0
  if (!len)
1168
0
    return 0;
1169
1170
0
  if (be32toh(magic) == SELINUX_MAGIC_COMPILED_FCONTEXT)
1171
0
    return 1;
1172
1173
  /*
1174
   * Treat old format magic in little endian as fcontext file as well,
1175
   * to avoid it getting parsed as text file.
1176
   */
1177
0
  if (le32toh(magic) == SELINUX_MAGIC_COMPILED_FCONTEXT)
1178
0
    return 2;
1179
1180
0
  return 0;
1181
0
}
1182
1183
0
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
1184
1185
static FILE *open_file(const char *path, const char *suffix, char *save_path,
1186
           size_t len, struct stat *sb, bool open_oldest)
1187
0
{
1188
0
  unsigned int i;
1189
0
  int rc;
1190
0
  char stack_path[len];
1191
0
  struct file_details *found = NULL;
1192
1193
  /*
1194
   * Rolling append of suffix. Try to open with path.suffix then the
1195
   * next as path.suffix.suffix and so forth.
1196
   */
1197
0
  struct file_details fdetails[2] = { { .suffix = suffix },
1198
0
              { .suffix = "bin" } };
1199
1200
0
  rc = snprintf(stack_path, sizeof(stack_path), "%s", path);
1201
0
  if (rc < 0 || (size_t)rc >= sizeof(stack_path)) {
1202
0
    errno = ENAMETOOLONG;
1203
0
    return NULL;
1204
0
  }
1205
1206
0
  for (i = 0; i < ARRAY_SIZE(fdetails); i++) {
1207
    /* This handles the case if suffix is null */
1208
0
    path = rolling_append(stack_path, fdetails[i].suffix,
1209
0
              sizeof(stack_path));
1210
0
    if (!path) {
1211
0
      errno = ENOMEM;
1212
0
      return NULL;
1213
0
    }
1214
1215
0
    rc = stat(path, &fdetails[i].sb);
1216
0
    if (rc)
1217
0
      continue;
1218
1219
    /* first file thing found, just take it */
1220
0
    if (!found) {
1221
0
      strcpy(save_path, path);
1222
0
      found = &fdetails[i];
1223
0
      continue;
1224
0
    }
1225
1226
    /*
1227
     * Keep picking the newest file found. Where "newest"
1228
     * includes equality. This provides a precedence on
1229
     * secondary suffixes even when the timestamp is the
1230
     * same. Ie choose file_contexts.bin over file_contexts
1231
     * even if the time stamp is the same. Invert this logic
1232
     * on open_oldest set to true. The idea is that if the
1233
     * newest file failed to process, we can attempt to
1234
     * process the oldest. The logic here is subtle and depends
1235
     * on the array ordering in fdetails for the case when time
1236
     * stamps are the same.
1237
     */
1238
0
    if (open_oldest ^
1239
0
        (fdetails[i].sb.st_mtime >= found->sb.st_mtime)) {
1240
0
      found = &fdetails[i];
1241
0
      strcpy(save_path, path);
1242
0
    }
1243
0
  }
1244
1245
0
  if (!found) {
1246
0
    errno = ENOENT;
1247
0
    return NULL;
1248
0
  }
1249
1250
0
  FILE *fp = fopen(save_path, "re");
1251
0
  if (!fp)
1252
0
    return NULL;
1253
1254
  /*
1255
   * Re-stat to ensure we use the same file size
1256
   * as the file we just opened.
1257
   */
1258
0
  if (fstat(fileno(fp), sb) < 0) {
1259
0
    fclose_errno_safe(fp);
1260
0
    return NULL;
1261
0
  }
1262
1263
0
  return fp;
1264
0
}
1265
1266
static int process_file(const char *path, const char *suffix,
1267
      struct selabel_handle *rec, const char *prefix,
1268
      struct selabel_digest *digest, uint8_t inputno)
1269
0
{
1270
0
  int rc;
1271
0
  unsigned int i;
1272
0
  struct stat sb;
1273
0
  FILE *fp = NULL;
1274
0
  char found_path[PATH_MAX];
1275
1276
  /*
1277
   * On the first pass open the newest modified file. If it fails to
1278
   * process, then the second pass shall open the oldest file. If both
1279
   * passes fail, then it's a fatal error.
1280
   */
1281
0
  for (i = 0; i < 2; i++) {
1282
0
    fp = open_file(path, suffix, found_path, sizeof(found_path),
1283
0
             &sb, i > 0);
1284
0
    if (fp == NULL)
1285
0
      return -1;
1286
1287
0
    rc = fcontext_is_binary(fp);
1288
0
    if (rc < 0) {
1289
0
      fclose_errno_safe(fp);
1290
0
      return -1;
1291
0
    }
1292
1293
0
    if (rc == 2) {
1294
0
      COMPAT_LOG(
1295
0
        SELINUX_INFO,
1296
0
        "%s:  Old compiled fcontext format, skipping\n",
1297
0
        found_path);
1298
0
      errno = EINVAL;
1299
0
    } else if (rc == 1) {
1300
0
      rc = load_mmap(fp, sb.st_size, rec, found_path,
1301
0
               inputno);
1302
0
    } else {
1303
0
      rc = process_text_file(fp, prefix, rec, found_path,
1304
0
                 inputno);
1305
0
    }
1306
1307
0
    if (!rc)
1308
0
      rc = digest_add_specfile(digest, fp, NULL, sb.st_size,
1309
0
             found_path);
1310
1311
0
    fclose_errno_safe(fp);
1312
1313
0
    if (!rc)
1314
0
      return 0;
1315
0
  }
1316
0
  return -1;
1317
0
}
1318
1319
static void selabel_subs_fini(struct selabel_sub *subs, uint32_t num)
1320
0
{
1321
0
  for (uint32_t i = 0; i < num; i++) {
1322
0
    free(subs[i].src);
1323
0
    free(subs[i].dst);
1324
0
  }
1325
1326
0
  free(subs);
1327
0
}
1328
1329
static char *selabel_apply_subs(const struct selabel_sub *subs, uint32_t num,
1330
        const char *src, size_t slen)
1331
7.62k
{
1332
7.62k
  char *dst, *tmp;
1333
7.62k
  uint32_t len;
1334
1335
7.62k
  for (uint32_t i = 0; i < num; i++) {
1336
0
    const struct selabel_sub *ptr = &subs[i];
1337
1338
0
    if (strncmp(src, ptr->src, ptr->slen) == 0) {
1339
0
      if (src[ptr->slen] == '/' || src[ptr->slen] == '\0') {
1340
0
        if ((src[ptr->slen] == '/') &&
1341
0
            (strcmp(ptr->dst, "/") == 0))
1342
0
          len = ptr->slen + 1;
1343
0
        else
1344
0
          len = ptr->slen;
1345
1346
0
        dst = malloc(ptr->dlen + slen - len + 1);
1347
0
        if (!dst)
1348
0
          return NULL;
1349
1350
0
        tmp = mempcpy(dst, ptr->dst, ptr->dlen);
1351
0
        tmp = mempcpy(tmp, &src[len], slen - len);
1352
0
        *tmp = '\0';
1353
0
        return dst;
1354
0
      }
1355
0
    }
1356
0
  }
1357
1358
7.62k
  return NULL;
1359
7.62k
}
1360
1361
#if !defined(BUILD_HOST) && !defined(ANDROID)
1362
static int selabel_subs_init(const char *path, struct selabel_digest *digest,
1363
           struct selabel_sub **out_subs, uint32_t *out_num,
1364
           uint32_t *out_alloc)
1365
0
{
1366
0
  char buf[1024];
1367
0
  FILE *cfg;
1368
0
  struct stat sb;
1369
0
  struct selabel_sub *tmp = NULL;
1370
0
  uint32_t tmp_num = 0, tmp_alloc = 0;
1371
0
  char *src_cpy = NULL, *dst_cpy = NULL;
1372
0
  int rc;
1373
1374
0
  *out_subs = NULL;
1375
0
  *out_num = 0;
1376
0
  *out_alloc = 0;
1377
1378
0
  cfg = fopen(path, "re");
1379
0
  if (!cfg) {
1380
    /* If the file does not exist, it is not fatal */
1381
0
    return (errno == ENOENT) ? 0 : -1;
1382
0
  }
1383
1384
0
  while (fgets_unlocked(buf, sizeof(buf), cfg)) {
1385
0
    char *ptr;
1386
0
    char *src = buf;
1387
0
    char *dst;
1388
0
    size_t slen, dlen;
1389
1390
0
    while (*src && isspace((unsigned char)*src))
1391
0
      src++;
1392
0
    if (src[0] == '#')
1393
0
      continue;
1394
0
    ptr = src;
1395
0
    while (*ptr && !isspace((unsigned char)*ptr))
1396
0
      ptr++;
1397
0
    if (*ptr)
1398
0
      *ptr++ = '\0';
1399
0
    if (!*src)
1400
0
      continue;
1401
1402
0
    dst = ptr;
1403
0
    while (*dst && isspace((unsigned char)*dst))
1404
0
      dst++;
1405
0
    ptr = dst;
1406
0
    while (*ptr && !isspace((unsigned char)*ptr))
1407
0
      ptr++;
1408
0
    *ptr = '\0';
1409
0
    if (!*dst)
1410
0
      continue;
1411
1412
0
    slen = strlen(src);
1413
0
    if (slen >= UINT32_MAX) {
1414
0
      errno = EINVAL;
1415
0
      goto err;
1416
0
    }
1417
1418
0
    dlen = strlen(dst);
1419
0
    if (dlen >= UINT32_MAX) {
1420
0
      errno = EINVAL;
1421
0
      goto err;
1422
0
    }
1423
1424
0
    src_cpy = strdup(src);
1425
0
    if (!src_cpy)
1426
0
      goto err;
1427
1428
0
    dst_cpy = strdup(dst);
1429
0
    if (!dst_cpy)
1430
0
      goto err;
1431
1432
0
    rc = GROW_ARRAY(tmp);
1433
0
    if (rc)
1434
0
      goto err;
1435
1436
0
    tmp[tmp_num++] = (struct selabel_sub){
1437
0
      .src = src_cpy,
1438
0
      .slen = slen,
1439
0
      .dst = dst_cpy,
1440
0
      .dlen = dlen,
1441
0
    };
1442
0
    src_cpy = NULL;
1443
0
    dst_cpy = NULL;
1444
0
  }
1445
1446
0
  rc = fstat(fileno(cfg), &sb);
1447
0
  if (rc < 0)
1448
0
    goto err;
1449
1450
0
  if (digest_add_specfile(digest, cfg, NULL, sb.st_size, path) < 0)
1451
0
    goto err;
1452
1453
  /* LIFO order for backward compatibility */
1454
0
  for (uint32_t i = 0; i < tmp_num / 2; i++) {
1455
0
    struct selabel_sub swap;
1456
1457
0
    swap = tmp[i];
1458
0
    tmp[i] = tmp[tmp_num - i - 1];
1459
0
    tmp[tmp_num - i - 1] = swap;
1460
0
  }
1461
1462
0
  *out_subs = tmp;
1463
0
  *out_num = tmp_num;
1464
0
  *out_alloc = tmp_alloc;
1465
1466
0
  fclose(cfg);
1467
1468
0
  return 0;
1469
1470
0
err:
1471
0
  free(dst_cpy);
1472
0
  free(src_cpy);
1473
0
  for (uint32_t i = 0; i < tmp_num; i++) {
1474
0
    free(tmp[i].src);
1475
0
    free(tmp[i].dst);
1476
0
  }
1477
0
  free(tmp);
1478
0
  fclose_errno_safe(cfg);
1479
0
  return -1;
1480
0
}
1481
#endif
1482
1483
static char *selabel_sub_key(const struct saved_data *data, const char *key,
1484
           size_t key_len)
1485
3.81k
{
1486
3.81k
  char *ptr, *dptr;
1487
1488
3.81k
  ptr = selabel_apply_subs(data->subs, data->subs_num, key, key_len);
1489
3.81k
  if (ptr) {
1490
0
    dptr = selabel_apply_subs(data->dist_subs, data->dist_subs_num,
1491
0
            ptr, strlen(ptr));
1492
0
    if (dptr) {
1493
0
      free(ptr);
1494
0
      ptr = dptr;
1495
0
    }
1496
3.81k
  } else {
1497
3.81k
    ptr = selabel_apply_subs(data->dist_subs, data->dist_subs_num,
1498
3.81k
           key, key_len);
1499
3.81k
  }
1500
1501
3.81k
  return ptr;
1502
3.81k
}
1503
1504
static void closef(struct selabel_handle *rec);
1505
1506
static const char *const opt_suffixes[] = { "homedirs", "local" };
1507
1508
static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
1509
    unsigned n)
1510
0
{
1511
0
  struct saved_data *data = rec->data;
1512
0
  uint8_t num_required_paths = 0, num_optional_paths = 0, i, j;
1513
0
  size_t total_paths;
1514
0
  const char *prefix = NULL;
1515
0
  int status = -1;
1516
0
  bool baseonly = false, path_provided = false;
1517
1518
0
  if (n > UINT8_MAX) {
1519
0
    errno = EINVAL;
1520
0
    return -1;
1521
0
  }
1522
1523
  /* Process arguments */
1524
0
  for (i = 0; i < n; i++) {
1525
0
    switch (opts[i].type) {
1526
0
    case SELABEL_OPT_PATH:
1527
0
      if (opts[i].value) {
1528
0
        num_required_paths++;
1529
0
        path_provided = true;
1530
0
      }
1531
0
      break;
1532
0
    case SELABEL_OPT_SUBSET:
1533
0
      prefix = opts[i].value;
1534
0
      break;
1535
0
    case SELABEL_OPT_BASEONLY:
1536
0
      baseonly = !!opts[i].value;
1537
0
      break;
1538
0
    case SELABEL_OPT_UNUSED:
1539
0
    case SELABEL_OPT_VALIDATE:
1540
0
    case SELABEL_OPT_DIGEST:
1541
0
      break;
1542
0
    default:
1543
0
      errno = EINVAL;
1544
0
      return -1;
1545
0
    }
1546
0
  }
1547
1548
  /* If no paths were provided, we will use the default path or fail,
1549
   * depending on the target. */
1550
0
  if (!path_provided) {
1551
0
#if !defined(BUILD_HOST) && !defined(ANDROID)
1552
0
    num_required_paths = 1;
1553
#else
1554
    selinux_log(SELINUX_ERROR,
1555
          "No path given to file labeling backend\n");
1556
    errno = EINVAL;
1557
    return -1;
1558
#endif
1559
0
  }
1560
1561
0
  if (!baseonly)
1562
0
    num_optional_paths = ARRAY_SIZE(opt_suffixes);
1563
1564
0
  total_paths = num_required_paths + num_optional_paths;
1565
1566
  /* Make sure total input files do not exceed the 256 indices supported
1567
   * by uint8_t inputno */
1568
0
  if (total_paths > UINT8_MAX + 1) {
1569
0
    errno = EINVAL;
1570
0
    return -1;
1571
0
  }
1572
1573
  /* Allocate the paths. */
1574
0
  rec->spec_files = calloc(total_paths, sizeof(*rec->spec_files));
1575
0
  if (rec->spec_files == NULL)
1576
0
    goto finish;
1577
0
  rec->spec_files_len = total_paths;
1578
1579
  /* Copy all the paths given. */
1580
0
  if (path_provided) {
1581
0
    for (i = 0, j = 0; i < n; i++) {
1582
0
      if (opts[i].type == SELABEL_OPT_PATH && opts[i].value) {
1583
0
        rec->spec_files[j] = strdup(opts[i].value);
1584
0
        if (rec->spec_files[j] == NULL)
1585
0
          goto finish;
1586
0
        j++;
1587
0
      }
1588
0
    }
1589
0
  }
1590
1591
0
#if !defined(BUILD_HOST) && !defined(ANDROID)
1592
0
  char subs_file[PATH_MAX + 1];
1593
  /* Process local and distribution substitution files */
1594
0
  if (!path_provided) {
1595
0
    rec->spec_files[0] = strdup(selinux_file_context_path());
1596
0
    if (rec->spec_files[0] == NULL)
1597
0
      goto finish;
1598
0
    status = selabel_subs_init(
1599
0
      selinux_file_context_subs_dist_path(), rec->digest,
1600
0
      &data->dist_subs, &data->dist_subs_num,
1601
0
      &data->dist_subs_alloc);
1602
0
    if (status)
1603
0
      goto finish;
1604
0
    status = selabel_subs_init(selinux_file_context_subs_path(),
1605
0
             rec->digest, &data->subs,
1606
0
             &data->subs_num, &data->subs_alloc);
1607
0
    if (status)
1608
0
      goto finish;
1609
0
  } else {
1610
0
    snprintf(subs_file, sizeof(subs_file), "%s.subs_dist",
1611
0
       rec->spec_files[0]);
1612
0
    status = selabel_subs_init(subs_file, rec->digest,
1613
0
             &data->dist_subs,
1614
0
             &data->dist_subs_num,
1615
0
             &data->dist_subs_alloc);
1616
0
    if (status)
1617
0
      goto finish;
1618
0
    snprintf(subs_file, sizeof(subs_file), "%s.subs",
1619
0
       rec->spec_files[0]);
1620
0
    status = selabel_subs_init(subs_file, rec->digest, &data->subs,
1621
0
             &data->subs_num, &data->subs_alloc);
1622
0
    if (status)
1623
0
      goto finish;
1624
0
  }
1625
0
#endif
1626
1627
0
  for (i = 0; i < num_optional_paths; i++) {
1628
0
    if (asprintf(&rec->spec_files[num_required_paths + i], "%s.%s",
1629
0
           rec->spec_files[0], opt_suffixes[i]) < 0) {
1630
0
      rec->spec_files[num_required_paths + i] = NULL;
1631
0
      goto finish;
1632
0
    }
1633
0
  }
1634
1635
  /*
1636
   * Process each main input file.
1637
   */
1638
0
  for (i = 0; i < num_required_paths; i++) {
1639
0
    status = process_file(rec->spec_files[i], NULL, rec, prefix,
1640
0
              rec->digest, i);
1641
0
    if (status)
1642
0
      goto finish;
1643
0
  }
1644
1645
  /*
1646
   * Process each optional input file.
1647
   */
1648
0
  for (i = 0; i < num_optional_paths; i++) {
1649
0
    status = process_file(rec->spec_files[num_required_paths + i],
1650
0
              NULL, rec, prefix, rec->digest,
1651
0
              num_required_paths + i);
1652
0
    if (status && errno != ENOENT)
1653
0
      goto finish;
1654
0
  }
1655
1656
0
  sort_specs(data);
1657
1658
0
  if (rec->validating) {
1659
0
    status = nodups_spec_node(rec, data->root);
1660
0
    if (status)
1661
0
      goto finish;
1662
0
  }
1663
1664
0
  digest_gen_hash(rec->digest);
1665
1666
0
  status = 0;
1667
1668
0
finish:
1669
0
  if (status)
1670
0
    closef(rec);
1671
1672
0
  return status;
1673
0
}
1674
1675
/*
1676
 * Backend interface routines
1677
 */
1678
static void closef(struct selabel_handle *rec)
1679
0
{
1680
0
  struct saved_data *data = (struct saved_data *)rec->data;
1681
0
  struct mmap_area *area, *last_area;
1682
1683
0
  if (!data)
1684
0
    return;
1685
1686
0
  selabel_subs_fini(data->subs, data->subs_num);
1687
0
  selabel_subs_fini(data->dist_subs, data->dist_subs_num);
1688
1689
0
  free_spec_node(data->root);
1690
0
  free(data->root);
1691
1692
0
  area = data->mmap_areas;
1693
0
  while (area) {
1694
0
    munmap(area->addr, area->len);
1695
0
    last_area = area;
1696
0
    area = area->next;
1697
0
    free(last_area);
1698
0
  }
1699
0
  free(data);
1700
0
  rec->data = NULL;
1701
0
}
1702
1703
static uint32_t search_literal_spec(const struct literal_spec *array,
1704
            uint32_t size, const char *key,
1705
            size_t key_len, bool partial)
1706
3.81k
{
1707
3.81k
  uint32_t lower, upper;
1708
1709
3.81k
  if (size == 0)
1710
1.87k
    return (uint32_t)-1;
1711
1712
1.94k
  lower = 0;
1713
1.94k
  upper = size - 1;
1714
1715
3.83k
  while (lower <= upper) {
1716
3.39k
    uint32_t m = lower + (upper - lower) / 2;
1717
3.39k
    int r;
1718
1719
3.39k
    if (partial)
1720
1.86k
      r = strncmp(array[m].literal_match, key, key_len);
1721
1.52k
    else
1722
1.52k
      r = strcmp(array[m].literal_match, key);
1723
1724
3.39k
    if (r == 0) {
1725
      /* Return the first result, regardless of file kind */
1726
59.6k
      while (m > 0) {
1727
58.8k
        if (partial)
1728
1.55k
          r = strncmp(array[m - 1].literal_match,
1729
1.55k
                key, key_len);
1730
57.3k
        else
1731
57.3k
          r = strcmp(array[m - 1].literal_match,
1732
57.3k
               key);
1733
1734
58.8k
        if (r == 0)
1735
58.3k
          m--;
1736
540
        else
1737
540
          break;
1738
58.8k
      }
1739
1.30k
      return m;
1740
1.30k
    }
1741
1742
2.09k
    if (r < 0)
1743
1.67k
      lower = m + 1;
1744
419
    else {
1745
419
      if (m == 0)
1746
197
        break;
1747
1748
222
      upper = m - 1;
1749
222
    }
1750
2.09k
  }
1751
1752
643
  return (uint32_t)-1;
1753
1.94k
}
1754
1755
FUZZ_EXTERN void free_lookup_result(struct lookup_result *result)
1756
8.10k
{
1757
8.10k
  struct lookup_result *tmp;
1758
1759
661k
  while (result) {
1760
652k
    tmp = result->next;
1761
652k
    free(result);
1762
652k
    result = tmp;
1763
652k
  }
1764
8.10k
}
1765
1766
/**
1767
 * all_node_specs() - Return all definitions in the given node and all transitive children.
1768
 * @node:      The top level node to return all definitions for.
1769
 * @file_kind: The kind of the file to look up (translated from file type into LABEL_FILE_KIND_*).
1770
 * @find_all:  Whether return all file context definitions or just any.
1771
 * @buf:       A pre-allocated buffer for a potential result to avoid allocating it on the heap or
1772
 *             NULL. Mutual exclusive with @find_all.
1773
 *
1774
 * Return: A linked list of all file context definitions if a match was found.
1775
 *         NULL is returned in case of no match found, or an allocation failure.
1776
 */
1777
static struct lookup_result *all_node_specs(struct spec_node *node,
1778
              uint8_t file_kind, bool find_all,
1779
              struct lookup_result *buf)
1780
6.87k
{
1781
6.87k
  struct lookup_result *result = NULL;
1782
6.87k
  struct lookup_result **next = &result;
1783
1784
6.87k
  assert(!(find_all && buf != NULL));
1785
1786
13.5k
  for (uint32_t i = 0; i < node->literal_specs_num; i++) {
1787
6.72k
    struct literal_spec *lspec = &node->literal_specs[i];
1788
6.72k
    struct lookup_result *r;
1789
1790
6.72k
    if (file_kind != LABEL_FILE_KIND_ALL &&
1791
4.53k
        lspec->file_kind != LABEL_FILE_KIND_ALL &&
1792
2.02k
        lspec->file_kind != file_kind)
1793
880
      continue;
1794
1795
5.84k
    if (strcmp(lspec->lr.ctx_raw, "<<none>>") == 0)
1796
480
      continue;
1797
1798
5.36k
    if (likely(!find_all && buf)) {
1799
0
      r = buf;
1800
5.36k
    } else {
1801
5.36k
      r = malloc(sizeof(*r));
1802
5.36k
      if (!r)
1803
0
        goto fail;
1804
5.36k
    }
1805
1806
5.36k
    *r = (struct lookup_result){
1807
5.36k
      .regex_str = lspec->regex_str,
1808
5.36k
      .prefix_len = lspec->prefix_len,
1809
5.36k
      .file_kind = lspec->file_kind,
1810
5.36k
      .lr = &lspec->lr,
1811
5.36k
      .has_meta_chars = false,
1812
5.36k
      .next = NULL,
1813
5.36k
    };
1814
1815
5.36k
    if (!find_all)
1816
40
      return r;
1817
1818
5.32k
    *next = r;
1819
5.32k
    next = &r->next;
1820
5.32k
  }
1821
1822
47.8k
  for (uint32_t i = 0; i < node->regex_specs_num; i++) {
1823
41.0k
    struct regex_spec *rspec = &node->regex_specs[i];
1824
41.0k
    struct lookup_result *r;
1825
1826
41.0k
    if (file_kind != LABEL_FILE_KIND_ALL &&
1827
39.4k
        rspec->file_kind != LABEL_FILE_KIND_ALL &&
1828
1.11k
        file_kind != rspec->file_kind)
1829
763
      continue;
1830
1831
40.3k
    if (strcmp(rspec->lr.ctx_raw, "<<none>>") == 0)
1832
405
      continue;
1833
1834
39.9k
    if (likely(!find_all && buf)) {
1835
0
      r = buf;
1836
39.9k
    } else {
1837
39.9k
      r = malloc(sizeof(*r));
1838
39.9k
      if (!r)
1839
0
        goto fail;
1840
39.9k
    }
1841
1842
39.9k
    *r = (struct lookup_result){
1843
39.9k
      .regex_str = rspec->regex_str,
1844
39.9k
      .prefix_len = rspec->prefix_len,
1845
39.9k
      .file_kind = rspec->file_kind,
1846
39.9k
      .lr = &rspec->lr,
1847
39.9k
      .has_meta_chars = true,
1848
39.9k
      .next = NULL,
1849
39.9k
    };
1850
1851
39.9k
    if (!find_all)
1852
38
      return r;
1853
1854
39.8k
    *next = r;
1855
39.8k
    next = &r->next;
1856
39.8k
  }
1857
1858
11.6k
  for (uint32_t i = 0; i < node->children_num; i++) {
1859
4.90k
    struct spec_node *child = &node->children[i];
1860
4.90k
    struct lookup_result *r, *last;
1861
1862
4.90k
    r = all_node_specs(child, file_kind, find_all, buf);
1863
4.90k
    if (!r)
1864
1.00k
      continue;
1865
1866
3.89k
    if (!find_all)
1867
88
      return r;
1868
1869
3.80k
    last = r;
1870
37.7k
    while (last->next)
1871
33.9k
      last = last->next;
1872
1873
3.80k
    *next = r;
1874
3.80k
    next = &last->next;
1875
3.80k
  }
1876
1877
6.70k
  return result;
1878
1879
0
fail:
1880
0
  free_lookup_result(result);
1881
1882
0
  return NULL;
1883
6.79k
}
1884
1885
/**
1886
 * lookup_check_node() - Try to find a file context definition in the given node or parents.
1887
 * @node:      The deepest specification node to match against. Parent nodes are successively
1888
 *             searched on no match or when finding all matches.
1889
 * @key:       The absolute file path to look up.
1890
 * @file_kind: The kind of the file to look up (translated from file type into LABEL_FILE_KIND_*).
1891
 * @partial:   Whether to partially match the given file path or completely.
1892
 * @find_all:  Whether to find all file context definitions or just the most specific.
1893
 * @buf:       A pre-allocated buffer for a potential result to avoid allocating it on the heap or
1894
 *             NULL. Mutual exclusive with @find_all.
1895
 *
1896
 * Return: A pointer to a file context definition if a match was found. If @find_all was specified
1897
 *         its a linked list of all results. If @buf was specified it is returned on a match found.
1898
 *         NULL is returned in case of no match found.
1899
 */
1900
static struct lookup_result *
1901
lookup_check_node(struct spec_node *node, const char *key, uint8_t file_kind,
1902
      bool partial, bool find_all, struct lookup_result *buf)
1903
3.81k
{
1904
3.81k
  struct lookup_result *result = NULL;
1905
3.81k
  struct lookup_result **next = &result;
1906
3.81k
  struct lookup_result *child_regex_match = NULL;
1907
3.81k
  uint8_t child_regex_match_inputno = 0; /* initialize to please GCC */
1908
3.81k
  uint32_t child_regex_match_lineno = 1; /* initialize to please GCC */
1909
3.81k
  size_t key_len = strlen(key);
1910
1911
3.81k
  assert(!(find_all && buf != NULL));
1912
1913
7.96k
  for (struct spec_node *n = node; n; n = n->parent) {
1914
4.34k
    if (n == node) {
1915
3.81k
      uint32_t literal_idx = search_literal_spec(
1916
3.81k
        n->literal_specs, n->literal_specs_num, key,
1917
3.81k
        key_len, partial);
1918
3.81k
      if (literal_idx != (uint32_t)-1) {
1919
117k
        do {
1920
117k
          struct literal_spec *lspec =
1921
117k
            &n->literal_specs[literal_idx];
1922
1923
117k
          if (file_kind == LABEL_FILE_KIND_ALL ||
1924
3.37k
              lspec->file_kind ==
1925
3.37k
                LABEL_FILE_KIND_ALL ||
1926
117k
              lspec->file_kind == file_kind) {
1927
117k
            struct lookup_result *r;
1928
1929
117k
#ifdef __ATOMIC_RELAXED
1930
117k
            __atomic_store_n(
1931
117k
              &lspec->any_matches,
1932
117k
              true, __ATOMIC_RELAXED);
1933
#else
1934
#error "Please use a compiler that supports __atomic builtins"
1935
#endif
1936
1937
117k
            if (strcmp(lspec->lr.ctx_raw,
1938
117k
                 "<<none>>") == 0) {
1939
236
              errno = ENOENT;
1940
236
              if (partial) {
1941
232
                literal_idx++;
1942
232
                continue;
1943
232
              }
1944
1945
4
              goto fail;
1946
236
            }
1947
1948
117k
            if (likely(buf)) {
1949
0
              r = buf;
1950
117k
            } else {
1951
117k
              r = malloc(sizeof(*r));
1952
117k
              if (!r)
1953
0
                goto fail;
1954
117k
            }
1955
1956
117k
            *r = (struct lookup_result){
1957
117k
              .regex_str =
1958
117k
                lspec->regex_str,
1959
117k
              .prefix_len =
1960
117k
                lspec->prefix_len,
1961
117k
              .file_kind =
1962
117k
                lspec->file_kind,
1963
117k
              .lr = &lspec->lr,
1964
117k
              .has_meta_chars = false,
1965
117k
              .next = NULL,
1966
117k
            };
1967
1968
117k
            if (likely(!find_all))
1969
115
              return r;
1970
1971
117k
            *next = r;
1972
117k
            next = &r->next;
1973
117k
          }
1974
1975
117k
          literal_idx++;
1976
117k
        } while (
1977
117k
          literal_idx < n->literal_specs_num &&
1978
117k
          (partial ?
1979
2.79k
             (strncmp(n->literal_specs[literal_idx]
1980
2.79k
                  .literal_match,
1981
2.79k
                key, key_len) == 0) :
1982
117k
             (strcmp(n->literal_specs[literal_idx]
1983
114k
                 .literal_match,
1984
114k
               key) == 0)));
1985
1.30k
      }
1986
3.81k
    }
1987
1988
501k
    for (uint32_t i = n->regex_specs_num; i > 0; i--) {
1989
      /* search in reverse order */
1990
497k
      struct regex_spec *rspec = &n->regex_specs[i - 1];
1991
497k
      char errbuf[256];
1992
497k
      int rc;
1993
1994
497k
      if (child_regex_match &&
1995
1.03k
          (rspec->inputno < child_regex_match_inputno ||
1996
1.00k
           (rspec->inputno == child_regex_match_inputno &&
1997
927
            rspec->lineno < child_regex_match_lineno)))
1998
93
        break;
1999
2000
497k
      if (file_kind != LABEL_FILE_KIND_ALL &&
2001
477k
          rspec->file_kind != LABEL_FILE_KIND_ALL &&
2002
815
          file_kind != rspec->file_kind)
2003
297
        continue;
2004
2005
497k
      if (compile_regex(rspec, errbuf, sizeof(errbuf)) < 0) {
2006
60
        COMPAT_LOG(
2007
60
          SELINUX_ERROR,
2008
60
          "Failed to compile regular expression '%s':  %s\n",
2009
60
          rspec->regex_str, errbuf);
2010
60
        goto fail;
2011
60
      }
2012
2013
497k
      rc = regex_match(rspec->regex, key, partial);
2014
497k
      if (rc == REGEX_MATCH ||
2015
490k
          (partial && rc == REGEX_MATCH_PARTIAL)) {
2016
490k
        struct lookup_result *r;
2017
2018
490k
        if (rc == REGEX_MATCH) {
2019
490k
#ifdef __ATOMIC_RELAXED
2020
490k
          __atomic_store_n(&rspec->any_matches,
2021
490k
               true,
2022
490k
               __ATOMIC_RELAXED);
2023
#else
2024
#error "Please use a compiler that supports __atomic builtins"
2025
#endif
2026
490k
        }
2027
2028
490k
        if (strcmp(rspec->lr.ctx_raw, "<<none>>") ==
2029
490k
            0) {
2030
268
          errno = ENOENT;
2031
268
          if (partial)
2032
266
            continue;
2033
2034
2
          goto fail;
2035
268
        }
2036
2037
490k
        if (child_regex_match) {
2038
113
          r = child_regex_match;
2039
490k
        } else if (buf) {
2040
0
          r = buf;
2041
490k
        } else {
2042
490k
          r = malloc(sizeof(*r));
2043
490k
          if (!r)
2044
0
            goto fail;
2045
490k
        }
2046
2047
490k
        *r = (struct lookup_result){
2048
490k
          .regex_str = rspec->regex_str,
2049
490k
          .prefix_len = rspec->prefix_len,
2050
490k
          .file_kind = rspec->file_kind,
2051
490k
          .lr = &rspec->lr,
2052
490k
          .has_meta_chars = true,
2053
490k
          .next = NULL,
2054
490k
        };
2055
2056
490k
        if (likely(!find_all)) {
2057
288
          child_regex_match = r;
2058
288
          child_regex_match_inputno =
2059
288
            rspec->inputno;
2060
288
          child_regex_match_lineno =
2061
288
            rspec->lineno;
2062
288
          goto parent_node;
2063
288
        }
2064
2065
490k
        *next = r;
2066
490k
        next = &r->next;
2067
2068
490k
        continue;
2069
490k
      }
2070
2071
6.83k
      if (rc == REGEX_NO_MATCH)
2072
6.82k
        continue;
2073
2074
      /* else it's an error */
2075
6.83k
      errno = ENOENT;
2076
13
      goto fail;
2077
6.83k
    }
2078
2079
4.15k
parent_node:
2080
4.15k
    continue;
2081
4.22k
  }
2082
2083
3.62k
  if (child_regex_match)
2084
163
    return child_regex_match;
2085
2086
3.45k
  if (!result)
2087
3.45k
    errno = ENOENT;
2088
3.45k
  return result;
2089
2090
79
fail:
2091
79
  if (!find_all && child_regex_match && child_regex_match != buf)
2092
12
    free(child_regex_match);
2093
2094
79
  free_lookup_result(result);
2095
2096
79
  return NULL;
2097
3.62k
}
2098
2099
static struct spec_node *search_child_node(struct spec_node *array,
2100
             uint32_t size, const char *key,
2101
             size_t key_len)
2102
919
{
2103
919
  uint32_t lower, upper;
2104
2105
919
  if (size == 0)
2106
71
    return NULL;
2107
2108
848
  lower = 0;
2109
848
  upper = size - 1;
2110
2111
1.33k
  while (lower <= upper) {
2112
1.14k
    uint32_t m = lower + (upper - lower) / 2;
2113
1.14k
    int r;
2114
2115
1.14k
    r = strncmp(array[m].stem, key, key_len);
2116
2117
1.14k
    if (r == 0 && array[m].stem[key_len] == '\0')
2118
534
      return &array[m];
2119
2120
611
    if (r < 0)
2121
405
      lower = m + 1;
2122
206
    else {
2123
206
      if (m == 0)
2124
125
        break;
2125
2126
81
      upper = m - 1;
2127
81
    }
2128
611
  }
2129
2130
314
  return NULL;
2131
848
}
2132
2133
static struct spec_node *lookup_find_deepest_node(struct spec_node *node,
2134
              const char *key,
2135
              const char **rest_key)
2136
3.81k
{
2137
  /* Find the node matching the deepest stem */
2138
2139
3.81k
  struct spec_node *n = node;
2140
3.81k
  const char *p = key;
2141
2142
3.81k
  *rest_key = NULL;
2143
2144
4.34k
  while (true) {
2145
4.34k
    struct spec_node *child;
2146
4.34k
    size_t length;
2147
4.34k
    const char *q;
2148
2149
4.34k
    if (unlikely(*p != '/'))
2150
1.98k
      break;
2151
2152
2.36k
    q = strchr(p + 1, '/');
2153
2.36k
    if (q == NULL) {
2154
1.44k
      *rest_key = (p + 1);
2155
1.44k
      break;
2156
1.44k
    }
2157
2158
919
    length = q - p - 1;
2159
919
    if (unlikely(length == 0)) {
2160
      /* double slash */
2161
0
      p = q;
2162
0
      continue;
2163
0
    }
2164
2165
919
    child = search_child_node(n->children, n->children_num, p + 1,
2166
919
            length);
2167
919
    if (!child) {
2168
385
      *rest_key = (p + 1);
2169
385
      break;
2170
385
    }
2171
2172
534
    n = child;
2173
534
    p = q;
2174
534
  }
2175
2176
3.81k
  return n;
2177
3.81k
}
2178
2179
static uint8_t mode_to_file_kind(int type)
2180
5.68k
{
2181
5.68k
  type &= S_IFMT;
2182
2183
5.68k
  switch (type) {
2184
0
  case S_IFBLK:
2185
0
    return LABEL_FILE_KIND_BLK;
2186
0
  case S_IFCHR:
2187
0
    return LABEL_FILE_KIND_CHR;
2188
0
  case S_IFDIR:
2189
0
    return LABEL_FILE_KIND_DIR;
2190
0
  case S_IFIFO:
2191
0
    return LABEL_FILE_KIND_FIFO;
2192
0
  case S_IFLNK:
2193
0
    return LABEL_FILE_KIND_LNK;
2194
3.53k
  case S_IFSOCK:
2195
3.53k
    return LABEL_FILE_KIND_SOCK;
2196
0
  case S_IFREG:
2197
0
    return LABEL_FILE_KIND_REG;
2198
2.15k
  case 0:
2199
2.15k
  default:
2200
2.15k
    return LABEL_FILE_KIND_ALL;
2201
5.68k
  }
2202
5.68k
}
2203
2204
// Finds all the matches of |key| in the given context. Returns the result in
2205
// the allocated array and updates the match count. If match_count is NULL,
2206
// stops early once the 1st match is found.
2207
FUZZ_EXTERN struct lookup_result *lookup_all(struct selabel_handle *rec,
2208
               const char *key, int type,
2209
               bool partial, bool find_all,
2210
               struct lookup_result *buf)
2211
5.68k
{
2212
5.68k
  struct saved_data *data = (struct saved_data *)rec->data;
2213
5.68k
  struct lookup_result *result = NULL;
2214
5.68k
  struct spec_node *node;
2215
5.68k
  size_t len;
2216
5.68k
  uint8_t file_kind = mode_to_file_kind(type);
2217
5.68k
  char *clean_key = NULL;
2218
5.68k
  const char *prev_slash, *next_slash, *rest_key;
2219
5.68k
  unsigned int sofar = 0;
2220
5.68k
  char *sub = NULL;
2221
2222
5.68k
  if (unlikely(!key)) {
2223
0
    errno = EINVAL;
2224
0
    goto finish;
2225
0
  }
2226
2227
5.68k
  if (unlikely(!data->num_specs)) {
2228
30
    errno = ENOENT;
2229
30
    goto finish;
2230
30
  }
2231
2232
  /* Remove duplicate slashes */
2233
5.65k
  if (unlikely(next_slash = strstr(key, "//"))) {
2234
54
    clean_key = (char *)malloc(strlen(key) + 1);
2235
54
    if (!clean_key)
2236
0
      goto finish;
2237
54
    prev_slash = key;
2238
702
    while (next_slash) {
2239
648
      memcpy(clean_key + sofar, prev_slash,
2240
648
             next_slash - prev_slash);
2241
648
      sofar += next_slash - prev_slash;
2242
648
      prev_slash = next_slash + 1;
2243
648
      next_slash = strstr(prev_slash, "//");
2244
648
    }
2245
54
    strcpy(clean_key + sofar, prev_slash);
2246
54
    key = clean_key;
2247
54
  }
2248
2249
  /* remove trailing slash */
2250
5.65k
  len = strlen(key);
2251
5.65k
  if (unlikely(len == 0)) {
2252
1.84k
    errno = EINVAL;
2253
1.84k
    goto finish;
2254
1.84k
  }
2255
2256
3.81k
  if (unlikely(len > 1 && key[len - 1] == '/')) {
2257
    /* reuse clean_key from above if available */
2258
62
    if (!clean_key) {
2259
56
      clean_key = (char *)malloc(len);
2260
56
      if (!clean_key)
2261
0
        goto finish;
2262
2263
56
      memcpy(clean_key, key, len - 1);
2264
56
    }
2265
2266
62
    clean_key[len - 1] = '\0';
2267
62
    key = clean_key;
2268
62
    len--;
2269
62
  }
2270
2271
3.81k
  sub = selabel_sub_key(data, key, len);
2272
3.81k
  if (sub)
2273
0
    key = sub;
2274
2275
3.81k
  node = lookup_find_deepest_node(data->root, key, &rest_key);
2276
2277
3.81k
  result =
2278
3.81k
    lookup_check_node(node, key, file_kind, partial, find_all, buf);
2279
2280
  /* For partial lookup if the key matches a child node, recursively add all specs */
2281
3.81k
  if (partial && rest_key) {
2282
1.54k
    if (!find_all && result != NULL) {
2283
      /* found already a result, and only one result is wanted */
2284
65
      goto finish;
2285
65
    }
2286
2287
9.55k
    for (uint32_t i = 0; i < node->children_num; i++) {
2288
8.15k
      struct spec_node *child = &node->children[i];
2289
8.15k
      struct lookup_result *r, *last;
2290
2291
8.15k
      if (strncmp(child->stem, rest_key, strlen(rest_key)) !=
2292
8.15k
          0)
2293
6.17k
        continue;
2294
2295
1.97k
      r = all_node_specs(child, file_kind, find_all, buf);
2296
1.97k
      if (!r)
2297
378
        continue;
2298
2299
1.59k
      last = r;
2300
45.2k
      while (last->next)
2301
43.7k
        last = last->next;
2302
2303
1.59k
      last->next = result;
2304
1.59k
      result = r;
2305
2306
1.59k
      if (!find_all)
2307
78
        break;
2308
1.59k
    }
2309
1.48k
  }
2310
2311
5.68k
finish:
2312
5.68k
  free(clean_key);
2313
5.68k
  free(sub);
2314
5.68k
  return result;
2315
3.81k
}
2316
2317
static struct lookup_result *lookup_common(struct selabel_handle *rec,
2318
             const char *key, int type,
2319
             bool partial,
2320
             struct lookup_result *buf)
2321
0
{
2322
0
  return lookup_all(rec, key, type, partial, false, buf);
2323
0
}
2324
2325
/*
2326
 * Returns true if the digest of all partial matched contexts is the same as
2327
 * the one saved by setxattr, otherwise returns false. The length of the SHA1
2328
 * digest will always be returned. The caller must free any returned digests.
2329
 */
2330
static bool get_digests_all_partial_matches(struct selabel_handle *rec,
2331
              const char *pathname,
2332
              uint8_t **calculated_digest,
2333
              uint8_t **xattr_digest,
2334
              size_t *digest_len)
2335
0
{
2336
0
  uint8_t read_digest[SHA1_HASH_SIZE];
2337
0
  ssize_t read_size = getxattr(pathname, RESTORECON_PARTIAL_MATCH_DIGEST,
2338
0
             read_digest, SHA1_HASH_SIZE
2339
#ifdef __APPLE__
2340
             ,
2341
             0, 0
2342
#endif /* __APPLE __ */
2343
0
  );
2344
0
  uint8_t hash_digest[SHA1_HASH_SIZE];
2345
0
  bool status =
2346
0
    selabel_hash_all_partial_matches(rec, pathname, hash_digest);
2347
2348
0
  *xattr_digest = NULL;
2349
0
  *calculated_digest = NULL;
2350
0
  *digest_len = SHA1_HASH_SIZE;
2351
2352
0
  if (read_size == SHA1_HASH_SIZE) {
2353
0
    *xattr_digest = calloc(1, SHA1_HASH_SIZE + 1);
2354
0
    if (!*xattr_digest)
2355
0
      goto oom;
2356
2357
0
    memcpy(*xattr_digest, read_digest, SHA1_HASH_SIZE);
2358
0
  }
2359
2360
0
  if (status) {
2361
0
    *calculated_digest = calloc(1, SHA1_HASH_SIZE + 1);
2362
0
    if (!*calculated_digest)
2363
0
      goto oom;
2364
2365
0
    memcpy(*calculated_digest, hash_digest, SHA1_HASH_SIZE);
2366
0
  }
2367
2368
0
  if (status && read_size == SHA1_HASH_SIZE &&
2369
0
      memcmp(read_digest, hash_digest, SHA1_HASH_SIZE) == 0)
2370
0
    return true;
2371
2372
0
  return false;
2373
2374
0
oom:
2375
0
  selinux_log(SELINUX_ERROR, "SELinux: %s: Out of memory\n", __func__);
2376
0
  return false;
2377
0
}
2378
2379
static bool hash_all_partial_matches(struct selabel_handle *rec,
2380
             const char *key, uint8_t *digest)
2381
0
{
2382
0
  assert(digest);
2383
2384
0
  struct lookup_result *matches =
2385
0
    lookup_all(rec, key, 0, true, true, NULL);
2386
0
  if (!matches) {
2387
0
    return false;
2388
0
  }
2389
2390
0
  Sha1Context context;
2391
0
  Sha1Initialise(&context);
2392
2393
0
  for (const struct lookup_result *m = matches; m; m = m->next) {
2394
0
    const char *regex_str = m->regex_str;
2395
0
    uint8_t file_kind = m->file_kind;
2396
0
    const char *ctx_raw = m->lr->ctx_raw;
2397
2398
0
    Sha1Update(&context, regex_str, strlen(regex_str) + 1);
2399
0
    Sha1Update(&context, &file_kind, sizeof(file_kind));
2400
0
    Sha1Update(&context, ctx_raw, strlen(ctx_raw) + 1);
2401
0
  }
2402
2403
0
  SHA1_HASH sha1_hash;
2404
0
  Sha1Finalise(&context, &sha1_hash);
2405
0
  memcpy(digest, sha1_hash.bytes, SHA1_HASH_SIZE);
2406
2407
0
  free_lookup_result(matches);
2408
0
  return true;
2409
0
}
2410
2411
static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
2412
           const char *key, int type)
2413
0
{
2414
0
  struct lookup_result buf, *result;
2415
2416
0
  result = lookup_common(rec, key, type, false, &buf);
2417
0
  if (!result)
2418
0
    return NULL;
2419
2420
0
  return result->lr;
2421
0
}
2422
2423
static bool partial_match(struct selabel_handle *rec, const char *key)
2424
0
{
2425
0
  struct lookup_result buf;
2426
2427
0
  return !!lookup_common(rec, key, 0, true, &buf);
2428
0
}
2429
2430
static struct selabel_lookup_rec *lookup_best_match(struct selabel_handle *rec,
2431
                const char *key,
2432
                const char **aliases,
2433
                int type)
2434
0
{
2435
0
  size_t n, i, best = (size_t)-1;
2436
0
  struct lookup_result **results;
2437
0
  uint16_t prefix_len = 0;
2438
0
  struct selabel_lookup_rec *lr = NULL;
2439
2440
0
  if (!aliases || !aliases[0])
2441
0
    return lookup(rec, key, type);
2442
2443
0
  for (n = 0; aliases[n]; n++)
2444
0
    ;
2445
2446
0
  results = calloc(n + 1, sizeof(*results));
2447
0
  if (!results)
2448
0
    return NULL;
2449
0
  results[0] = lookup_common(rec, key, type, false, NULL);
2450
0
  if (results[0]) {
2451
0
    if (!results[0]->has_meta_chars) {
2452
      /* exact match on key */
2453
0
      lr = results[0]->lr;
2454
0
      goto out;
2455
0
    }
2456
0
    best = 0;
2457
0
    prefix_len = results[0]->prefix_len;
2458
0
  }
2459
0
  for (i = 1; i <= n; i++) {
2460
0
    results[i] =
2461
0
      lookup_common(rec, aliases[i - 1], type, false, NULL);
2462
0
    if (results[i]) {
2463
0
      if (!results[i]->has_meta_chars) {
2464
        /* exact match on alias */
2465
0
        lr = results[i]->lr;
2466
0
        goto out;
2467
0
      }
2468
0
      if (results[i]->prefix_len > prefix_len) {
2469
0
        best = i;
2470
0
        prefix_len = results[i]->prefix_len;
2471
0
      }
2472
0
    }
2473
0
  }
2474
2475
0
  if (best != (size_t)-1) {
2476
    /* longest fixed prefix match on key or alias */
2477
0
    lr = results[best]->lr;
2478
0
  } else {
2479
0
    errno = ENOENT;
2480
0
  }
2481
2482
0
out:
2483
0
  for (i = 0; i <= n; i++)
2484
0
    free_lookup_result(results[i]);
2485
0
  free(results);
2486
0
  return lr;
2487
0
}
2488
2489
static void spec_node_stats(const struct spec_node *node)
2490
0
{
2491
0
  bool any_matches;
2492
2493
0
  for (uint32_t i = 0; i < node->literal_specs_num; i++) {
2494
0
    const struct literal_spec *lspec = &node->literal_specs[i];
2495
2496
0
#ifdef __ATOMIC_RELAXED
2497
0
    any_matches = __atomic_load_n(
2498
0
      &node->literal_specs[i].any_matches, __ATOMIC_RELAXED);
2499
#else
2500
#error "Please use a compiler that supports __atomic builtins"
2501
#endif
2502
2503
0
    if (!any_matches) {
2504
0
      COMPAT_LOG(SELINUX_WARNING,
2505
0
           "Warning!  No matches for (%s, %s, %s)\n",
2506
0
           lspec->regex_str,
2507
0
           file_kind_to_string(lspec->file_kind),
2508
0
           lspec->lr.ctx_raw);
2509
0
    }
2510
0
  }
2511
2512
0
  for (uint32_t i = 0; i < node->regex_specs_num; i++) {
2513
0
    const struct regex_spec *rspec = &node->regex_specs[i];
2514
2515
0
#ifdef __ATOMIC_RELAXED
2516
0
    any_matches =
2517
0
      __atomic_load_n(&rspec->any_matches, __ATOMIC_RELAXED);
2518
#else
2519
#error "Please use a compiler that supports __atomic builtins"
2520
#endif
2521
2522
0
    if (!any_matches) {
2523
0
      COMPAT_LOG(SELINUX_WARNING,
2524
0
           "Warning!  No matches for (%s, %s, %s)\n",
2525
0
           rspec->regex_str,
2526
0
           file_kind_to_string(rspec->file_kind),
2527
0
           rspec->lr.ctx_raw);
2528
0
    }
2529
0
  }
2530
2531
0
  for (uint32_t i = 0; i < node->children_num; i++)
2532
0
    spec_node_stats(&node->children[i]);
2533
0
}
2534
2535
static void stats(struct selabel_handle *rec)
2536
0
{
2537
0
  const struct saved_data *data = (const struct saved_data *)rec->data;
2538
2539
0
  spec_node_stats(data->root);
2540
0
}
2541
2542
static inline const char *fmt_stem(const char *stem)
2543
0
{
2544
0
  return stem ?: "(root)";
2545
0
}
2546
2547
static enum selabel_cmp_result lspec_incomp(const char *stem,
2548
              const struct literal_spec *lspec1,
2549
              const struct literal_spec *lspec2,
2550
              const char *reason, uint32_t iter1,
2551
              uint32_t iter2)
2552
0
{
2553
0
  selinux_log(
2554
0
    SELINUX_INFO,
2555
0
    "selabel_cmp: mismatched %s in stem %s on literal entry %u: (%s, %s, %s) vs entry %u: (%s, %s, %s)\n",
2556
0
    reason, fmt_stem(stem), iter1, lspec1->regex_str,
2557
0
    file_kind_to_string(lspec1->file_kind), lspec1->lr.ctx_raw,
2558
0
    iter2, lspec2->regex_str,
2559
0
    file_kind_to_string(lspec2->file_kind), lspec2->lr.ctx_raw);
2560
0
  return SELABEL_INCOMPARABLE;
2561
0
}
2562
2563
static enum selabel_cmp_result rspec_incomp(const char *stem,
2564
              const struct regex_spec *rspec1,
2565
              const struct regex_spec *rspec2,
2566
              const char *reason, uint32_t iter1,
2567
              uint32_t iter2)
2568
0
{
2569
0
  selinux_log(
2570
0
    SELINUX_INFO,
2571
0
    "selabel_cmp: mismatched %s in stem %s on regex entry %u: (%s, %s, %s) vs entry %u: (%s, %s, %s)\n",
2572
0
    reason, fmt_stem(stem), iter1, rspec1->regex_str,
2573
0
    file_kind_to_string(rspec1->file_kind), rspec1->lr.ctx_raw,
2574
0
    iter2, rspec2->regex_str,
2575
0
    file_kind_to_string(rspec2->file_kind), rspec2->lr.ctx_raw);
2576
0
  return SELABEL_INCOMPARABLE;
2577
0
}
2578
2579
static enum selabel_cmp_result spec_node_cmp(const struct spec_node *node1,
2580
               const struct spec_node *node2)
2581
30.6k
{
2582
30.6k
  enum selabel_cmp_result result = SELABEL_EQUAL;
2583
2584
30.6k
  if ((node1->stem && node2->stem &&
2585
24.9k
       strcmp(node1->stem, node2->stem) != 0) ||
2586
30.6k
      (node1->stem && node1->stem[0] != '\0' && !node2->stem) ||
2587
30.6k
      (!node1->stem && node2->stem && node2->stem[0] != '\0')) {
2588
0
    selinux_log(SELINUX_INFO,
2589
0
          "selabel_cmp: incompareable nodes: %s vs %s\n",
2590
0
          fmt_stem(node1->stem), fmt_stem(node2->stem));
2591
0
    return SELABEL_INCOMPARABLE;
2592
0
  }
2593
2594
  /* Literal specs comparison */
2595
30.6k
  {
2596
30.6k
    uint32_t iter1 = 0, iter2 = 0;
2597
2.56M
    while (iter1 < node1->literal_specs_num &&
2598
2.53M
           iter2 < node2->literal_specs_num) {
2599
2.53M
      const struct literal_spec *lspec1 =
2600
2.53M
        &node1->literal_specs[iter1];
2601
2.53M
      const struct literal_spec *lspec2 =
2602
2.53M
        &node2->literal_specs[iter2];
2603
2.53M
      int cmp;
2604
2605
2.53M
      cmp = strcmp(lspec1->literal_match,
2606
2.53M
             lspec2->literal_match);
2607
2.53M
      if (cmp < 0) {
2608
0
        if (result == SELABEL_EQUAL ||
2609
0
            result == SELABEL_SUPERSET) {
2610
0
          result = SELABEL_SUPERSET;
2611
0
          iter1++;
2612
0
          continue;
2613
0
        }
2614
2615
0
        return lspec_incomp(node1->stem, lspec1, lspec2,
2616
0
                "literal_str", iter1,
2617
0
                iter2);
2618
0
      }
2619
2620
2.53M
      if (cmp > 0) {
2621
0
        if (result == SELABEL_EQUAL ||
2622
0
            result == SELABEL_SUBSET) {
2623
0
          result = SELABEL_SUBSET;
2624
0
          iter2++;
2625
0
          continue;
2626
0
        }
2627
2628
0
        return lspec_incomp(node1->stem, lspec1, lspec2,
2629
0
                "literal_str", iter1,
2630
0
                iter2);
2631
0
      }
2632
2633
      /* If literal match is equal compare file kind */
2634
2635
2.53M
      if (lspec1->file_kind > lspec2->file_kind) {
2636
0
        if (result == SELABEL_EQUAL ||
2637
0
            result == SELABEL_SUPERSET) {
2638
0
          result = SELABEL_SUPERSET;
2639
0
          iter1++;
2640
0
          continue;
2641
0
        }
2642
2643
0
        return lspec_incomp(node1->stem, lspec1, lspec2,
2644
0
                "file_kind", iter1, iter2);
2645
0
      }
2646
2647
2.53M
      if (lspec1->file_kind < lspec2->file_kind) {
2648
0
        if (result == SELABEL_EQUAL ||
2649
0
            result == SELABEL_SUBSET) {
2650
0
          result = SELABEL_SUBSET;
2651
0
          iter2++;
2652
0
          continue;
2653
0
        }
2654
2655
0
        return lspec_incomp(node1->stem, lspec1, lspec2,
2656
0
                "file_kind", iter1, iter2);
2657
0
      }
2658
2659
2.53M
      iter1++;
2660
2.53M
      iter2++;
2661
2.53M
    }
2662
30.6k
    if (iter1 != node1->literal_specs_num) {
2663
0
      if (result == SELABEL_EQUAL ||
2664
0
          result == SELABEL_SUPERSET) {
2665
0
        result = SELABEL_SUPERSET;
2666
0
      } else {
2667
0
        selinux_log(
2668
0
          SELINUX_INFO,
2669
0
          "selabel_cmp: mismatch literal left remnant in stem %s\n",
2670
0
          fmt_stem(node1->stem));
2671
0
        return SELABEL_INCOMPARABLE;
2672
0
      }
2673
0
    }
2674
30.6k
    if (iter2 != node2->literal_specs_num) {
2675
0
      if (result == SELABEL_EQUAL ||
2676
0
          result == SELABEL_SUBSET) {
2677
0
        result = SELABEL_SUBSET;
2678
0
      } else {
2679
0
        selinux_log(
2680
0
          SELINUX_INFO,
2681
0
          "selabel_cmp: mismatch literal right remnant in stem %s\n",
2682
0
          fmt_stem(node1->stem));
2683
0
        return SELABEL_INCOMPARABLE;
2684
0
      }
2685
0
    }
2686
30.6k
  }
2687
2688
  /* Regex specs comparison */
2689
30.6k
  {
2690
30.6k
    uint32_t iter1 = 0, iter2 = 0;
2691
773k
    while (iter1 < node1->regex_specs_num &&
2692
742k
           iter2 < node2->regex_specs_num) {
2693
742k
      const struct regex_spec *rspec1 =
2694
742k
        &node1->regex_specs[iter1];
2695
742k
      const struct regex_spec *rspec2 =
2696
742k
        &node2->regex_specs[iter2];
2697
742k
      bool found_successor;
2698
2699
742k
      if (rspec1->file_kind == rspec2->file_kind &&
2700
742k
          strcmp(rspec1->regex_str, rspec2->regex_str) == 0) {
2701
742k
        iter1++;
2702
742k
        iter2++;
2703
742k
        continue;
2704
742k
      }
2705
2706
0
      if (result == SELABEL_SUPERSET) {
2707
0
        iter1++;
2708
0
        continue;
2709
0
      }
2710
2711
0
      if (result == SELABEL_SUBSET) {
2712
0
        iter2++;
2713
0
        continue;
2714
0
      }
2715
2716
0
      assert(result == SELABEL_EQUAL);
2717
2718
0
      found_successor = false;
2719
2720
0
      for (uint32_t i = iter2; i < node2->regex_specs_num;
2721
0
           i++) {
2722
0
        const struct regex_spec *successor =
2723
0
          &node2->regex_specs[i];
2724
2725
0
        if (rspec1->file_kind == successor->file_kind &&
2726
0
            strcmp(rspec1->regex_str,
2727
0
             successor->regex_str) == 0) {
2728
0
          result = SELABEL_SUBSET;
2729
0
          iter1++;
2730
0
          iter2 = i + 1;
2731
0
          found_successor = true;
2732
0
          break;
2733
0
        }
2734
0
      }
2735
2736
0
      if (found_successor)
2737
0
        continue;
2738
2739
0
      for (uint32_t i = iter1; i < node1->regex_specs_num;
2740
0
           i++) {
2741
0
        const struct regex_spec *successor =
2742
0
          &node1->regex_specs[i];
2743
2744
0
        if (successor->file_kind == rspec2->file_kind &&
2745
0
            strcmp(successor->regex_str,
2746
0
             rspec2->regex_str) == 0) {
2747
0
          result = SELABEL_SUPERSET;
2748
0
          iter1 = i + 1;
2749
0
          iter2++;
2750
0
          found_successor = true;
2751
0
          break;
2752
0
        }
2753
0
      }
2754
2755
0
      if (found_successor)
2756
0
        continue;
2757
2758
0
      return rspec_incomp(node1->stem, rspec1, rspec2,
2759
0
              "regex", iter1, iter2);
2760
0
    }
2761
30.6k
    if (iter1 != node1->regex_specs_num) {
2762
0
      if (result == SELABEL_EQUAL ||
2763
0
          result == SELABEL_SUPERSET) {
2764
0
        result = SELABEL_SUPERSET;
2765
0
      } else {
2766
0
        const struct regex_spec *rspec1 =
2767
0
          &node1->regex_specs[iter1];
2768
2769
0
        selinux_log(
2770
0
          SELINUX_INFO,
2771
0
          "selabel_cmp: mismatch regex left remnant in stem %s entry %u: (%s, %s, %s)\n",
2772
0
          fmt_stem(node1->stem), iter1,
2773
0
          rspec1->regex_str,
2774
0
          file_kind_to_string(rspec1->file_kind),
2775
0
          rspec1->lr.ctx_raw);
2776
0
        return SELABEL_INCOMPARABLE;
2777
0
      }
2778
0
    }
2779
30.6k
    if (iter2 != node2->regex_specs_num) {
2780
0
      if (result == SELABEL_EQUAL ||
2781
0
          result == SELABEL_SUBSET) {
2782
0
        result = SELABEL_SUBSET;
2783
0
      } else {
2784
0
        const struct regex_spec *rspec2 =
2785
0
          &node2->regex_specs[iter2];
2786
2787
0
        selinux_log(
2788
0
          SELINUX_INFO,
2789
0
          "selabel_cmp: mismatch regex right remnant in stem %s entry %u: (%s, %s, %s)\n",
2790
0
          fmt_stem(node1->stem), iter2,
2791
0
          rspec2->regex_str,
2792
0
          file_kind_to_string(rspec2->file_kind),
2793
0
          rspec2->lr.ctx_raw);
2794
0
        return SELABEL_INCOMPARABLE;
2795
0
      }
2796
0
    }
2797
30.6k
  }
2798
2799
  /* Child nodes comparison */
2800
30.6k
  {
2801
30.6k
    uint32_t iter1 = 0, iter2 = 0;
2802
55.5k
    while (iter1 < node1->children_num &&
2803
24.9k
           iter2 < node2->children_num) {
2804
24.9k
      const struct spec_node *child1 =
2805
24.9k
        &node1->children[iter1];
2806
24.9k
      const struct spec_node *child2 =
2807
24.9k
        &node2->children[iter2];
2808
24.9k
      enum selabel_cmp_result child_result;
2809
24.9k
      int cmp;
2810
2811
24.9k
      cmp = strcmp(child1->stem, child2->stem);
2812
24.9k
      if (cmp < 0) {
2813
0
        if (result == SELABEL_EQUAL ||
2814
0
            result == SELABEL_SUPERSET) {
2815
0
          result = SELABEL_SUPERSET;
2816
0
          iter1++;
2817
0
          continue;
2818
0
        }
2819
2820
0
        selinux_log(
2821
0
          SELINUX_INFO,
2822
0
          "selabel_cmp: mismatch left remnant child node %s stem %s\n",
2823
0
          child1->stem, fmt_stem(node1->stem));
2824
0
        return SELABEL_INCOMPARABLE;
2825
0
      }
2826
2827
24.9k
      if (cmp > 0) {
2828
0
        if (result == SELABEL_EQUAL ||
2829
0
            result == SELABEL_SUBSET) {
2830
0
          result = SELABEL_SUBSET;
2831
0
          iter2++;
2832
0
          continue;
2833
0
        }
2834
2835
0
        selinux_log(
2836
0
          SELINUX_INFO,
2837
0
          "selabel_cmp: mismatch right remnant child node %s stem %s\n",
2838
0
          child1->stem, fmt_stem(node1->stem));
2839
0
        return SELABEL_INCOMPARABLE;
2840
0
      }
2841
2842
24.9k
      iter1++;
2843
24.9k
      iter2++;
2844
2845
      /* If stem is equal do a deep comparison */
2846
2847
24.9k
      child_result = spec_node_cmp(child1, child2);
2848
24.9k
      switch (child_result) {
2849
0
      case SELABEL_SUBSET:
2850
0
        if (result == SELABEL_EQUAL ||
2851
0
            result == SELABEL_SUBSET) {
2852
0
          result = SELABEL_SUBSET;
2853
0
          continue;
2854
0
        }
2855
0
        selinux_log(
2856
0
          SELINUX_INFO,
2857
0
          "selabel_cmp: mismatch child node %s stem %s\n",
2858
0
          child1->stem, fmt_stem(node1->stem));
2859
0
        return SELABEL_INCOMPARABLE;
2860
24.9k
      case SELABEL_EQUAL:
2861
24.9k
        continue;
2862
0
      case SELABEL_SUPERSET:
2863
0
        if (result == SELABEL_EQUAL ||
2864
0
            result == SELABEL_SUPERSET) {
2865
0
          result = SELABEL_SUPERSET;
2866
0
          continue;
2867
0
        }
2868
0
        selinux_log(
2869
0
          SELINUX_INFO,
2870
0
          "selabel_cmp: mismatch child node %s stem %s\n",
2871
0
          child1->stem, fmt_stem(node1->stem));
2872
0
        return SELABEL_INCOMPARABLE;
2873
0
      case SELABEL_INCOMPARABLE:
2874
0
      default:
2875
0
        selinux_log(
2876
0
          SELINUX_INFO,
2877
0
          "selabel_cmp: mismatch child node %s stem %s\n",
2878
0
          child1->stem, fmt_stem(node1->stem));
2879
0
        return SELABEL_INCOMPARABLE;
2880
24.9k
      }
2881
24.9k
    }
2882
30.6k
    if (iter1 != node1->children_num) {
2883
0
      if (result == SELABEL_EQUAL ||
2884
0
          result == SELABEL_SUPERSET) {
2885
0
        result = SELABEL_SUPERSET;
2886
0
      } else {
2887
0
        selinux_log(
2888
0
          SELINUX_INFO,
2889
0
          "selabel_cmp: mismatch child left remnant in stem %s\n",
2890
0
          fmt_stem(node1->stem));
2891
0
        return SELABEL_INCOMPARABLE;
2892
0
      }
2893
0
    }
2894
30.6k
    if (iter2 != node2->children_num) {
2895
0
      if (result == SELABEL_EQUAL ||
2896
0
          result == SELABEL_SUBSET) {
2897
0
        result = SELABEL_SUBSET;
2898
0
      } else {
2899
0
        selinux_log(
2900
0
          SELINUX_INFO,
2901
0
          "selabel_cmp: mismatch child right remnant in stem %s\n",
2902
0
          fmt_stem(node1->stem));
2903
0
        return SELABEL_INCOMPARABLE;
2904
0
      }
2905
0
    }
2906
30.6k
  }
2907
2908
30.6k
  return result;
2909
30.6k
}
2910
2911
FUZZ_EXTERN enum selabel_cmp_result cmp(const struct selabel_handle *h1,
2912
          const struct selabel_handle *h2)
2913
5.68k
{
2914
5.68k
  const struct saved_data *data1, *data2;
2915
2916
  /* Ensured by selabel_cmp() */
2917
5.68k
  assert(h1->backend == SELABEL_CTX_FILE &&
2918
5.68k
         h2->backend == SELABEL_CTX_FILE);
2919
2920
5.68k
  data1 = h1->data;
2921
5.68k
  data2 = h2->data;
2922
2923
5.68k
  if (data1->num_specs == 0)
2924
30
    return data2->num_specs == 0 ? SELABEL_EQUAL : SELABEL_SUBSET;
2925
5.65k
  if (data2->num_specs == 0)
2926
0
    return SELABEL_SUPERSET;
2927
2928
5.65k
  return spec_node_cmp(data1->root, data2->root);
2929
5.65k
}
2930
2931
int selabel_file_init(struct selabel_handle *rec,
2932
          const struct selinux_opt *opts, unsigned nopts)
2933
0
{
2934
0
  struct saved_data *data;
2935
0
  struct spec_node *root;
2936
2937
0
  data = calloc(1, sizeof(*data));
2938
0
  if (!data)
2939
0
    return -1;
2940
2941
0
  root = calloc(1, sizeof(*root));
2942
0
  if (!root) {
2943
0
    free(data);
2944
0
    return -1;
2945
0
  }
2946
2947
0
  data->root = root;
2948
2949
0
  rec->data = data;
2950
0
  rec->func_close = &closef;
2951
0
  rec->func_stats = &stats;
2952
0
  rec->func_lookup = &lookup;
2953
0
  rec->func_partial_match = &partial_match;
2954
0
  rec->func_get_digests_all_partial_matches =
2955
0
    &get_digests_all_partial_matches;
2956
0
  rec->func_hash_all_partial_matches = &hash_all_partial_matches;
2957
0
  rec->func_lookup_best_match = &lookup_best_match;
2958
0
  rec->func_cmp = &cmp;
2959
2960
0
  return init(rec, opts, nopts);
2961
0
}