LCOV - code coverage report
Current view: top level - bpfilter/cgen - cgen.c (source / functions) Coverage Total Hit
Test: coverage.lcov Lines: 86.7 % 210 182
Test Date: 2026-01-27 20:38:38 Functions: 92.9 % 14 13
Branches: 43.7 % 190 83

             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/cgen.h"
       7                 :             : 
       8                 :             : #include <errno.h>
       9                 :             : #include <stddef.h>
      10                 :             : #include <stdint.h>
      11                 :             : #include <stdlib.h>
      12                 :             : #include <string.h>
      13                 :             : #include <sys/types.h>
      14                 :             : 
      15                 :             : #include <bpfilter/chain.h>
      16                 :             : #include <bpfilter/counter.h>
      17                 :             : #include <bpfilter/dump.h>
      18                 :             : #include <bpfilter/front.h>
      19                 :             : #include <bpfilter/helper.h>
      20                 :             : #include <bpfilter/hook.h>
      21                 :             : #include <bpfilter/io.h>
      22                 :             : #include <bpfilter/list.h>
      23                 :             : #include <bpfilter/logger.h>
      24                 :             : #include <bpfilter/ns.h>
      25                 :             : #include <bpfilter/pack.h>
      26                 :             : 
      27                 :             : #include "cgen/dump.h"
      28                 :             : #include "cgen/prog/link.h"
      29                 :             : #include "cgen/program.h"
      30                 :             : #include "ctx.h"
      31                 :             : #include "opts.h"
      32                 :             : 
      33                 :         114 : static int _bf_cgen_get_chain_pindir_fd(const char *name)
      34                 :             : {
      35                 :         114 :     _cleanup_close_ int bf_fd = -1;
      36                 :         114 :     _cleanup_close_ int chain_fd = -1;
      37                 :             : 
      38                 :             :     assert(name);
      39                 :             : 
      40                 :         114 :     bf_fd = bf_ctx_get_pindir_fd();
      41         [ +  - ]:         114 :     if (bf_fd < 0)
      42                 :             :         return bf_fd;
      43                 :             : 
      44                 :         114 :     chain_fd = bf_opendir_at(bf_fd, name, true);
      45         [ -  + ]:         114 :     if (chain_fd < 0)
      46                 :             :         return chain_fd;
      47                 :             : 
      48                 :         114 :     return TAKE_FD(chain_fd);
      49                 :             : }
      50                 :             : 
      51                 :          60 : int bf_cgen_new(struct bf_cgen **cgen, enum bf_front front,
      52                 :             :                 struct bf_chain **chain)
      53                 :             : {
      54                 :             :     assert(cgen);
      55                 :             :     assert(chain);
      56                 :             : 
      57                 :          60 :     *cgen = malloc(sizeof(struct bf_cgen));
      58         [ +  - ]:          60 :     if (!*cgen)
      59                 :             :         return -ENOMEM;
      60                 :             : 
      61                 :          60 :     (*cgen)->front = front;
      62                 :          60 :     (*cgen)->program = NULL;
      63                 :             :     (*cgen)->chain = NULL;
      64                 :          60 :     (*cgen)->chain = TAKE_PTR(*chain);
      65                 :             : 
      66                 :          60 :     return 0;
      67                 :             : }
      68                 :             : 
      69                 :           3 : int bf_cgen_new_from_pack(struct bf_cgen **cgen, bf_rpack_node_t node)
      70                 :             : {
      71                 :           3 :     _free_bf_cgen_ struct bf_cgen *_cgen = NULL;
      72                 :             :     bf_rpack_node_t child;
      73                 :             :     int r;
      74                 :             : 
      75                 :             :     assert(cgen);
      76                 :             : 
      77                 :           3 :     _cgen = malloc(sizeof(*_cgen));
      78         [ +  - ]:           3 :     if (!_cgen)
      79                 :             :         return -ENOMEM;
      80                 :             : 
      81                 :           3 :     _cgen->program = NULL;
      82                 :             : 
      83   [ -  +  -  +  :           3 :     r = bf_rpack_kv_enum(node, "front", &_cgen->front, 0, _BF_FRONT_MAX);
                   -  - ]
      84                 :             :     if (r)
      85         [ #  # ]:           0 :         return bf_rpack_key_err(r, "bf_cgen.front");
      86                 :             : 
      87                 :           3 :     r = bf_rpack_kv_obj(node, "chain", &child);
      88         [ -  + ]:           3 :     if (r)
      89         [ #  # ]:           0 :         return bf_rpack_key_err(r, "bf_cgen.chain");
      90                 :             : 
      91                 :           3 :     r = bf_chain_new_from_pack(&_cgen->chain, child);
      92         [ -  + ]:           3 :     if (r)
      93         [ #  # ]:           0 :         return bf_rpack_key_err(r, "bf_cgen.chain");
      94                 :             : 
      95                 :           3 :     r = bf_rpack_kv_node(node, "program", &child);
      96         [ -  + ]:           3 :     if (r)
      97         [ #  # ]:           0 :         return bf_rpack_key_err(r, "bf_cgen.program");
      98         [ +  - ]:           3 :     if (!bf_rpack_is_nil(child)) {
      99                 :           3 :         _cleanup_close_ int dir_fd = -1;
     100                 :             : 
     101         [ -  + ]:           3 :         if ((dir_fd = _bf_cgen_get_chain_pindir_fd(_cgen->chain->name)) < 0) {
     102         [ #  # ]:           0 :             return bf_err_r(dir_fd,
     103                 :             :                             "failed to open chain pin directory for '%s'",
     104                 :             :                             _cgen->chain->name);
     105                 :             :         }
     106                 :             : 
     107                 :           3 :         r = bf_program_new_from_pack(&_cgen->program, _cgen->chain, dir_fd,
     108                 :             :                                      child);
     109         [ +  - ]:           3 :         if (r)
     110                 :             :             return r;
     111                 :             :     }
     112                 :             : 
     113                 :           3 :     *cgen = TAKE_PTR(_cgen);
     114                 :             : 
     115                 :           3 :     return 0;
     116                 :             : }
     117                 :             : 
     118                 :         127 : void bf_cgen_free(struct bf_cgen **cgen)
     119                 :             : {
     120                 :          64 :     _cleanup_close_ int pin_fd = -1;
     121                 :             : 
     122                 :             :     assert(cgen);
     123                 :             : 
     124         [ +  + ]:         127 :     if (!*cgen)
     125                 :             :         return;
     126                 :             : 
     127                 :             :     /* Perform a non-recursive removal of the chain's pin directory: if
     128                 :             :      * the chain hasn't been pinned (e.g. due to a failure), the pin directory
     129                 :             :      * will be empty and will be removed. If the chain is valid and pinned, then
     130                 :             :      * the removal of the pin directory will fail, but that's alright. */
     131   [ +  -  +  - ]:          63 :     if (bf_opts_persist() && (pin_fd = bf_ctx_get_pindir_fd()) >= 0)
     132                 :          63 :         bf_rmdir_at(pin_fd, (*cgen)->chain->name, false);
     133                 :             : 
     134                 :          63 :     bf_program_free(&(*cgen)->program);
     135                 :          63 :     bf_chain_free(&(*cgen)->chain);
     136                 :             : 
     137                 :          63 :     free(*cgen);
     138                 :          63 :     *cgen = NULL;
     139                 :             : }
     140                 :             : 
     141                 :         169 : int bf_cgen_pack(const struct bf_cgen *cgen, bf_wpack_t *pack)
     142                 :             : {
     143                 :             :     assert(cgen);
     144                 :             :     assert(pack);
     145                 :             : 
     146                 :         169 :     bf_wpack_kv_enum(pack, "front", cgen->front);
     147                 :             : 
     148                 :         169 :     bf_wpack_open_object(pack, "chain");
     149                 :         169 :     bf_chain_pack(cgen->chain, pack);
     150                 :         169 :     bf_wpack_close_object(pack);
     151                 :             : 
     152         [ +  - ]:         169 :     if (cgen->program) {
     153                 :         169 :         bf_wpack_open_object(pack, "program");
     154                 :         169 :         bf_program_pack(cgen->program, pack);
     155                 :         169 :         bf_wpack_close_object(pack);
     156                 :             :     } else {
     157                 :             :         bf_wpack_kv_nil(pack, "program");
     158                 :             :     }
     159                 :             : 
     160         [ -  + ]:         169 :     return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
     161                 :             : }
     162                 :             : 
     163                 :          15 : void bf_cgen_dump(const struct bf_cgen *cgen, prefix_t *prefix)
     164                 :             : {
     165                 :             :     assert(cgen);
     166                 :             :     assert(prefix);
     167                 :             : 
     168         [ +  - ]:          15 :     DUMP(prefix, "struct bf_cgen at %p", cgen);
     169                 :             : 
     170                 :          15 :     bf_dump_prefix_push(prefix);
     171         [ +  - ]:          15 :     DUMP(prefix, "front: %s", bf_front_to_str(cgen->front));
     172                 :             : 
     173                 :             :     // Chain
     174         [ +  - ]:          15 :     DUMP(prefix, "chain: struct bf_chain *");
     175                 :          15 :     bf_dump_prefix_push(prefix);
     176                 :          15 :     bf_chain_dump(cgen->chain, bf_dump_prefix_last(prefix));
     177                 :          15 :     bf_dump_prefix_pop(prefix);
     178                 :             : 
     179                 :             :     // Programs
     180         [ +  + ]:          15 :     if (cgen->program) {
     181         [ +  - ]:           3 :         DUMP(bf_dump_prefix_last(prefix), "program: struct bf_program *");
     182                 :           3 :         bf_dump_prefix_push(prefix);
     183                 :           3 :         bf_program_dump(cgen->program, bf_dump_prefix_last(prefix));
     184                 :           3 :         bf_dump_prefix_pop(prefix);
     185                 :             :     } else {
     186         [ +  - ]:          12 :         DUMP(bf_dump_prefix_last(prefix), "program: (struct bf_program *)NULL");
     187                 :             :     }
     188                 :             : 
     189                 :          15 :     bf_dump_prefix_pop(prefix);
     190                 :          15 : }
     191                 :             : 
     192                 :          32 : int bf_cgen_get_counter(const struct bf_cgen *cgen,
     193                 :             :                         enum bf_counter_type counter_idx,
     194                 :             :                         struct bf_counter *counter)
     195                 :             : {
     196                 :             :     assert(cgen);
     197                 :             :     assert(counter);
     198                 :             : 
     199                 :             :     /* There are two more counter than rules. The special counters must
     200                 :             :      * be accessed via the specific values, to avoid confusion. */
     201         [ +  + ]:          32 :     enum bf_counter_type rule_count = bf_list_size(&cgen->chain->rules);
     202         [ +  + ]:          32 :     if (counter_idx == BF_COUNTER_POLICY) {
     203                 :             :         counter_idx = rule_count;
     204         [ +  + ]:          19 :     } else if (counter_idx == BF_COUNTER_ERRORS) {
     205                 :          13 :         counter_idx = rule_count + 1;
     206         [ +  - ]:           6 :     } else if (counter_idx < 0 || counter_idx >= rule_count) {
     207                 :             :         return -EINVAL;
     208                 :             :     }
     209                 :             : 
     210                 :          32 :     return bf_program_get_counter(cgen->program, counter_idx, counter);
     211                 :             : }
     212                 :             : 
     213                 :          48 : int bf_cgen_set(struct bf_cgen *cgen, const struct bf_ns *ns,
     214                 :             :                 struct bf_hookopts **hookopts)
     215                 :             : {
     216                 :          48 :     _free_bf_program_ struct bf_program *prog = NULL;
     217                 :          48 :     _cleanup_close_ int pindir_fd = -1;
     218                 :             :     int r;
     219                 :             : 
     220                 :             :     assert(cgen);
     221                 :             : 
     222         [ +  - ]:          48 :     if (bf_opts_persist()) {
     223                 :          48 :         pindir_fd = _bf_cgen_get_chain_pindir_fd(cgen->chain->name);
     224         [ +  - ]:          48 :         if (pindir_fd < 0)
     225                 :             :             return pindir_fd;
     226                 :             :     }
     227                 :             : 
     228                 :          48 :     r = bf_program_new(&prog, cgen->chain);
     229         [ +  - ]:          48 :     if (r < 0)
     230                 :             :         return r;
     231                 :             : 
     232                 :          48 :     r = bf_program_generate(prog);
     233         [ -  + ]:          48 :     if (r < 0)
     234         [ #  # ]:           0 :         return bf_err_r(r, "failed to generate bf_program");
     235                 :             : 
     236                 :          48 :     r = bf_program_load(prog);
     237         [ -  + ]:          48 :     if (r < 0)
     238         [ #  # ]:           0 :         return bf_err_r(r, "failed to load the chain");
     239                 :             : 
     240         [ +  + ]:          48 :     if (hookopts) {
     241                 :          19 :         r = bf_ns_set(ns, bf_ctx_get_ns());
     242         [ -  + ]:          19 :         if (r)
     243         [ #  # ]:           0 :             return bf_err_r(r, "failed to switch to the client's namespaces");
     244                 :             : 
     245                 :          19 :         r = bf_program_attach(prog, hookopts);
     246         [ +  + ]:          19 :         if (r < 0)
     247         [ +  - ]:           2 :             return bf_err_r(r, "failed to load and attach the chain");
     248                 :             : 
     249         [ -  + ]:          17 :         if (bf_ns_set(bf_ctx_get_ns(), ns))
     250         [ #  # ]:           0 :             bf_abort("failed to restore previous namespaces, aborting");
     251                 :             :     }
     252                 :             : 
     253         [ +  - ]:          46 :     if (bf_opts_persist()) {
     254                 :          46 :         r = bf_program_pin(prog, pindir_fd);
     255         [ +  - ]:          46 :         if (r)
     256                 :             :             return r;
     257                 :             :     }
     258                 :             : 
     259                 :          46 :     cgen->program = TAKE_PTR(prog);
     260                 :             : 
     261                 :          46 :     return r;
     262                 :             : }
     263                 :             : 
     264                 :          12 : int bf_cgen_load(struct bf_cgen *cgen)
     265                 :             : {
     266                 :          12 :     _free_bf_program_ struct bf_program *prog = NULL;
     267                 :          12 :     _cleanup_close_ int pindir_fd = -1;
     268                 :             :     int r;
     269                 :             : 
     270                 :             :     assert(cgen);
     271                 :             : 
     272         [ +  - ]:          12 :     if (bf_opts_persist()) {
     273                 :          12 :         pindir_fd = _bf_cgen_get_chain_pindir_fd(cgen->chain->name);
     274         [ +  - ]:          12 :         if (pindir_fd < 0)
     275                 :             :             return pindir_fd;
     276                 :             :     }
     277                 :             : 
     278                 :          12 :     r = bf_program_new(&prog, cgen->chain);
     279         [ +  - ]:          12 :     if (r < 0)
     280                 :             :         return r;
     281                 :             : 
     282                 :          12 :     r = bf_program_generate(prog);
     283         [ -  + ]:          12 :     if (r < 0)
     284         [ #  # ]:           0 :         return bf_err_r(r, "failed to generate bf_program");
     285                 :             : 
     286                 :          12 :     r = bf_program_load(prog);
     287         [ -  + ]:          12 :     if (r < 0)
     288         [ #  # ]:           0 :         return bf_err_r(r, "failed to load the chain");
     289                 :             : 
     290         [ +  - ]:          12 :     if (bf_opts_persist()) {
     291                 :          12 :         r = bf_program_pin(prog, pindir_fd);
     292         [ +  - ]:          12 :         if (r)
     293                 :             :             return r;
     294                 :             :     }
     295                 :             : 
     296         [ +  - ]:          12 :     bf_info("load %s", cgen->chain->name);
     297                 :          12 :     bf_cgen_dump(cgen, EMPTY_PREFIX);
     298                 :             : 
     299                 :          12 :     cgen->program = TAKE_PTR(prog);
     300                 :             : 
     301                 :          12 :     return r;
     302                 :             : }
     303                 :             : 
     304                 :           9 : int bf_cgen_attach(struct bf_cgen *cgen, const struct bf_ns *ns,
     305                 :             :                    struct bf_hookopts **hookopts)
     306                 :             : {
     307                 :           9 :     _cleanup_close_ int pindir_fd = -1;
     308                 :             :     int r;
     309                 :             : 
     310                 :             :     assert(cgen);
     311                 :             :     assert(ns);
     312                 :             :     assert(hookopts);
     313                 :             : 
     314         [ +  - ]:           9 :     bf_info("attaching %s to %s", cgen->chain->name,
     315                 :             :             bf_hook_to_str(cgen->chain->hook));
     316                 :           9 :     bf_hookopts_dump(*hookopts, EMPTY_PREFIX);
     317                 :             : 
     318         [ +  - ]:           9 :     if (bf_opts_persist()) {
     319                 :           9 :         pindir_fd = _bf_cgen_get_chain_pindir_fd(cgen->chain->name);
     320         [ +  - ]:           9 :         if (pindir_fd < 0)
     321                 :             :             return pindir_fd;
     322                 :             :     }
     323                 :             : 
     324                 :           9 :     r = bf_ns_set(ns, bf_ctx_get_ns());
     325         [ -  + ]:           9 :     if (r)
     326         [ #  # ]:           0 :         return bf_err_r(r, "failed to switch to the client's namespaces");
     327                 :             : 
     328                 :           9 :     r = bf_program_attach(cgen->program, hookopts);
     329         [ +  + ]:           9 :     if (r < 0)
     330         [ +  - ]:           2 :         return bf_err_r(r, "failed to attach chain '%s'", cgen->chain->name);
     331                 :             : 
     332         [ -  + ]:           7 :     if (bf_ns_set(bf_ctx_get_ns(), ns))
     333         [ #  # ]:           0 :         bf_abort("failed to restore previous namespaces, aborting");
     334                 :             : 
     335         [ +  - ]:           7 :     if (bf_opts_persist()) {
     336                 :           7 :         r = bf_link_pin(cgen->program->link, pindir_fd);
     337         [ -  + ]:           7 :         if (r) {
     338                 :           0 :             bf_program_detach(cgen->program);
     339                 :           0 :             return r;
     340                 :             :         }
     341                 :             :     }
     342                 :             : 
     343                 :             :     return r;
     344                 :             : }
     345                 :             : 
     346                 :           4 : int bf_cgen_update(struct bf_cgen *cgen, struct bf_chain **new_chain)
     347                 :             : {
     348                 :           4 :     _free_bf_program_ struct bf_program *new_prog = NULL;
     349                 :           4 :     _cleanup_close_ int pindir_fd = -1;
     350                 :             :     struct bf_program *old_prog;
     351                 :             :     int r;
     352                 :             : 
     353                 :             :     assert(cgen);
     354                 :             :     assert(new_chain);
     355                 :             : 
     356                 :           4 :     old_prog = cgen->program;
     357                 :             : 
     358         [ +  - ]:           4 :     if (bf_opts_persist()) {
     359                 :           4 :         pindir_fd = _bf_cgen_get_chain_pindir_fd((*new_chain)->name);
     360         [ +  - ]:           4 :         if (pindir_fd < 0)
     361                 :             :             return pindir_fd;
     362                 :             :     }
     363                 :             : 
     364                 :           4 :     r = bf_program_new(&new_prog, *new_chain);
     365         [ -  + ]:           4 :     if (r < 0)
     366         [ #  # ]:           0 :         return bf_err_r(r, "failed to create a new bf_program");
     367                 :             : 
     368                 :           4 :     r = bf_program_generate(new_prog);
     369         [ -  + ]:           4 :     if (r < 0) {
     370         [ #  # ]:           0 :         return bf_err_r(r,
     371                 :             :                         "failed to generate the bytecode for a new bf_program");
     372                 :             :     }
     373                 :             : 
     374                 :           4 :     r = bf_program_load(new_prog);
     375         [ -  + ]:           4 :     if (r)
     376         [ #  # ]:           0 :         return bf_err_r(r, "failed to load new program");
     377                 :             : 
     378         [ +  - ]:           4 :     if (bf_opts_persist())
     379                 :           4 :         bf_program_unpin(old_prog, pindir_fd);
     380                 :             : 
     381                 :           4 :     r = bf_link_update(old_prog->link, cgen->chain->hook,
     382                 :           4 :                        new_prog->runtime.prog_fd);
     383         [ -  + ]:           4 :     if (r) {
     384         [ #  # ]:           0 :         bf_err_r(r, "failed to update bf_link object with new program");
     385   [ #  #  #  # ]:           0 :         if (bf_opts_persist() && bf_program_pin(old_prog, pindir_fd) < 0)
     386         [ #  # ]:           0 :             bf_err("failed to repin old program, ignoring");
     387                 :           0 :         return r;
     388                 :             :     }
     389                 :             : 
     390                 :             :     // We updated the old link, we need to store it in the new program
     391                 :           4 :     bf_swap(new_prog->link, old_prog->link);
     392                 :             : 
     393         [ +  - ]:           4 :     if (bf_opts_persist()) {
     394                 :           4 :         r = bf_program_pin(new_prog, pindir_fd);
     395         [ -  + ]:           4 :         if (r)
     396         [ #  # ]:           0 :             bf_warn_r(r, "failed to pin new prog, ignoring");
     397                 :             :     }
     398                 :             : 
     399                 :           4 :     bf_swap(cgen->program, new_prog);
     400                 :             : 
     401                 :           4 :     bf_chain_free(&cgen->chain);
     402                 :           4 :     cgen->chain = TAKE_PTR(*new_chain);
     403                 :             : 
     404                 :           4 :     return 0;
     405                 :             : }
     406                 :             : 
     407                 :           0 : void bf_cgen_detach(struct bf_cgen *cgen)
     408                 :             : {
     409                 :             :     assert(cgen);
     410                 :             : 
     411                 :           0 :     bf_program_detach(cgen->program);
     412                 :           0 : }
     413                 :             : 
     414                 :          38 : void bf_cgen_unload(struct bf_cgen *cgen)
     415                 :             : {
     416                 :          38 :     _cleanup_close_ int chain_fd = -1;
     417                 :             : 
     418                 :             :     assert(cgen);
     419                 :             : 
     420                 :          38 :     chain_fd = _bf_cgen_get_chain_pindir_fd(cgen->chain->name);
     421         [ -  + ]:          38 :     if (chain_fd < 0) {
     422         [ #  # ]:           0 :         bf_err_r(chain_fd, "failed to open pin directory for '%s'",
     423                 :             :                  cgen->chain->name);
     424                 :             :         return;
     425                 :             :     }
     426                 :             : 
     427                 :             :     // The chain's pin directory will be removed in bf_cgen_free()
     428                 :          38 :     bf_program_unpin(cgen->program, chain_fd);
     429                 :          38 :     bf_program_unload(cgen->program);
     430                 :             : }
     431                 :             : 
     432                 :          13 : int bf_cgen_get_counters(const struct bf_cgen *cgen, bf_list *counters)
     433                 :             : {
     434                 :          13 :     bf_list _counters = bf_list_default_from(*counters);
     435                 :             :     int r;
     436                 :             : 
     437                 :             :     assert(cgen);
     438                 :             :     assert(counters);
     439                 :             : 
     440                 :             :     /* Iterate over all the rules, then the policy counter (size(rules)) and
     441                 :             :      * the errors counters (sizeof(rules) + 1)*/
     442         [ +  + ]:          45 :     for (size_t i = 0; i < bf_list_size(&cgen->chain->rules) + 2; ++i) {
     443                 :           0 :         _free_bf_counter_ struct bf_counter *counter = NULL;
     444                 :          32 :         ssize_t idx = (ssize_t)i;
     445                 :             : 
     446         [ +  + ]:          32 :         if (i == bf_list_size(&cgen->chain->rules))
     447                 :             :             idx = BF_COUNTER_POLICY;
     448         [ +  + ]:          19 :         else if (i == bf_list_size(&cgen->chain->rules) + 1)
     449                 :             :             idx = BF_COUNTER_ERRORS;
     450                 :             : 
     451                 :          32 :         r = bf_counter_new(&counter, 0, 0);
     452         [ +  - ]:          32 :         if (r)
     453                 :             :             return r;
     454                 :             : 
     455                 :          32 :         r = bf_cgen_get_counter(cgen, idx, counter);
     456         [ +  - ]:          32 :         if (r)
     457                 :             :             return r;
     458                 :             : 
     459                 :          32 :         r = bf_list_add_tail(&_counters, counter);
     460         [ +  - ]:          32 :         if (r)
     461                 :             :             return r;
     462                 :             : 
     463                 :          32 :         TAKE_PTR(counter);
     464                 :             :     }
     465                 :             : 
     466                 :          13 :     *counters = bf_list_move(_counters);
     467                 :             : 
     468                 :          13 :     return 0;
     469                 :             : }
        

Generated by: LCOV version 2.0-1