LCOV - code coverage report
Current view: top level - bpfilter/cgen - swich.c (source / functions) Coverage Total Hit
Test: coverage.lcov Lines: 96.4 % 55 53
Test Date: 2025-11-24 12:34:34 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/helper.h>
      18                 :             : #include <bpfilter/list.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                 :        1095 : 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                 :             :     bf_assert(option);
      64                 :             :     bf_assert(insns);
      65                 :             : 
      66                 :        1095 :     _option = malloc(sizeof(*_option) + (sizeof(struct bpf_insn) * insns_len));
      67         [ +  - ]:        1095 :     if (!_option)
      68                 :             :         return -ENOMEM;
      69                 :             : 
      70                 :        1095 :     _option->imm = imm;
      71                 :        1095 :     _option->insns_len = insns_len;
      72                 :        1095 :     memcpy(_option->insns, insns, sizeof(struct bpf_insn) * insns_len);
      73                 :             : 
      74                 :        1095 :     *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                 :         770 : static void _bf_swich_option_free(struct bf_swich_option **option)
      88                 :             : {
      89                 :             :     bf_assert(option);
      90                 :             : 
      91   [ -  -  -  +  :        2190 :     if (!*option)
          -  +  +  -  +  
                      - ]
      92                 :             :         return;
      93                 :             : 
      94                 :           0 :     free(*option);
      95                 :        1095 :     *option = NULL;
      96                 :             : }
      97                 :             : 
      98                 :         325 : int bf_swich_init(struct bf_swich *swich, struct bf_program *program, int reg)
      99                 :             : {
     100                 :             :     bf_assert(swich);
     101                 :             :     bf_assert(program);
     102                 :             : 
     103                 :         325 :     swich->program = program;
     104                 :         325 :     swich->reg = reg;
     105                 :             : 
     106                 :         325 :     bf_list_init(
     107                 :             :         &swich->options,
     108                 :         325 :         (bf_list_ops[]) {{.free = (bf_list_ops_free)_bf_swich_option_free}});
     109                 :             : 
     110                 :         325 :     swich->default_opt = NULL;
     111                 :             : 
     112                 :         325 :     return 0;
     113                 :             : }
     114                 :             : 
     115                 :         325 : void bf_swich_cleanup(struct bf_swich *swich)
     116                 :             : {
     117                 :             :     bf_assert(swich);
     118                 :             : 
     119                 :         325 :     bf_list_clean(&swich->options);
     120                 :             :     _bf_swich_option_free(&swich->default_opt);
     121                 :         325 :     free(swich->default_opt);
     122                 :         325 : }
     123                 :             : 
     124                 :         770 : int bf_swich_add_option(struct bf_swich *swich, uint32_t imm,
     125                 :             :                         const struct bpf_insn *insns, size_t insns_len)
     126                 :             : {
     127                 :         770 :     _free_bf_swich_option_ struct bf_swich_option *option = NULL;
     128                 :             :     int r;
     129                 :             : 
     130                 :             :     bf_assert(swich);
     131                 :             :     bf_assert(insns);
     132                 :             : 
     133                 :         770 :     r = _bf_swich_option_new(&option, imm, insns, insns_len);
     134         [ +  - ]:         770 :     if (r)
     135                 :             :         return r;
     136                 :             : 
     137                 :         770 :     r = bf_list_add_tail(&swich->options, option);
     138         [ +  - ]:         770 :     if (r)
     139                 :             :         return r;
     140                 :             : 
     141                 :         770 :     TAKE_PTR(option);
     142                 :             : 
     143                 :         770 :     return 0;
     144                 :             : }
     145                 :             : 
     146                 :         325 : int bf_swich_set_default(struct bf_swich *swich, const struct bpf_insn *insns,
     147                 :             :                          size_t insns_len)
     148                 :             : {
     149                 :         325 :     _free_bf_swich_option_ struct bf_swich_option *option = NULL;
     150                 :             :     int r;
     151                 :             : 
     152                 :             :     bf_assert(swich);
     153                 :             :     bf_assert(insns);
     154                 :             : 
     155         [ -  + ]:         325 :     if (swich->default_opt) {
     156         [ #  # ]:           0 :         bf_warn("default bf_swich option already exists, replacing it");
     157                 :             :         _bf_swich_option_free(&swich->default_opt);
     158                 :             :     }
     159                 :             : 
     160                 :         325 :     r = _bf_swich_option_new(&option, 0, insns, insns_len);
     161         [ +  - ]:         325 :     if (r)
     162                 :             :         return r;
     163                 :             : 
     164                 :         325 :     swich->default_opt = TAKE_PTR(option);
     165                 :             : 
     166                 :         325 :     return 0;
     167                 :             : }
     168                 :             : 
     169                 :         325 : int bf_swich_generate(struct bf_swich *swich)
     170                 :             : {
     171                 :         325 :     struct bf_program *program = swich->program;
     172                 :             : 
     173                 :             :     // Match an option against the value and jump to the related bytecode
     174   [ +  -  +  + ]:        1865 :     bf_list_foreach (&swich->options, option_node) {
     175                 :             :         struct bf_swich_option *option = bf_list_node_get_data(option_node);
     176                 :             : 
     177   [ -  +  +  + ]:         770 :         option->jmp = bf_jmpctx_get(
     178                 :             :             program, BPF_JMP_IMM(BPF_JEQ, swich->reg, option->imm, 0));
     179                 :             :     }
     180                 :             : 
     181                 :             :     // Insert the default option if any
     182         [ +  - ]:         325 :     if (swich->default_opt)
     183         [ -  + ]:         325 :         swich->default_opt->jmp = bf_jmpctx_get(program, BPF_JMP_A(0));
     184                 :             : 
     185                 :             :     // Insert each option's bytecode
     186   [ +  -  +  + ]:        1865 :     bf_list_foreach (&swich->options, option_node) {
     187                 :             :         struct bf_swich_option *option = bf_list_node_get_data(option_node);
     188                 :             : 
     189                 :         770 :         bf_jmpctx_cleanup(&option->jmp);
     190         [ +  + ]:        1540 :         for (size_t i = 0; i < option->insns_len; ++i)
     191         [ +  - ]:         770 :             EMIT(program, option->insns[i]);
     192   [ -  +  +  + ]:         770 :         option->jmp = bf_jmpctx_get(program, BPF_JMP_A(0));
     193                 :             :     }
     194                 :             : 
     195                 :             :     // Insert the default instructions
     196         [ +  - ]:         325 :     if (swich->default_opt) {
     197                 :         325 :         bf_jmpctx_cleanup(&swich->default_opt->jmp);
     198         [ +  + ]:         650 :         for (size_t i = 0; i < swich->default_opt->insns_len; ++i)
     199         [ +  - ]:         325 :             EMIT(program, swich->default_opt->insns[i]);
     200                 :             :     }
     201                 :             : 
     202   [ +  -  +  +  :        2190 :     bf_list_foreach (&swich->options, option_node) {
                   +  + ]
     203                 :             :         struct bf_swich_option *option = bf_list_node_get_data(option_node);
     204                 :             : 
     205                 :         770 :         bf_jmpctx_cleanup(&option->jmp);
     206                 :             :     }
     207                 :             : 
     208                 :             :     return 0;
     209                 :             : }
        

Generated by: LCOV version 2.0-1