LCOV - code coverage report
Current view: top level - libbpfilter/cgen - swich.c (source / functions) Coverage Total Hit
Test: coverage.lcov Lines: 96.3 % 54 52
Test Date: 2026-05-28 10:53:42 Functions: 100.0 % 7 7
Branches: 60.3 % 58 35

             Branch data     Line data    Source code
       1                 :             : /* SPDX-License-Identifier: GPL-2.0-only */
       2                 :             : /*
       3                 :             :  * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
       4                 :             :  */
       5                 :             : 
       6                 :             : #include "cgen/swich.h"
       7                 :             : 
       8                 :             : #include <linux/bpf.h>
       9                 :             : #include <linux/bpf_common.h>
      10                 :             : 
      11                 :             : #include <errno.h>
      12                 :             : #include <stddef.h>
      13                 :             : #include <stdint.h>
      14                 :             : #include <stdlib.h>
      15                 :             : #include <string.h>
      16                 :             : 
      17                 :             : #include <bpfilter/core/list.h>
      18                 :             : #include <bpfilter/helper.h>
      19                 :             : #include <bpfilter/logger.h>
      20                 :             : 
      21                 :             : #include "cgen/jmp.h"
      22                 :             : #include "cgen/program.h"
      23                 :             : #include "filter.h"
      24                 :             : 
      25                 :             : /// Cleanup attribute for a @ref bf_swich_option variable.
      26                 :             : #define _free_bf_swich_option_ __attribute__((cleanup(_bf_swich_option_free)))
      27                 :             : 
      28                 :             : /**
      29                 :             :  * @struct bf_swich_option
      30                 :             :  *
      31                 :             :  * Represent a @c case of the switch: contains the instructions to execute if
      32                 :             :  * the switch's register is equal to a specific value.
      33                 :             :  */
      34                 :             : struct bf_swich_option
      35                 :             : {
      36                 :             :     /// Immediate value to match against the switch's register.
      37                 :             :     uint32_t imm;
      38                 :             :     /// Jump context used to jump from the comparison to the bytecode to
      39                 :             :     /// execute, then to the end of the switch.
      40                 :             :     struct bf_jmpctx jmp;
      41                 :             :     /// Number of instructions for this option.
      42                 :             :     size_t insns_len;
      43                 :             :     /// Instructions to execute, as a flexible array member.
      44                 :             :     struct bpf_insn insns[];
      45                 :             : };
      46                 :             : 
      47                 :             : static void _bf_swich_option_free(struct bf_swich_option **option);
      48                 :             : 
      49                 :             : /**
      50                 :             :  * Allocate and initialize a new switch option (case).
      51                 :             :  *
      52                 :             :  * @param option Switch option to allocate and initialize. Can't be NULL.
      53                 :             :  * @param imm Immediate value to match against the switch's register.
      54                 :             :  * @param insns Instructions to execute if the option matches.
      55                 :             :  * @param insns_len Number of instructions in @p insns .
      56                 :             :  * @return 0 on success, or negative errno value on failure.
      57                 :             :  */
      58                 :       13320 : static int _bf_swich_option_new(struct bf_swich_option **option, uint32_t imm,
      59                 :             :                                 const struct bpf_insn *insns, size_t insns_len)
      60                 :             : {
      61                 :             :     _free_bf_swich_option_ struct bf_swich_option *_option = NULL;
      62                 :             : 
      63                 :             :     assert(option);
      64                 :             :     assert(insns);
      65                 :             : 
      66                 :       13320 :     _option = malloc(sizeof(*_option) + (sizeof(struct bpf_insn) * insns_len));
      67         [ +  - ]:       13320 :     if (!_option)
      68                 :             :         return -ENOMEM;
      69                 :             : 
      70                 :       13320 :     _option->imm = imm;
      71                 :       13320 :     _option->insns_len = insns_len;
      72                 :       13320 :     memcpy(_option->insns, insns, sizeof(struct bpf_insn) * insns_len);
      73                 :             : 
      74                 :       13320 :     *option = TAKE_PTR(_option);
      75                 :             : 
      76                 :             :     return 0;
      77                 :             : }
      78                 :             : 
      79                 :             : /**
      80                 :             :  * Free a switch option.
      81                 :             :  *
      82                 :             :  * Free @p option is it points to valid memory. If @p option points to a NULL
      83                 :             :  * pointer, nothing is done.
      84                 :             :  *
      85                 :             :  * @param option Option to free. Can't be NULL.
      86                 :             :  */
      87                 :        9712 : static void _bf_swich_option_free(struct bf_swich_option **option)
      88                 :             : {
      89                 :             :     assert(option);
      90                 :             : 
      91   [ -  -  -  +  :       26640 :     if (!*option)
          -  +  +  -  +  
                      - ]
      92                 :             :         return;
      93                 :             : 
      94                 :           0 :     free(*option);
      95                 :       13320 :     *option = NULL;
      96                 :             : }
      97                 :             : 
      98                 :        3608 : int bf_swich_init(struct bf_swich *swich, struct bf_program *program, int reg)
      99                 :             : {
     100                 :             :     assert(swich);
     101                 :             :     assert(program);
     102                 :             : 
     103                 :        3608 :     swich->program = program;
     104                 :        3608 :     swich->reg = reg;
     105                 :             : 
     106                 :        3608 :     bf_list_init(
     107                 :             :         &swich->options,
     108                 :        3608 :         (bf_list_ops[]) {{.free = (bf_list_ops_free)_bf_swich_option_free}});
     109                 :             : 
     110                 :        3608 :     swich->default_opt = NULL;
     111                 :             : 
     112                 :        3608 :     return 0;
     113                 :             : }
     114                 :             : 
     115                 :        3608 : void bf_swich_cleanup(struct bf_swich *swich)
     116                 :             : {
     117                 :             :     assert(swich);
     118                 :             : 
     119                 :        3608 :     bf_list_clean(&swich->options);
     120                 :             :     _bf_swich_option_free(&swich->default_opt);
     121                 :        3608 : }
     122                 :             : 
     123                 :        9712 : int bf_swich_add_option(struct bf_swich *swich, uint32_t imm,
     124                 :             :                         const struct bpf_insn *insns, size_t insns_len)
     125                 :             : {
     126                 :        9712 :     _free_bf_swich_option_ struct bf_swich_option *option = NULL;
     127                 :             :     int r;
     128                 :             : 
     129                 :             :     assert(swich);
     130                 :             :     assert(insns);
     131                 :             : 
     132                 :        9712 :     r = _bf_swich_option_new(&option, imm, insns, insns_len);
     133         [ +  - ]:        9712 :     if (r)
     134                 :             :         return r;
     135                 :             : 
     136                 :        9712 :     r = bf_list_add_tail(&swich->options, option);
     137         [ +  - ]:        9712 :     if (r)
     138                 :             :         return r;
     139                 :             : 
     140                 :        9712 :     TAKE_PTR(option);
     141                 :             : 
     142                 :        9712 :     return 0;
     143                 :             : }
     144                 :             : 
     145                 :        3608 : int bf_swich_set_default(struct bf_swich *swich, const struct bpf_insn *insns,
     146                 :             :                          size_t insns_len)
     147                 :             : {
     148                 :        3608 :     _free_bf_swich_option_ struct bf_swich_option *option = NULL;
     149                 :             :     int r;
     150                 :             : 
     151                 :             :     assert(swich);
     152                 :             :     assert(insns);
     153                 :             : 
     154         [ -  + ]:        3608 :     if (swich->default_opt) {
     155         [ #  # ]:           0 :         bf_warn("default bf_swich option already exists, replacing it");
     156                 :             :         _bf_swich_option_free(&swich->default_opt);
     157                 :             :     }
     158                 :             : 
     159                 :        3608 :     r = _bf_swich_option_new(&option, 0, insns, insns_len);
     160         [ +  - ]:        3608 :     if (r)
     161                 :             :         return r;
     162                 :             : 
     163                 :        3608 :     swich->default_opt = TAKE_PTR(option);
     164                 :             : 
     165                 :        3608 :     return 0;
     166                 :             : }
     167                 :             : 
     168                 :        3608 : int bf_swich_generate(struct bf_swich *swich)
     169                 :             : {
     170                 :        3608 :     struct bf_program *program = swich->program;
     171                 :             : 
     172                 :             :     // Match an option against the value and jump to the related bytecode
     173   [ +  -  +  + ]:       23032 :     bf_list_foreach (&swich->options, option_node) {
     174                 :             :         struct bf_swich_option *option = bf_list_node_get_data(option_node);
     175                 :             : 
     176   [ +  -  +  + ]:        9712 :         option->jmp = bf_jmpctx_get(
     177                 :             :             program, BPF_JMP_IMM(BPF_JEQ, swich->reg, option->imm, 0));
     178                 :             :     }
     179                 :             : 
     180                 :             :     // Insert the default option if any
     181         [ +  - ]:        3608 :     if (swich->default_opt)
     182         [ +  - ]:        3608 :         swich->default_opt->jmp = bf_jmpctx_get(program, BPF_JMP_A(0));
     183                 :             : 
     184                 :             :     // Insert each option's bytecode
     185   [ +  -  +  + ]:       23032 :     bf_list_foreach (&swich->options, option_node) {
     186                 :             :         struct bf_swich_option *option = bf_list_node_get_data(option_node);
     187                 :             : 
     188                 :        9712 :         bf_jmpctx_cleanup(&option->jmp);
     189         [ +  + ]:       19424 :         for (size_t i = 0; i < option->insns_len; ++i)
     190         [ +  - ]:        9712 :             EMIT(program, option->insns[i]);
     191   [ +  -  +  + ]:        9712 :         option->jmp = bf_jmpctx_get(program, BPF_JMP_A(0));
     192                 :             :     }
     193                 :             : 
     194                 :             :     // Insert the default instructions
     195         [ +  - ]:        3608 :     if (swich->default_opt) {
     196                 :        3608 :         bf_jmpctx_cleanup(&swich->default_opt->jmp);
     197         [ +  + ]:        7216 :         for (size_t i = 0; i < swich->default_opt->insns_len; ++i)
     198         [ +  - ]:        3608 :             EMIT(program, swich->default_opt->insns[i]);
     199                 :             :     }
     200                 :             : 
     201   [ +  -  +  +  :       26640 :     bf_list_foreach (&swich->options, option_node) {
                   +  + ]
     202                 :             :         struct bf_swich_option *option = bf_list_node_get_data(option_node);
     203                 :             : 
     204                 :        9712 :         bf_jmpctx_cleanup(&option->jmp);
     205                 :             :     }
     206                 :             : 
     207                 :             :     return 0;
     208                 :             : }
        

Generated by: LCOV version 2.0-1