• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Alan-Jowett / ebpf-verifier / 18949625295

28 Oct 2025 10:33AM UTC coverage: 87.448% (-1.0%) from 88.47%
18949625295

push

github

elazarg
Bump CLI11 to v2.6.1

Signed-off-by: Elazar Gershuni <elazarg@gmail.com>

9022 of 10317 relevant lines covered (87.45%)

10783407.68 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.61
/src/linux/linux_platform.cpp
1
// Copyright (c) Prevail Verifier contributors.
2
// SPDX-License-Identifier: MIT
3
#include <stdexcept>
4
#if __linux__
5
#include <linux/bpf.h>
6
#define PTYPE(name, descr, native_type, prefixes) {name, descr, native_type, prefixes}
7
#define PTYPE_PRIVILEGED(name, descr, native_type, prefixes) {name, descr, native_type, prefixes, true}
8
#else
9
#define PTYPE(name, descr, native_type, prefixes) {name, descr, 0, prefixes}
10
#define PTYPE_PRIVILEGED(name, descr, native_type, prefixes) {name, descr, 0, prefixes, true}
11
#endif
12
#include "elf_loader.hpp"
13
#include "linux/gpl/spec_type_descriptors.hpp"
14
#include "linux/linux_platform.hpp"
15
#include "platform.hpp"
16
#include "verifier.hpp"
17

18
namespace prevail {
19
// Map definitions as they appear in an ELF file, so field width matters.
20
struct BpfLoadMapDef {
21
    uint32_t type;
22
    uint32_t key_size;
23
    uint32_t value_size;
24
    uint32_t max_entries;
25
    uint32_t map_flags;
26
    uint32_t inner_map_idx;
27
    uint32_t numa_node;
28
};
29

30
static int create_map_linux(uint32_t map_type, uint32_t key_size, uint32_t value_size, uint32_t max_entries,
31
                            ebpf_verifier_options_t options);
32

33
// Allow for comma as a separator between multiple prefixes, to make
34
// the preprocessor treat a prefix list as one macro argument.
35
#define COMMA ,
36

37
const EbpfProgramType linux_socket_filter_program_type =
38
    PTYPE("socket_filter", &g_socket_filter_descr, BPF_PROG_TYPE_SOCKET_FILTER, {"socket"});
39

40
const EbpfProgramType linux_xdp_program_type = PTYPE("xdp", &g_xdp_descr, BPF_PROG_TYPE_XDP, {"xdp"});
41

42
const EbpfProgramType cilium_lxc_program_type = PTYPE("lxc", &g_sched_descr, BPF_PROG_TYPE_SOCKET_FILTER, {});
43

44
const std::vector<EbpfProgramType> linux_program_types = {
45
    PTYPE("unspec", &g_unspec_descr, BPF_PROG_TYPE_UNSPEC, {}),
46
    linux_socket_filter_program_type,
47
    linux_xdp_program_type,
48
    PTYPE("cgroup_device", &g_cgroup_dev_descr, BPF_PROG_TYPE_CGROUP_DEVICE, {"cgroup/dev"}),
49
    PTYPE("cgroup_skb", &g_socket_filter_descr, BPF_PROG_TYPE_CGROUP_SKB, {"cgroup/skb"}),
50
    PTYPE("cgroup_sock", &g_cgroup_sock_descr, BPF_PROG_TYPE_CGROUP_SOCK, {"cgroup/sock"}),
51
    PTYPE_PRIVILEGED("kprobe", &g_kprobe_descr, BPF_PROG_TYPE_KPROBE, {"kprobe/" COMMA "kretprobe/"}),
52
    PTYPE("lwt_in", &g_lwt_inout_descr, BPF_PROG_TYPE_LWT_IN, {"lwt_in"}),
53
    PTYPE("lwt_out", &g_lwt_inout_descr, BPF_PROG_TYPE_LWT_OUT, {"lwt_out"}),
54
    PTYPE("lwt_xmit", &g_lwt_xmit_descr, BPF_PROG_TYPE_LWT_XMIT, {"lwt_xmit"}),
55
    PTYPE("perf_event", &g_perf_event_descr, BPF_PROG_TYPE_PERF_EVENT, {"perf_section" COMMA "perf_event"}),
56
    PTYPE("sched_act", &g_sched_descr, BPF_PROG_TYPE_SCHED_ACT, {"action"}),
57
    PTYPE("sched_cls", &g_sched_descr, BPF_PROG_TYPE_SCHED_CLS, {"classifier"}),
58
    PTYPE("sk_skb", &g_sk_skb_descr, BPF_PROG_TYPE_SK_SKB, {"sk_skb"}),
59
    PTYPE("sock_ops", &g_sock_ops_descr, BPF_PROG_TYPE_SOCK_OPS, {"sockops"}),
60
    PTYPE("tracepoint", &g_tracepoint_descr, BPF_PROG_TYPE_TRACEPOINT, {"tracepoint/"}),
61

62
    // The following types are currently mapped to the socket filter program
63
    // type but should be mapped to the relevant native linux program type
64
    // value.
65
    PTYPE("sk_msg", &g_sk_msg_md, BPF_PROG_TYPE_SOCKET_FILTER, {"sk_msg"}),
66
    PTYPE("raw_tracepoint", &g_tracepoint_descr, BPF_PROG_TYPE_SOCKET_FILTER, {"raw_tracepoint/"}),
67
    PTYPE("cgroup_sock_addr", &g_cgroup_sock_descr, BPF_PROG_TYPE_SOCKET_FILTER, {}),
68
    PTYPE("lwt_seg6local", &g_lwt_xmit_descr, BPF_PROG_TYPE_SOCKET_FILTER, {"lwt_seg6local"}),
69
    PTYPE("lirc_mode2", &g_sk_msg_md, BPF_PROG_TYPE_SOCKET_FILTER, {"lirc_mode2"}),
70
};
71

72
static EbpfProgramType get_program_type_linux(const std::string& section, const std::string& path) {
9,782✔
73
    EbpfProgramType type{};
9,782✔
74

75
    // linux only deduces from section, but cilium and cilium_test have this information
76
    // in the filename:
77
    // * cilium/bpf_xdp.o:from-netdev is XDP
78
    // * bpf_cilium_test/bpf_lb-DLB_L3.o:from-netdev is SK_SKB
79
    if (path.find("cilium") != std::string::npos) {
9,782✔
80
        if (path.find("xdp") != std::string::npos) {
2,198✔
81
            return linux_xdp_program_type;
522✔
82
        }
83
        if (path.find("lxc") != std::string::npos) {
1,676✔
84
            return cilium_lxc_program_type;
936✔
85
        }
86
    }
87

88
    for (const EbpfProgramType& t : linux_program_types) {
147,556✔
89
        for (const std::string& prefix : t.section_prefixes) {
284,000✔
90
            if (section.find(prefix) == 0) {
144,768✔
91
                return t;
6,976✔
92
            }
93
        }
94
    }
95

96
    return linux_socket_filter_program_type;
4,154✔
97
}
9,782✔
98

99
#ifdef __linux__
100
#define BPF_MAP_TYPE(x) BPF_MAP_TYPE_##x, #x
101
#else
102
#define BPF_MAP_TYPE(x) 0, #x
103
#endif
104

105
static const EbpfMapType linux_map_types[] = {
106
    {BPF_MAP_TYPE(UNSPEC)},
107
    {BPF_MAP_TYPE(HASH)},
108
    {BPF_MAP_TYPE(ARRAY), true},
109
    {BPF_MAP_TYPE(PROG_ARRAY), true, EbpfMapValueType::PROGRAM},
110
    {BPF_MAP_TYPE(PERF_EVENT_ARRAY), true},
111
    {BPF_MAP_TYPE(PERCPU_HASH)},
112
    {BPF_MAP_TYPE(PERCPU_ARRAY), true},
113
    {BPF_MAP_TYPE(STACK_TRACE)},
114
    {BPF_MAP_TYPE(CGROUP_ARRAY), true},
115
    {BPF_MAP_TYPE(LRU_HASH)},
116
    {BPF_MAP_TYPE(LRU_PERCPU_HASH)},
117
    {BPF_MAP_TYPE(LPM_TRIE)},
118
    {BPF_MAP_TYPE(ARRAY_OF_MAPS), true, EbpfMapValueType::MAP},
119
    {BPF_MAP_TYPE(HASH_OF_MAPS), false, EbpfMapValueType::MAP},
120
    {BPF_MAP_TYPE(DEVMAP)},
121
    {BPF_MAP_TYPE(SOCKMAP)},
122
    {BPF_MAP_TYPE(CPUMAP)},
123
#ifdef BPF_MAP_TYPE_XSKMAP
124
    {BPF_MAP_TYPE(XSKMAP)},
125
    {BPF_MAP_TYPE(SOCKHASH)},
126
    {BPF_MAP_TYPE(CGROUP_STORAGE)},
127
    {BPF_MAP_TYPE(REUSEPORT_SOCKARRAY)},
128
    {BPF_MAP_TYPE(PERCPU_CGROUP_STORAGE)},
129
    {BPF_MAP_TYPE(QUEUE)},
130
    {BPF_MAP_TYPE(STACK)},
131
#endif
132
};
133

134
EbpfMapType get_map_type_linux(uint32_t platform_specific_type) {
19,562✔
135
    const uint32_t index = platform_specific_type;
19,562✔
136
    if (index == 0 || index >= std::size(linux_map_types)) {
29,192✔
137
        return linux_map_types[0];
310✔
138
    }
139
    EbpfMapType type = linux_map_types[index];
19,252✔
140
#ifdef __linux__
141
    assert(type.platform_specific_type == platform_specific_type);
19,252✔
142
#else
143
    type.platform_specific_type = platform_specific_type;
144
#endif
145
    return type;
19,252✔
146
}
19,252✔
147

148
void parse_maps_section_linux(std::vector<EbpfMapDescriptor>& map_descriptors, const char* data,
566✔
149
                              const size_t map_def_size, const int map_count, const ebpf_platform_t* platform,
150
                              const ebpf_verifier_options_t options) {
151
    // Copy map definitions from the ELF section into a local list.
152
    auto mapdefs = std::vector<BpfLoadMapDef>();
566✔
153
    for (int i = 0; i < map_count; i++) {
6,366✔
154
        BpfLoadMapDef def = {0};
5,800✔
155
        memcpy(&def, data + i * map_def_size, std::min(map_def_size, sizeof(def)));
6,559✔
156
        mapdefs.emplace_back(def);
5,800✔
157
    }
158

159
    // Add map definitions into the map_descriptors list.
160
    for (const auto& s : mapdefs) {
6,366✔
161
        EbpfMapType type = get_map_type_linux(s.type);
5,800✔
162
        map_descriptors.emplace_back(EbpfMapDescriptor{
2,900✔
163
            .original_fd = create_map_linux(s.type, s.key_size, s.value_size, s.max_entries, options),
5,800✔
164
            .type = s.type,
5,800✔
165
            .key_size = s.key_size,
5,800✔
166
            .value_size = s.value_size,
5,800✔
167
            .max_entries = s.max_entries,
5,800✔
168
            .inner_map_fd = gsl::narrow<int32_t>(s.inner_map_idx) // Temporarily fill in the index. This will be
5,800✔
169
                                                                  // replaced in the resolve_inner_map_references pass.
170
        });
171
    }
5,800✔
172
}
566✔
173

174
// Initialize the inner_map_fd in each map descriptor.
175
void resolve_inner_map_references_linux(std::vector<EbpfMapDescriptor>& map_descriptors) {
564✔
176
    for (size_t i = 0; i < map_descriptors.size(); i++) {
6,364✔
177
        const int inner = map_descriptors[i].inner_map_fd; // Get the inner_map_idx back.
5,800✔
178
        if (inner < 0 || inner >= gsl::narrow<int>(map_descriptors.size())) {
5,800✔
179
            throw UnmarshalError("bad inner map index " + std::to_string(inner) + " for map " + std::to_string(i));
×
180
        }
181
        map_descriptors[i].inner_map_fd = map_descriptors.at(inner).original_fd;
5,800✔
182
    }
183
}
564✔
184

185
#if __linux__
186
static int do_bpf(const bpf_cmd cmd, bpf_attr& attr) { return syscall(321, cmd, &attr, sizeof(attr)); }
×
187
#endif
188

189
/** Try to allocate a Linux map.
190
 *
191
 *  This function requires admin privileges.
192
 */
193
static int create_map_linux(const uint32_t map_type, const uint32_t key_size, const uint32_t value_size,
5,800✔
194
                            const uint32_t max_entries, const ebpf_verifier_options_t options) {
195
    if (options.mock_map_fds) {
5,800✔
196
        const EbpfMapType type = get_map_type_linux(map_type);
5,800✔
197
        return create_map_crab(type, key_size, value_size, max_entries, options);
5,800✔
198
    }
5,800✔
199

200
#if __linux__
201
    bpf_attr attr{};
×
202
    memset(&attr, '\0', sizeof(attr));
×
203
    attr.map_type = map_type;
×
204
    attr.key_size = key_size;
×
205
    attr.value_size = value_size;
×
206
    attr.max_entries = 20;
×
207
    attr.map_flags = map_type == BPF_MAP_TYPE_HASH ? BPF_F_NO_PREALLOC : 0;
×
208
    const int map_fd = do_bpf(BPF_MAP_CREATE, attr);
×
209
    if (map_fd < 0) {
×
210
        std::cerr << "Failed to create map, " << strerror(errno) << "\n";
×
211
        std::cerr << "Map: \n"
212
                  << " map_type = " << attr.map_type << "\n"
×
213
                  << " key_size = " << attr.key_size << "\n"
×
214
                  << " value_size = " << attr.value_size << "\n"
×
215
                  << " max_entries = " << attr.max_entries << "\n"
×
216
                  << " map_flags = " << attr.map_flags << "\n";
×
217
        exit(2);
×
218
    }
219
    return map_fd;
220
#else
221
    throw std::runtime_error(std::string("cannot create a Linux map"));
222
#endif
223
}
224

225
EbpfMapDescriptor& get_map_descriptor_linux(const int map_fd) {
18,882✔
226
    // First check if we already have the map descriptor cached.
227
    EbpfMapDescriptor* map = find_map_descriptor(map_fd);
18,882✔
228
    if (map != nullptr) {
18,882✔
229
        return *map;
18,882✔
230
    }
231

232
    // This fd was not created from the maps section of an ELF file,
233
    // but it may be an fd created by an app before calling the verifier.
234
    // In this case, we would like to query the map descriptor info
235
    // (key size, value size) from the execution context, but this is
236
    // not yet supported on Linux.
237

238
    throw UnmarshalError("map_fd " + std::to_string(map_fd) + " not found");
×
239
}
240

241
const ebpf_platform_t g_ebpf_platform_linux = {
242
    get_program_type_linux,
243
    get_helper_prototype_linux,
244
    is_helper_usable_linux,
245
    sizeof(BpfLoadMapDef),
246
    parse_maps_section_linux,
247
    get_map_descriptor_linux,
248
    get_map_type_linux,
249
    resolve_inner_map_references_linux,
250
    bpf_conformance_groups_t::default_groups | bpf_conformance_groups_t::packet,
251
};
252
} // namespace prevail
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc