LCOV - code coverage report
Current view: top level - libbpfilter - btf.c (source / functions) Coverage Total Hit
Test: coverage.lcov Lines: 91.8 % 97 89
Test Date: 2026-11-05 13:56:16 Functions: 100.0 % 9 9
Branches: 66.0 % 106 70

             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                 :             : #include <string.h>
      16                 :             : 
      17                 :             : #include "bpfilter/core/vector.h"
      18                 :             : #include "bpfilter/helper.h"
      19                 :             : #include "bpfilter/logger.h"
      20                 :             : 
      21                 :             : struct bf_btf_cache_entry
      22                 :             : {
      23                 :             :     char *name;
      24                 :             :     int id;
      25                 :             : };
      26                 :             : 
      27                 :             : static struct btf *_bf_btf = NULL;
      28                 :             : static bf_vector _bf_btf_cache;
      29                 :             : 
      30                 :         440 : int bf_btf_setup(void)
      31                 :             : {
      32         [ +  + ]:         440 :     if (_bf_btf)
      33         [ +  - ]:           1 :         return bf_err_r(-EBUSY, "kernel BTF is already initialized");
      34                 :             : 
      35                 :         439 :     _bf_btf = btf__load_vmlinux_btf();
      36         [ +  + ]:         439 :     if (!_bf_btf)
      37         [ +  - ]:           1 :         return bf_err_r(errno, "failed to load vmlinux BTF");
      38                 :             : 
      39                 :         438 :     bf_vector_init(&_bf_btf_cache, sizeof(struct bf_btf_cache_entry));
      40                 :             : 
      41                 :         438 :     return 0;
      42                 :             : }
      43                 :             : 
      44                 :         438 : void bf_btf_teardown(void)
      45                 :             : {
      46   [ +  +  +  + ]:        1515 :     bf_vector_foreach (&_bf_btf_cache, elem) {
      47                 :             :         struct bf_btf_cache_entry *entry = elem;
      48                 :             : 
      49                 :         639 :         BF_FREEP(&entry->name);
      50                 :             :     }
      51                 :         438 :     bf_vector_clean(&_bf_btf_cache);
      52                 :             : 
      53                 :         438 :     btf__free(_bf_btf);
      54                 :         438 :     _bf_btf = NULL;
      55                 :         438 : }
      56                 :             : 
      57                 :        5516 : int bf_btf_get_id(const char *name)
      58                 :             : {
      59                 :             :     _cleanup_free_ char *owned_name = NULL;
      60                 :             :     struct bf_btf_cache_entry entry;
      61                 :             :     int id;
      62                 :             : 
      63                 :             :     assert(name);
      64                 :             : 
      65   [ +  +  +  + ]:       13484 :     bf_vector_foreach (&_bf_btf_cache, elem) {
      66                 :             :         const struct bf_btf_cache_entry *cached = elem;
      67                 :             : 
      68         [ +  + ]:        7327 :         if (bf_streq(cached->name, name))
      69                 :        4875 :             return cached->id;
      70                 :             :     }
      71                 :             : 
      72                 :         641 :     id = btf__find_by_name(_bf_btf, name);
      73         [ +  + ]:         641 :     if (id < 0)
      74         [ +  - ]:           2 :         return bf_err_r(id, "failed to find BTF type for \"%s\"", name);
      75                 :             : 
      76                 :             :     // Cache allocation failures must not prevent successful lookups.
      77                 :         639 :     owned_name = strdup(name);
      78         [ -  + ]:         639 :     if (!owned_name)
      79                 :             :         return id;
      80                 :             : 
      81                 :         639 :     entry = (struct bf_btf_cache_entry) {.name = owned_name, .id = id};
      82         [ +  - ]:         639 :     if (!bf_vector_add(&_bf_btf_cache, &entry))
      83                 :             :         TAKE_PTR(owned_name);
      84                 :             : 
      85                 :             :     return id;
      86                 :             : }
      87                 :             : 
      88                 :           6 : const char *bf_btf_get_name(int id)
      89                 :             : {
      90                 :             :     const struct btf_type *type;
      91                 :             : 
      92                 :           6 :     type = btf__type_by_id(_bf_btf, id);
      93         [ +  + ]:           6 :     if (!type) {
      94         [ +  - ]:           2 :         bf_warn("can't find BTF type ID %d", id);
      95                 :             :         return NULL;
      96                 :             :     }
      97                 :             : 
      98                 :           4 :     return btf__name_by_offset(_bf_btf, type->name_off);
      99                 :             : }
     100                 :             : 
     101                 :           1 : int bf_btf_kernel_has_token(void)
     102                 :             : {
     103                 :             :     int bpf_attr_id;
     104                 :             :     const struct btf_type *bpf_attr_type;
     105                 :             :     const struct btf_member *bpf_attr_members;
     106                 :             : 
     107                 :           1 :     bpf_attr_id = btf__find_by_name_kind(_bf_btf, "bpf_attr", BTF_KIND_UNION);
     108         [ -  + ]:           1 :     if (bpf_attr_id < 0) {
     109         [ #  # ]:           0 :         return bf_err_r(bpf_attr_id,
     110                 :             :                         "can't find structure 'bpf_attr' in kernel BTF");
     111                 :             :     }
     112                 :             : 
     113                 :           1 :     bpf_attr_type = btf__type_by_id(_bf_btf, bpf_attr_id);
     114         [ -  + ]:           1 :     if (!bpf_attr_type)
     115         [ #  # ]:           0 :         return bf_err_r(-EINVAL, "failed to request 'bpf_attr' BTF type");
     116                 :             : 
     117                 :             :     bpf_attr_members = btf_members(bpf_attr_type);
     118                 :             :     // Iterate through union members
     119         [ +  - ]:           4 :     for (unsigned short i = 0; i < btf_vlen(bpf_attr_type); i++) {
     120                 :             :         const struct btf_type *member_type =
     121                 :           4 :             btf__type_by_id(_bf_btf, bpf_attr_members[i].type);
     122                 :             :         const struct btf_member *m_members = btf_members(member_type);
     123                 :             : 
     124                 :             :         // We are looking for an anonymous structure
     125   [ +  -  +  + ]:           4 :         if (!btf_is_struct(member_type) || bpf_attr_members[i].name_off != 0)
     126                 :           1 :             continue;
     127                 :             : 
     128         [ +  + ]:          51 :         for (int j = 0; j < btf_vlen(member_type); j++) {
     129                 :             :             const char *member_name =
     130                 :          49 :                 btf__name_by_offset(_bf_btf, m_members[j].name_off);
     131                 :             : 
     132   [ +  -  +  + ]:          49 :             if (member_name && bf_streq(member_name, "prog_token_fd"))
     133                 :             :                 return 0;
     134                 :             :         }
     135                 :             :     }
     136                 :             : 
     137                 :             :     return -ENOENT;
     138                 :             : }
     139                 :             : 
     140                 :             : #define _bf_btf_type_is_compound(type)                                         \
     141                 :             :     (BTF_INFO_KIND((type)->info) == BTF_KIND_UNION ||                          \
     142                 :             :      BTF_INFO_KIND((type)->info) == BTF_KIND_STRUCT)
     143                 :             : 
     144                 :             : /**
     145                 :             :  * @brief Find the offset of `field_name` in BTF type ID `compound_id`.
     146                 :             :  *
     147                 :             :  * @param compound_id Compound BTF type id.
     148                 :             :  * @param field_name Name of the field to find the offset of. Can't be NULL.
     149                 :             :  * @return Offset (in bits) of `field_name` in `compound_id` BTF type.
     150                 :             :  */
     151                 :       10613 : static ssize_t _bf_btf_offset_in_compound(uint32_t compound_id,
     152                 :             :                                           const char *field_name)
     153                 :             : {
     154                 :             :     ssize_t offset;
     155                 :             :     const struct btf_type *compound_type, *member_type;
     156                 :             :     const struct btf_member *member;
     157                 :             : 
     158                 :             :     assert(field_name);
     159                 :             : 
     160                 :       10613 :     compound_type = btf__type_by_id(_bf_btf, compound_id);
     161         [ -  + ]:       10613 :     if (!compound_type)
     162                 :           0 :         return -errno;
     163                 :             : 
     164         [ +  - ]:       10613 :     if (!_bf_btf_type_is_compound(compound_type))
     165                 :             :         return -ENOTSUP;
     166                 :             : 
     167                 :       10613 :     member = (const struct btf_member *)(compound_type + 1);
     168         [ +  + ]:       60643 :     for (size_t i = 0; i < BTF_INFO_VLEN(compound_type->info); ++i, ++member) {
     169                 :       54728 :         const char *name = btf__name_by_offset(_bf_btf, member->name_off);
     170                 :             : 
     171                 :       54728 :         member_type = btf__type_by_id(_bf_btf, member->type);
     172         [ -  + ]:       54728 :         if (!member_type)
     173                 :           0 :             return -errno;
     174                 :             : 
     175                 :             :         // Member is an anonymous compound type? Check its members
     176   [ +  +  +  - ]:       54728 :         if (*name == '\0' && _bf_btf_type_is_compound(member_type)) {
     177                 :        6011 :             offset = _bf_btf_offset_in_compound(member->type, field_name);
     178         [ +  + ]:        6011 :             if (offset < 0)
     179                 :        5914 :                 continue;
     180                 :             : 
     181         [ -  + ]:          97 :             if (BTF_MEMBER_BIT_OFFSET(member->offset) % 8) {
     182                 :             :                 // Assuming this is not possible, but who knows
     183         [ #  # ]:           0 :                 return bf_err_r(-ENOTSUP,
     184                 :             :                                 "anonymous compound parent is a bitfield");
     185                 :             :             }
     186                 :             : 
     187         [ +  + ]:          97 :             if (BTF_INFO_KFLAG(compound_type->info))
     188                 :          63 :                 offset += BTF_MEMBER_BIT_OFFSET(member->offset);
     189                 :             :             else
     190                 :          34 :                 offset += member->offset;
     191                 :             : 
     192                 :             :             return offset;
     193                 :             :         }
     194                 :             : 
     195         [ +  + ]:       48717 :         if (!bf_streq(name, field_name))
     196                 :       44116 :             continue;
     197                 :             : 
     198                 :        4601 :         offset = (ssize_t)member->offset;
     199         [ +  + ]:        4601 :         if (BTF_INFO_KFLAG(compound_type->info))
     200                 :        1246 :             offset = BTF_MEMBER_BIT_OFFSET(member->offset);
     201                 :             : 
     202                 :             :         return offset;
     203                 :             :     }
     204                 :             : 
     205                 :             :     return -EINVAL;
     206                 :             : }
     207                 :             : 
     208                 :        4603 : static int _bf_btf_get_compound_type_id(const char *name)
     209                 :             : {
     210                 :             :     int compound_id;
     211                 :             : 
     212                 :        4603 :     compound_id = btf__find_by_name_kind(_bf_btf, name, BTF_KIND_STRUCT);
     213         [ +  - ]:        4603 :     if (compound_id < 0 && compound_id != -ENOENT)
     214                 :             :         return compound_id;
     215         [ +  + ]:        4603 :     if (compound_id >= 0)
     216                 :             :         return compound_id;
     217                 :             : 
     218                 :           1 :     compound_id = btf__find_by_name_kind(_bf_btf, name, BTF_KIND_UNION);
     219                 :             :     if (compound_id < 0 && compound_id != -ENOENT)
     220                 :             :         return compound_id;
     221                 :             : 
     222                 :             :     return compound_id;
     223                 :             : }
     224                 :             : 
     225                 :        4603 : int bf_btf_get_field_off(const char *struct_name, const char *field_name)
     226                 :             : {
     227                 :             :     int id;
     228                 :             :     ssize_t offset;
     229                 :             : 
     230                 :             :     assert(struct_name);
     231                 :             :     assert(field_name);
     232                 :             : 
     233                 :        4603 :     id = _bf_btf_get_compound_type_id(struct_name);
     234         [ +  + ]:        4603 :     if (id < 0)
     235         [ +  - ]:           1 :         return bf_err_r(id, "failed to find BTF type ID for '%s'", struct_name);
     236                 :             : 
     237                 :        4602 :     offset = _bf_btf_offset_in_compound(id, field_name);
     238         [ +  + ]:        4602 :     if (offset < 0) {
     239         [ +  - ]:           1 :         return bf_err_r((int)offset, "failed to find offset of %s.%s",
     240                 :             :                         struct_name, field_name);
     241                 :             :     }
     242         [ +  + ]:        4601 :     if (offset % 8) {
     243         [ +  - ]:           1 :         return bf_err_r(-EINVAL, "%s.%s has a bit offset", struct_name,
     244                 :             :                         field_name);
     245                 :             :     }
     246         [ -  + ]:        4600 :     if (offset / 8 > INT_MAX) {
     247         [ #  # ]:           0 :         return bf_err_r(-E2BIG, "%s.%s has an offset bigger than %d",
     248                 :             :                         struct_name, field_name, INT_MAX);
     249                 :             :     }
     250                 :             : 
     251                 :        4600 :     return (int)(offset / 8);
     252                 :             : }
     253                 :             : 
     254                 :           9 : int bf_btf_get_type_size(const char *name)
     255                 :             : {
     256                 :             :     int id;
     257                 :             :     __s64 size;
     258                 :             : 
     259                 :             :     assert(name);
     260                 :             : 
     261                 :           9 :     id = btf__find_by_name_kind(_bf_btf, name, BTF_KIND_STRUCT);
     262         [ +  + ]:           9 :     if (id < 0)
     263         [ +  - ]:           3 :         return bf_err_r(id, "failed to find BTF structure '%s'", name);
     264                 :             : 
     265                 :           6 :     size = btf__resolve_size(_bf_btf, id);
     266         [ -  + ]:           6 :     if (size < 0)
     267         [ #  # ]:           0 :         return bf_err_r((int)size, "failed to resolve the size of '%s'", name);
     268         [ -  + ]:           6 :     if (size > INT_MAX)
     269         [ #  # ]:           0 :         return bf_err_r(-E2BIG, "'%s' is bigger than %d bytes", name, INT_MAX);
     270                 :             : 
     271                 :           6 :     return (int)size;
     272                 :             : }
        

Generated by: LCOV version 2.0-1