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