LCOV - code coverage report
Current view: top level - libbpfilter - btf.c (source / functions) Coverage Total Hit
Test: coverage.lcov Lines: 92.0 % 75 69
Test Date: 2025-11-24 12:34:34 Functions: 100.0 % 8 8
Branches: 65.8 % 76 50

             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 "bpfilter/btf.h"
       7                 :             : 
       8                 :             : #include <linux/btf.h>
       9                 :             : 
      10                 :             : #include <bpf/btf.h>
      11                 :             : #include <errno.h>
      12                 :             : #include <limits.h>
      13                 :             : #include <stddef.h>
      14                 :             : #include <stdlib.h>
      15                 :             : 
      16                 :             : #include "bpfilter/helper.h"
      17                 :             : #include "bpfilter/logger.h"
      18                 :             : 
      19                 :             : static struct btf *_bf_btf = NULL;
      20                 :             : 
      21                 :          26 : int bf_btf_setup(void)
      22                 :             : {
      23                 :          26 :     _bf_btf = btf__load_vmlinux_btf();
      24         [ +  + ]:          26 :     if (!_bf_btf)
      25         [ +  - ]:           1 :         return bf_err_r(errno, "failed to load vmlinux BTF");
      26                 :             : 
      27                 :             :     return 0;
      28                 :             : }
      29                 :             : 
      30                 :          24 : void bf_btf_teardown(void)
      31                 :             : {
      32                 :          24 :     btf__free(_bf_btf);
      33                 :          24 :     _bf_btf = NULL;
      34                 :          24 : }
      35                 :             : 
      36                 :         265 : int bf_btf_get_id(const char *name)
      37                 :             : {
      38                 :             :     int id;
      39                 :             : 
      40                 :             :     bf_assert(name);
      41                 :             : 
      42                 :         265 :     id = btf__find_by_name(_bf_btf, name);
      43         [ +  + ]:         265 :     if (id < 0)
      44         [ +  - ]:           1 :         return bf_err_r(errno, "failed to find BTF type for \"%s\"", name);
      45                 :             : 
      46                 :             :     return id;
      47                 :             : }
      48                 :             : 
      49                 :           3 : const char *bf_btf_get_name(int id)
      50                 :             : {
      51                 :             :     const struct btf_type *type;
      52                 :             : 
      53                 :           3 :     type = btf__type_by_id(_bf_btf, id);
      54         [ +  + ]:           3 :     if (!type) {
      55         [ +  - ]:           2 :         bf_warn("can't find BTF type ID %d", id);
      56                 :           2 :         return NULL;
      57                 :             :     }
      58                 :             : 
      59                 :           1 :     return btf__name_by_offset(_bf_btf, type->name_off);
      60                 :             : }
      61                 :             : 
      62                 :          21 : int bf_btf_kernel_has_token(void)
      63                 :             : {
      64                 :             :     int bpf_attr_id;
      65                 :             :     const struct btf_type *bpf_attr_type;
      66                 :             :     const struct btf_member *bpf_attr_members;
      67                 :             : 
      68                 :          21 :     bpf_attr_id = btf__find_by_name_kind(_bf_btf, "bpf_attr", BTF_KIND_UNION);
      69         [ -  + ]:          21 :     if (bpf_attr_id < 0) {
      70         [ #  # ]:           0 :         return bf_err_r(bpf_attr_id,
      71                 :             :                         "can't find structure 'bpf_attr' in kernel BTF");
      72                 :             :     }
      73                 :             : 
      74                 :          21 :     bpf_attr_type = btf__type_by_id(_bf_btf, bpf_attr_id);
      75         [ -  + ]:          21 :     if (!bpf_attr_type)
      76         [ #  # ]:           0 :         return bf_err_r(-EINVAL, "failed to request 'bpf_attr' BTF type");
      77                 :             : 
      78                 :             :     bpf_attr_members = btf_members(bpf_attr_type);
      79                 :             :     // Iterate through union members
      80         [ +  - ]:          84 :     for (unsigned short i = 0; i < btf_vlen(bpf_attr_type); i++) {
      81                 :             :         const struct btf_type *member_type =
      82                 :          84 :             btf__type_by_id(_bf_btf, bpf_attr_members[i].type);
      83                 :             :         const struct btf_member *m_members = btf_members(member_type);
      84                 :             : 
      85                 :             :         // We are looking for an anonymous structure
      86   [ +  -  +  + ]:          84 :         if (!btf_is_struct(member_type) || bpf_attr_members[i].name_off != 0)
      87                 :          21 :             continue;
      88                 :             : 
      89         [ +  + ]:        1029 :         for (int j = 0; j < btf_vlen(member_type); j++) {
      90                 :             :             const char *member_name =
      91                 :         987 :                 btf__name_by_offset(_bf_btf, m_members[j].name_off);
      92                 :             : 
      93   [ +  -  +  + ]:         987 :             if (member_name && bf_streq(member_name, "prog_token_fd"))
      94                 :             :                 return 0;
      95                 :             :         }
      96                 :             :     }
      97                 :             : 
      98                 :             :     return -ENOENT;
      99                 :             : }
     100                 :             : 
     101                 :             : #define _bf_btf_type_is_compound(type)                                         \
     102                 :             :     (BTF_INFO_KIND((type)->info) == BTF_KIND_UNION ||                          \
     103                 :             :      BTF_INFO_KIND((type)->info) == BTF_KIND_STRUCT)
     104                 :             : 
     105                 :             : /**
     106                 :             :  * @brief Find the offset of `field_name` in BTF type ID `compound_id`.
     107                 :             :  *
     108                 :             :  * @param compound_id Compound BTF type id.
     109                 :             :  * @param field_name Name of the field to find the offset of. Can't be NULL.
     110                 :             :  * @return Offset (in bits) of `field_name` in `compound_id` BTF type.
     111                 :             :  */
     112                 :         379 : static ssize_t _bf_btf_offset_in_compound(uint32_t compound_id,
     113                 :             :                                           const char *field_name)
     114                 :             : {
     115                 :             :     ssize_t offset;
     116                 :             :     const struct btf_type *compound_type, *member_type;
     117                 :             :     const struct btf_member *member;
     118                 :             : 
     119                 :             :     bf_assert(field_name);
     120                 :             : 
     121                 :         379 :     compound_type = btf__type_by_id(_bf_btf, compound_id);
     122         [ -  + ]:         379 :     if (!compound_type)
     123                 :           0 :         return -errno;
     124                 :             : 
     125         [ -  + ]:         379 :     if (!_bf_btf_type_is_compound(compound_type))
     126                 :             :         return -ENOTSUP;
     127                 :             : 
     128                 :         379 :     member = (const struct btf_member *)(compound_type + 1);
     129         [ +  + ]:        2378 :     for (size_t i = 0; i < BTF_INFO_VLEN(compound_type->info); ++i, ++member) {
     130                 :        2158 :         const char *name = btf__name_by_offset(_bf_btf, member->name_off);
     131                 :             : 
     132                 :        2158 :         member_type = btf__type_by_id(_bf_btf, member->type);
     133         [ -  + ]:        2158 :         if (!member_type)
     134                 :           0 :             return -errno;
     135                 :             : 
     136                 :             :         // Member is an anonymous compound type? Check its members
     137   [ +  +  +  - ]:        2158 :         if (*name == '\0' && _bf_btf_type_is_compound(member_type)) {
     138                 :         256 :             offset = _bf_btf_offset_in_compound(member->type, field_name);
     139         [ +  + ]:         256 :             if (offset < 0)
     140                 :         219 :                 continue;
     141                 :             : 
     142         [ -  + ]:          37 :             if (BTF_MEMBER_BIT_OFFSET(member->offset) % 8) {
     143                 :             :                 // Assuming this is not possible, but who knows
     144         [ #  # ]:           0 :                 return bf_err_r(-ENOTSUP,
     145                 :             :                                 "anonymous compound parent is a bitfield");
     146                 :             :             }
     147                 :             : 
     148         [ +  + ]:          37 :             if (BTF_INFO_KFLAG(compound_type->info))
     149                 :          23 :                 offset += BTF_MEMBER_BIT_OFFSET(member->offset);
     150                 :             :             else
     151                 :          14 :                 offset += member->offset;
     152                 :             : 
     153                 :          37 :             return offset;
     154                 :             :         }
     155                 :             : 
     156         [ +  + ]:        1902 :         if (!bf_streq(name, field_name))
     157                 :        1780 :             continue;
     158                 :             : 
     159                 :         122 :         offset = (ssize_t)member->offset;
     160         [ +  + ]:         122 :         if (BTF_INFO_KFLAG(compound_type->info))
     161                 :          28 :             offset = BTF_MEMBER_BIT_OFFSET(member->offset);
     162                 :             : 
     163                 :             :         return offset;
     164                 :             :     }
     165                 :             : 
     166                 :             :     return -EINVAL;
     167                 :             : }
     168                 :             : 
     169                 :         124 : static int _bf_btf_get_compound_type_id(const char *name)
     170                 :             : {
     171                 :             :     int compound_id;
     172                 :             : 
     173                 :         124 :     compound_id = btf__find_by_name_kind(_bf_btf, name, BTF_KIND_STRUCT);
     174         [ +  - ]:         124 :     if (compound_id < 0 && compound_id != -ENOENT)
     175                 :             :         return compound_id;
     176         [ +  + ]:         124 :     if (compound_id >= 0)
     177                 :             :         return compound_id;
     178                 :             : 
     179                 :           1 :     compound_id = btf__find_by_name_kind(_bf_btf, name, BTF_KIND_UNION);
     180                 :             :     if (compound_id < 0 && compound_id != -ENOENT)
     181                 :             :         return compound_id;
     182                 :             : 
     183                 :             :     return compound_id;
     184                 :             : }
     185                 :             : 
     186                 :         124 : int bf_btf_get_field_off(const char *struct_name, const char *field_name)
     187                 :             : {
     188                 :             :     int id;
     189                 :             :     ssize_t offset;
     190                 :             : 
     191                 :             :     bf_assert(struct_name);
     192                 :             :     bf_assert(field_name);
     193                 :             : 
     194                 :         124 :     id = _bf_btf_get_compound_type_id(struct_name);
     195         [ +  + ]:         124 :     if (id < 0)
     196         [ +  - ]:           1 :         return bf_err_r(id, "failed to find BTF type ID for '%s'", struct_name);
     197                 :             : 
     198                 :         123 :     offset = _bf_btf_offset_in_compound(id, field_name);
     199         [ +  + ]:         123 :     if (offset < 0) {
     200         [ +  - ]:           1 :         return bf_err_r((int)offset, "failed to find offset of %s.%s",
     201                 :             :                         struct_name, field_name);
     202                 :             :     }
     203         [ +  + ]:         122 :     if (offset % 8) {
     204         [ +  - ]:           1 :         return bf_err_r(-EINVAL, "%s.%s has a bit offset", struct_name,
     205                 :             :                         field_name);
     206                 :             :     }
     207         [ -  + ]:         121 :     if (offset / 8 > INT_MAX) {
     208         [ #  # ]:           0 :         return bf_err_r(-E2BIG, "%s.%s has an offset bigger than %d",
     209                 :             :                         struct_name, field_name, INT_MAX);
     210                 :             :     }
     211                 :             : 
     212                 :         121 :     return (int)(offset / 8);
     213                 :             : }
        

Generated by: LCOV version 2.0-1