1 /* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. 2 * Use of this source code is governed by a BSD-style license that can be 3 * found in the LICENSE file. 4 */ 5 6 #include <errno.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 11 #include "syscall_filter.h" 12 13 #include "util.h" 14 15 /* clang-format off */ 16 #define ONE_INSTR 1 17 #define TWO_INSTRS 2 18 19 #define compiler_warn(_state, _msg, ...) \ 20 warn("%s: %s(%zd): " _msg, __func__, (_state)->filename, \ 21 (_state)->line_number, ## __VA_ARGS__) 22 23 #define compiler_pwarn(_state, _msg, ...) \ 24 compiler_warn(_state, _msg ": %m", ## __VA_ARGS__) 25 /* clang-format on */ 26 27 int seccomp_can_softfail(void) 28 { 29 #if defined(USE_SECCOMP_SOFTFAIL) 30 return 1; 31 #endif 32 return 0; 33 } 34 35 int str_to_op(const char *op_str) 36 { 37 if (!strcmp(op_str, "==")) { 38 return EQ; 39 } else if (!strcmp(op_str, "!=")) { 40 return NE; 41 } else if (!strcmp(op_str, "<")) { 42 return LT; 43 } else if (!strcmp(op_str, "<=")) { 44 return LE; 45 } else if (!strcmp(op_str, ">")) { 46 return GT; 47 } else if (!strcmp(op_str, ">=")) { 48 return GE; 49 } else if (!strcmp(op_str, "&")) { 50 return SET; 51 } else if (!strcmp(op_str, "in")) { 52 return IN; 53 } else { 54 return 0; 55 } 56 } 57 58 struct sock_filter *new_instr_buf(size_t count) 59 { 60 struct sock_filter *buf = calloc(count, sizeof(struct sock_filter)); 61 if (!buf) 62 die("could not allocate BPF instruction buffer"); 63 64 return buf; 65 } 66 67 struct filter_block *new_filter_block(void) 68 { 69 struct filter_block *block = calloc(1, sizeof(struct filter_block)); 70 if (!block) 71 die("could not allocate BPF filter block"); 72 73 block->instrs = NULL; 74 block->last = block->next = NULL; 75 76 return block; 77 } 78 79 void append_filter_block(struct filter_block *head, struct sock_filter *instrs, 80 size_t len) 81 { 82 struct filter_block *new_last; 83 84 /* 85 * If |head| has no filter assigned yet, 86 * we don't create a new node. 87 */ 88 if (head->instrs == NULL) { 89 new_last = head; 90 } else { 91 new_last = new_filter_block(); 92 if (head->next != NULL) { 93 head->last->next = new_last; 94 head->last = new_last; 95 } else { 96 head->last = head->next = new_last; 97 } 98 head->total_len += len; 99 } 100 101 new_last->instrs = instrs; 102 new_last->total_len = new_last->len = len; 103 new_last->last = new_last->next = NULL; 104 } 105 106 void extend_filter_block_list(struct filter_block *list, 107 struct filter_block *another) 108 { 109 if (list->last != NULL) { 110 list->last->next = another; 111 list->last = another->last; 112 } else { 113 list->next = another; 114 list->last = another->last; 115 } 116 list->total_len += another->total_len; 117 } 118 119 void append_ret_kill(struct filter_block *head) 120 { 121 struct sock_filter *filter = new_instr_buf(ONE_INSTR); 122 set_bpf_ret_kill(filter); 123 append_filter_block(head, filter, ONE_INSTR); 124 } 125 126 void append_ret_trap(struct filter_block *head) 127 { 128 struct sock_filter *filter = new_instr_buf(ONE_INSTR); 129 set_bpf_ret_trap(filter); 130 append_filter_block(head, filter, ONE_INSTR); 131 } 132 133 void append_ret_errno(struct filter_block *head, int errno_val) 134 { 135 struct sock_filter *filter = new_instr_buf(ONE_INSTR); 136 set_bpf_ret_errno(filter, errno_val); 137 append_filter_block(head, filter, ONE_INSTR); 138 } 139 140 void append_allow_syscall(struct filter_block *head, int nr) 141 { 142 struct sock_filter *filter = new_instr_buf(ALLOW_SYSCALL_LEN); 143 size_t len = bpf_allow_syscall(filter, nr); 144 if (len != ALLOW_SYSCALL_LEN) 145 die("error building syscall number comparison"); 146 147 append_filter_block(head, filter, len); 148 } 149 150 void allow_logging_syscalls(struct filter_block *head) 151 { 152 unsigned int i; 153 for (i = 0; i < log_syscalls_len; i++) { 154 warn("allowing syscall: %s", log_syscalls[i]); 155 append_allow_syscall(head, lookup_syscall(log_syscalls[i])); 156 } 157 } 158 159 unsigned int get_label_id(struct bpf_labels *labels, const char *label_str) 160 { 161 int label_id = bpf_label_id(labels, label_str); 162 if (label_id < 0) 163 die("could not allocate BPF label string"); 164 return label_id; 165 } 166 167 unsigned int group_end_lbl(struct bpf_labels *labels, int nr, int idx) 168 { 169 char lbl_str[MAX_BPF_LABEL_LEN]; 170 snprintf(lbl_str, MAX_BPF_LABEL_LEN, "%d_%d_end", nr, idx); 171 return get_label_id(labels, lbl_str); 172 } 173 174 unsigned int success_lbl(struct bpf_labels *labels, int nr) 175 { 176 char lbl_str[MAX_BPF_LABEL_LEN]; 177 snprintf(lbl_str, MAX_BPF_LABEL_LEN, "%d_success", nr); 178 return get_label_id(labels, lbl_str); 179 } 180 181 int is_implicit_relative_path(const char *filename) 182 { 183 return filename[0] != '/' && (filename[0] != '.' || filename[1] != '/'); 184 } 185 186 int compile_atom(struct parser_state *state, struct filter_block *head, 187 char *atom, struct bpf_labels *labels, int nr, int grp_idx) 188 { 189 /* Splits the atom. */ 190 char *atom_ptr = NULL; 191 char *argidx_str = strtok_r(atom, " ", &atom_ptr); 192 if (argidx_str == NULL) { 193 compiler_warn(state, "empty atom"); 194 return -1; 195 } 196 197 char *operator_str = strtok_r(NULL, " ", &atom_ptr); 198 if (operator_str == NULL) { 199 compiler_warn(state, "invalid atom '%s'", argidx_str); 200 return -1; 201 } 202 203 char *constant_str = strtok_r(NULL, " ", &atom_ptr); 204 if (constant_str == NULL) { 205 compiler_warn(state, "invalid atom '%s %s'", argidx_str, 206 operator_str); 207 return -1; 208 } 209 210 /* Checks that there are no extra tokens. */ 211 const char *extra = strtok_r(NULL, " ", &atom_ptr); 212 if (extra != NULL) { 213 compiler_warn(state, "extra token '%s'", extra); 214 return -1; 215 } 216 217 if (strncmp(argidx_str, "arg", 3)) { 218 compiler_warn(state, "invalid argument token '%s'", argidx_str); 219 return -1; 220 } 221 222 char *argidx_ptr; 223 long int argidx = strtol(argidx_str + 3, &argidx_ptr, 10); 224 /* 225 * Checks that an actual argument index was parsed, 226 * and that there was nothing left after the index. 227 */ 228 if (argidx_ptr == argidx_str + 3 || *argidx_ptr != '\0') { 229 compiler_warn(state, "invalid argument index '%s'", 230 argidx_str + 3); 231 return -1; 232 } 233 234 int op = str_to_op(operator_str); 235 if (op < MIN_OPERATOR) { 236 compiler_warn(state, "invalid operator '%s'", operator_str); 237 return -1; 238 } 239 240 char *constant_str_ptr; 241 long int c = parse_constant(constant_str, &constant_str_ptr); 242 if (constant_str_ptr == constant_str) { 243 compiler_warn(state, "invalid constant '%s'", constant_str); 244 return -1; 245 } 246 247 /* 248 * Looks up the label for the end of the AND statement 249 * this atom belongs to. 250 */ 251 unsigned int id = group_end_lbl(labels, nr, grp_idx); 252 253 /* 254 * Builds a BPF comparison between a syscall argument 255 * and a constant. 256 * The comparison lives inside an AND statement. 257 * If the comparison succeeds, we continue 258 * to the next comparison. 259 * If this comparison fails, the whole AND statement 260 * will fail, so we jump to the end of this AND statement. 261 */ 262 struct sock_filter *comp_block; 263 size_t len = bpf_arg_comp(&comp_block, op, argidx, c, id); 264 if (len == 0) 265 return -1; 266 267 append_filter_block(head, comp_block, len); 268 return 0; 269 } 270 271 int compile_errno(struct parser_state *state, struct filter_block *head, 272 char *ret_errno, int use_ret_trap) 273 { 274 char *errno_ptr = NULL; 275 276 /* Splits the 'return' keyword and the actual errno value. */ 277 char *ret_str = strtok_r(ret_errno, " ", &errno_ptr); 278 if (!ret_str || strncmp(ret_str, "return", strlen("return"))) 279 return -1; 280 281 char *errno_val_str = strtok_r(NULL, " ", &errno_ptr); 282 283 if (errno_val_str) { 284 char *errno_val_ptr; 285 int errno_val = parse_constant(errno_val_str, &errno_val_ptr); 286 /* Checks to see if we parsed an actual errno. */ 287 if (errno_val_ptr == errno_val_str || errno_val == -1) { 288 compiler_warn(state, "invalid errno value '%s'", 289 errno_val_ptr); 290 return -1; 291 } 292 293 append_ret_errno(head, errno_val); 294 } else { 295 if (!use_ret_trap) 296 append_ret_kill(head); 297 else 298 append_ret_trap(head); 299 } 300 return 0; 301 } 302 303 struct filter_block *compile_policy_line(struct parser_state *state, int nr, 304 const char *policy_line, 305 unsigned int entry_lbl_id, 306 struct bpf_labels *labels, 307 int use_ret_trap) 308 { 309 /* 310 * |policy_line| should be an expression of the form: 311 * "arg0 == 3 && arg1 == 5 || arg0 == 0x8" 312 * 313 * This is, an expression in DNF (disjunctive normal form); 314 * a disjunction ('||') of one or more conjunctions ('&&') 315 * of one or more atoms. 316 * 317 * Atoms are of the form "arg{DNUM} {OP} {NUM}" 318 * where: 319 * - DNUM is a decimal number. 320 * - OP is an operator: ==, !=, & (flags set), or 'in' (inclusion). 321 * - NUM is an octal, decimal, or hexadecimal number. 322 * 323 * When the syscall arguments make the expression true, 324 * the syscall is allowed. If not, the process is killed. 325 * 326 * To block a syscall without killing the process, 327 * |policy_line| can be of the form: 328 * "return <errno>" 329 * 330 * This "return {NUM}" policy line will block the syscall, 331 * make it return -1 and set |errno| to NUM. 332 * 333 * A regular policy line can also include a "return <errno>" clause, 334 * separated by a semicolon (';'): 335 * "arg0 == 3 && arg1 == 5 || arg0 == 0x8; return {NUM}" 336 * 337 * If the syscall arguments don't make the expression true, 338 * the syscall will be blocked as above instead of killing the process. 339 */ 340 341 size_t len = 0; 342 int grp_idx = 0; 343 344 /* Checks for empty policy lines. */ 345 if (strlen(policy_line) == 0) { 346 compiler_warn(state, "empty policy line"); 347 return NULL; 348 } 349 350 /* We will modify |policy_line|, so let's make a copy. */ 351 char *line = strdup(policy_line); 352 if (!line) 353 return NULL; 354 355 /* 356 * We build the filter section as a collection of smaller 357 * "filter blocks" linked together in a singly-linked list. 358 */ 359 struct filter_block *head = new_filter_block(); 360 361 /* 362 * Filter sections begin with a label where the main filter 363 * will jump after checking the syscall number. 364 */ 365 struct sock_filter *entry_label = new_instr_buf(ONE_INSTR); 366 set_bpf_lbl(entry_label, entry_lbl_id); 367 append_filter_block(head, entry_label, ONE_INSTR); 368 369 /* Checks whether we're unconditionally blocking this syscall. */ 370 if (strncmp(line, "return", strlen("return")) == 0) { 371 if (compile_errno(state, head, line, use_ret_trap) < 0) { 372 free_block_list(head); 373 free(line); 374 return NULL; 375 } 376 free(line); 377 return head; 378 } 379 380 /* Splits the optional "return <errno>" part. */ 381 char *line_ptr; 382 char *arg_filter = strtok_r(line, ";", &line_ptr); 383 char *ret_errno = strtok_r(NULL, ";", &line_ptr); 384 385 /* 386 * Splits the policy line by '||' into conjunctions and each conjunction 387 * by '&&' into atoms. 388 */ 389 char *arg_filter_str = arg_filter; 390 char *group; 391 while ((group = tokenize(&arg_filter_str, "||")) != NULL) { 392 char *group_str = group; 393 char *comp; 394 while ((comp = tokenize(&group_str, "&&")) != NULL) { 395 /* Compiles each atom into a BPF block. */ 396 if (compile_atom(state, head, comp, labels, nr, 397 grp_idx) < 0) { 398 free_block_list(head); 399 free(line); 400 return NULL; 401 } 402 } 403 /* 404 * If the AND statement succeeds, we're done, 405 * so jump to SUCCESS line. 406 */ 407 unsigned int id = success_lbl(labels, nr); 408 struct sock_filter *group_end_block = new_instr_buf(TWO_INSTRS); 409 len = set_bpf_jump_lbl(group_end_block, id); 410 /* 411 * The end of each AND statement falls after the 412 * jump to SUCCESS. 413 */ 414 id = group_end_lbl(labels, nr, grp_idx++); 415 len += set_bpf_lbl(group_end_block + len, id); 416 append_filter_block(head, group_end_block, len); 417 } 418 419 /* 420 * If no AND statements succeed, we end up here, 421 * because we never jumped to SUCCESS. 422 * If we have to return an errno, do it, 423 * otherwise just kill the task. 424 */ 425 if (ret_errno) { 426 if (compile_errno(state, head, ret_errno, use_ret_trap) < 0) { 427 free_block_list(head); 428 free(line); 429 return NULL; 430 } 431 } else { 432 if (!use_ret_trap) 433 append_ret_kill(head); 434 else 435 append_ret_trap(head); 436 } 437 438 /* 439 * Every time the filter succeeds we jump to a predefined SUCCESS 440 * label. Add that label and BPF RET_ALLOW code now. 441 */ 442 unsigned int id = success_lbl(labels, nr); 443 struct sock_filter *success_block = new_instr_buf(TWO_INSTRS); 444 len = set_bpf_lbl(success_block, id); 445 len += set_bpf_ret_allow(success_block + len); 446 append_filter_block(head, success_block, len); 447 448 free(line); 449 return head; 450 } 451 452 int parse_include_statement(struct parser_state *state, char *policy_line, 453 unsigned int include_level, 454 const char **ret_filename) 455 { 456 if (strncmp("@include", policy_line, strlen("@include")) != 0) { 457 compiler_warn(state, "invalid statement '%s'", policy_line); 458 return -1; 459 } 460 461 if (policy_line[strlen("@include")] != ' ') { 462 compiler_warn(state, "invalid include statement '%s'", 463 policy_line); 464 return -1; 465 } 466 467 /* 468 * Disallow nested includes: only the initial policy file can have 469 * @include statements. 470 * Nested includes are not currently necessary and make the policy 471 * harder to understand. 472 */ 473 if (include_level > 0) { 474 compiler_warn(state, "@include statement nested too deep"); 475 return -1; 476 } 477 478 char *statement = policy_line; 479 /* Discard "@include" token. */ 480 (void)strsep(&statement, " "); 481 482 /* 483 * compile_filter() below receives a FILE*, so it's not trivial to open 484 * included files relative to the initial policy filename. 485 * To avoid mistakes, force the included file path to be absolute 486 * (start with '/'), or to explicitly load the file relative to CWD by 487 * using './'. 488 */ 489 const char *filename = statement; 490 if (is_implicit_relative_path(filename)) { 491 compiler_warn( 492 state, 493 "implicit relative path '%s' not supported, use './%s'", 494 filename, filename); 495 return -1; 496 } 497 498 *ret_filename = filename; 499 return 0; 500 } 501 502 /* 503 * This is like getline() but supports line wrapping with \. 504 */ 505 static ssize_t getmultiline(char **lineptr, size_t *n, FILE *stream) 506 { 507 ssize_t ret = getline(lineptr, n, stream); 508 if (ret < 0) 509 return ret; 510 511 char *line = *lineptr; 512 /* Eat the newline to make processing below easier. */ 513 if (ret > 0 && line[ret - 1] == '\n') 514 line[--ret] = '\0'; 515 516 /* If the line doesn't end in a backslash, we're done. */ 517 if (ret <= 0 || line[ret - 1] != '\\') 518 return ret; 519 520 /* This line ends in a backslash. Get the nextline. */ 521 line[--ret] = '\0'; 522 size_t next_n = 0; 523 char *next_line = NULL; 524 ssize_t next_ret = getmultiline(&next_line, &next_n, stream); 525 if (next_ret == -1) { 526 free(next_line); 527 /* We couldn't fully read the line, so return an error. */ 528 return -1; 529 } 530 531 /* Merge the lines. */ 532 *n = ret + next_ret + 2; 533 line = realloc(line, *n); 534 line[ret] = ' '; 535 memcpy(&line[ret + 1], next_line, next_ret + 1); 536 free(next_line); 537 *lineptr = line; 538 return ret; 539 } 540 541 int compile_file(const char *filename, FILE *policy_file, 542 struct filter_block *head, struct filter_block **arg_blocks, 543 struct bpf_labels *labels, int use_ret_trap, int allow_logging, 544 unsigned int include_level) 545 { 546 /* clang-format off */ 547 struct parser_state state = { 548 .filename = filename, 549 .line_number = 0, 550 }; 551 /* clang-format on */ 552 /* 553 * Loop through all the lines in the policy file. 554 * Build a jump table for the syscall number. 555 * If the policy line has an arg filter, build the arg filter 556 * as well. 557 * Chain the filter sections together and dump them into 558 * the final buffer at the end. 559 */ 560 char *line = NULL; 561 size_t len = 0; 562 int ret = 0; 563 564 while (getmultiline(&line, &len, policy_file) != -1) { 565 char *policy_line = line; 566 policy_line = strip(policy_line); 567 568 state.line_number++; 569 570 /* Allow comments and empty lines. */ 571 if (*policy_line == '#' || *policy_line == '\0') { 572 /* Reuse |line| in the next getline() call. */ 573 continue; 574 } 575 576 /* Allow @include statements. */ 577 if (*policy_line == '@') { 578 const char *filename = NULL; 579 if (parse_include_statement(&state, policy_line, 580 include_level, 581 &filename) != 0) { 582 compiler_warn( 583 &state, 584 "failed to parse include statement"); 585 ret = -1; 586 goto free_line; 587 } 588 589 FILE *included_file = fopen(filename, "re"); 590 if (included_file == NULL) { 591 compiler_pwarn(&state, "fopen('%s') failed", 592 filename); 593 ret = -1; 594 goto free_line; 595 } 596 if (compile_file(filename, included_file, head, 597 arg_blocks, labels, use_ret_trap, 598 allow_logging, 599 include_level + 1) == -1) { 600 compiler_warn(&state, "'@include %s' failed", 601 filename); 602 fclose(included_file); 603 ret = -1; 604 goto free_line; 605 } 606 fclose(included_file); 607 continue; 608 } 609 610 /* 611 * If it's not a comment, or an empty line, or an @include 612 * statement, treat |policy_line| as a regular policy line. 613 */ 614 char *syscall_name = strsep(&policy_line, ":"); 615 if (policy_line == NULL) { 616 warn("compile_file: malformed policy line, missing " 617 "':'"); 618 ret = -1; 619 goto free_line; 620 } 621 622 policy_line = strip(policy_line); 623 if (*policy_line == '\0') { 624 compiler_warn(&state, "empty policy line"); 625 ret = -1; 626 goto free_line; 627 } 628 629 syscall_name = strip(syscall_name); 630 int nr = lookup_syscall(syscall_name); 631 if (nr < 0) { 632 compiler_warn(&state, "nonexistent syscall '%s'", 633 syscall_name); 634 if (allow_logging) { 635 /* 636 * If we're logging failures, assume we're in a 637 * debugging case and continue. 638 * This is not super risky because an invalid 639 * syscall name is likely caused by a typo or by 640 * leftover lines from a different architecture. 641 * In either case, not including a policy line 642 * is equivalent to killing the process if the 643 * syscall is made, so there's no added attack 644 * surface. 645 */ 646 /* Reuse |line| in the next getline() call. */ 647 continue; 648 } 649 ret = -1; 650 goto free_line; 651 } 652 653 /* 654 * For each syscall, add either a simple ALLOW, 655 * or an arg filter block. 656 */ 657 if (strcmp(policy_line, "1") == 0) { 658 /* Add simple ALLOW. */ 659 append_allow_syscall(head, nr); 660 } else { 661 /* 662 * Create and jump to the label that will hold 663 * the arg filter block. 664 */ 665 unsigned int id = bpf_label_id(labels, syscall_name); 666 struct sock_filter *nr_comp = 667 new_instr_buf(ALLOW_SYSCALL_LEN); 668 bpf_allow_syscall_args(nr_comp, nr, id); 669 append_filter_block(head, nr_comp, ALLOW_SYSCALL_LEN); 670 671 /* Build the arg filter block. */ 672 struct filter_block *block = compile_policy_line( 673 &state, nr, policy_line, id, labels, use_ret_trap); 674 675 if (!block) { 676 if (*arg_blocks) { 677 free_block_list(*arg_blocks); 678 *arg_blocks = NULL; 679 } 680 ret = -1; 681 goto free_line; 682 } 683 684 if (*arg_blocks) { 685 extend_filter_block_list(*arg_blocks, block); 686 } else { 687 *arg_blocks = block; 688 } 689 } 690 /* Reuse |line| in the next getline() call. */ 691 } 692 /* getline(3) returned -1. This can mean EOF or the below errors. */ 693 if (errno == EINVAL || errno == ENOMEM) { 694 if (*arg_blocks) { 695 free_block_list(*arg_blocks); 696 *arg_blocks = NULL; 697 } 698 ret = -1; 699 } 700 701 free_line: 702 free(line); 703 return ret; 704 } 705 706 int compile_filter(const char *filename, FILE *initial_file, 707 struct sock_fprog *prog, int use_ret_trap, int allow_logging) 708 { 709 int ret = 0; 710 struct bpf_labels labels; 711 labels.count = 0; 712 713 if (!initial_file) { 714 warn("compile_filter: |initial_file| is NULL"); 715 return -1; 716 } 717 718 struct filter_block *head = new_filter_block(); 719 struct filter_block *arg_blocks = NULL; 720 721 /* Start filter by validating arch. */ 722 struct sock_filter *valid_arch = new_instr_buf(ARCH_VALIDATION_LEN); 723 size_t len = bpf_validate_arch(valid_arch); 724 append_filter_block(head, valid_arch, len); 725 726 /* Load syscall number. */ 727 struct sock_filter *load_nr = new_instr_buf(ONE_INSTR); 728 len = bpf_load_syscall_nr(load_nr); 729 append_filter_block(head, load_nr, len); 730 731 /* If logging failures, allow the necessary syscalls first. */ 732 if (allow_logging) 733 allow_logging_syscalls(head); 734 735 if (compile_file(filename, initial_file, head, &arg_blocks, &labels, 736 use_ret_trap, allow_logging, 737 0 /* include_level */) != 0) { 738 warn("compile_filter: compile_file() failed"); 739 ret = -1; 740 goto free_filter; 741 } 742 743 /* 744 * If none of the syscalls match, either fall through to KILL, 745 * or return TRAP. 746 */ 747 if (!use_ret_trap) 748 append_ret_kill(head); 749 else 750 append_ret_trap(head); 751 752 /* Allocate the final buffer, now that we know its size. */ 753 size_t final_filter_len = 754 head->total_len + (arg_blocks ? arg_blocks->total_len : 0); 755 if (final_filter_len > BPF_MAXINSNS) { 756 ret = -1; 757 goto free_filter; 758 } 759 760 struct sock_filter *final_filter = 761 calloc(final_filter_len, sizeof(struct sock_filter)); 762 763 if (flatten_block_list(head, final_filter, 0, final_filter_len) < 0) { 764 free(final_filter); 765 ret = -1; 766 goto free_filter; 767 } 768 769 if (flatten_block_list(arg_blocks, final_filter, head->total_len, 770 final_filter_len) < 0) { 771 free(final_filter); 772 ret = -1; 773 goto free_filter; 774 } 775 776 if (bpf_resolve_jumps(&labels, final_filter, final_filter_len) < 0) { 777 free(final_filter); 778 ret = -1; 779 goto free_filter; 780 } 781 782 prog->filter = final_filter; 783 prog->len = final_filter_len; 784 785 free_filter: 786 free_block_list(head); 787 free_block_list(arg_blocks); 788 free_label_strings(&labels); 789 return ret; 790 } 791 792 int flatten_block_list(struct filter_block *head, struct sock_filter *filter, 793 size_t index, size_t cap) 794 { 795 size_t _index = index; 796 797 struct filter_block *curr; 798 size_t i; 799 800 for (curr = head; curr; curr = curr->next) { 801 for (i = 0; i < curr->len; i++) { 802 if (_index >= cap) 803 return -1; 804 filter[_index++] = curr->instrs[i]; 805 } 806 } 807 return 0; 808 } 809 810 void free_block_list(struct filter_block *head) 811 { 812 struct filter_block *current, *prev; 813 814 current = head; 815 while (current) { 816 free(current->instrs); 817 prev = current; 818 current = current->next; 819 free(prev); 820 } 821 } 822