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

Alan-Jowett / ebpf-verifier / 27778108035

07 Jun 2026 06:51PM UTC coverage: 86.386% (-2.5%) from 88.93%
27778108035

push

github

elazarg
Release v0.2.5

Bump project version to 0.2.5 and add a CHANGELOG entry covering ELF loader hardening, numeric-domain soundness fixes, and the writable helper output initialization documentation update since v0.2.4. Also updates the using_installed_package example version requirement.

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

9125 of 10563 relevant lines covered (86.39%)

6334294.72 hits per line

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

85.14
/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

7
#ifndef BPF_PROG_TYPE_CGROUP_SYSCTL
8
#define BPF_PROG_TYPE_CGROUP_SYSCTL 23
9
#endif
10
#ifndef BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE
11
#define BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE 24
12
#endif
13
#ifndef BPF_PROG_TYPE_CGROUP_SOCKOPT
14
#define BPF_PROG_TYPE_CGROUP_SOCKOPT 25
15
#endif
16
#ifndef BPF_PROG_TYPE_TRACING
17
#define BPF_PROG_TYPE_TRACING 26
18
#endif
19
#ifndef BPF_PROG_TYPE_STRUCT_OPS
20
#define BPF_PROG_TYPE_STRUCT_OPS 27
21
#endif
22
#ifndef BPF_PROG_TYPE_EXT
23
#define BPF_PROG_TYPE_EXT 28
24
#endif
25
#ifndef BPF_PROG_TYPE_LSM
26
#define BPF_PROG_TYPE_LSM 29
27
#endif
28
#ifndef BPF_PROG_TYPE_SK_LOOKUP
29
#define BPF_PROG_TYPE_SK_LOOKUP 30
30
#endif
31
#ifndef BPF_PROG_TYPE_SYSCALL
32
#define BPF_PROG_TYPE_SYSCALL 31
33
#endif
34
#ifndef BPF_PROG_TYPE_NETFILTER
35
#define BPF_PROG_TYPE_NETFILTER 32
36
#endif
37

38
#ifndef BPF_MAP_TYPE_XSKMAP
39
#define BPF_MAP_TYPE_XSKMAP 17
40
#endif
41
#ifndef BPF_MAP_TYPE_SOCKHASH
42
#define BPF_MAP_TYPE_SOCKHASH 18
43
#endif
44
#ifndef BPF_MAP_TYPE_CGROUP_STORAGE
45
#define BPF_MAP_TYPE_CGROUP_STORAGE 19
46
#endif
47
#ifndef BPF_MAP_TYPE_REUSEPORT_SOCKARRAY
48
#define BPF_MAP_TYPE_REUSEPORT_SOCKARRAY 20
49
#endif
50
#ifndef BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE
51
#define BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE 21
52
#endif
53
#ifndef BPF_MAP_TYPE_QUEUE
54
#define BPF_MAP_TYPE_QUEUE 22
55
#endif
56
#ifndef BPF_MAP_TYPE_STACK
57
#define BPF_MAP_TYPE_STACK 23
58
#endif
59
#ifndef BPF_MAP_TYPE_SK_STORAGE
60
#define BPF_MAP_TYPE_SK_STORAGE 24
61
#endif
62
#ifndef BPF_MAP_TYPE_DEVMAP_HASH
63
#define BPF_MAP_TYPE_DEVMAP_HASH 25
64
#endif
65
#ifndef BPF_MAP_TYPE_STRUCT_OPS
66
#define BPF_MAP_TYPE_STRUCT_OPS 26
67
#endif
68
#ifndef BPF_MAP_TYPE_RINGBUF
69
#define BPF_MAP_TYPE_RINGBUF 27
70
#endif
71
#ifndef BPF_MAP_TYPE_INODE_STORAGE
72
#define BPF_MAP_TYPE_INODE_STORAGE 28
73
#endif
74
#ifndef BPF_MAP_TYPE_TASK_STORAGE
75
#define BPF_MAP_TYPE_TASK_STORAGE 29
76
#endif
77
#ifndef BPF_MAP_TYPE_BLOOM_FILTER
78
#define BPF_MAP_TYPE_BLOOM_FILTER 30
79
#endif
80
#ifndef BPF_MAP_TYPE_USER_RINGBUF
81
#define BPF_MAP_TYPE_USER_RINGBUF 31
82
#endif
83
#ifndef BPF_MAP_TYPE_CGRP_STORAGE
84
#define BPF_MAP_TYPE_CGRP_STORAGE 32
85
#endif
86
#ifndef BPF_MAP_TYPE_ARENA
87
#define BPF_MAP_TYPE_ARENA 33
88
#endif
89

90
#define PTYPE(name, descr, native_type, prefixes) {name, descr, native_type, prefixes}
91
#define PTYPE_PRIVILEGED(name, descr, native_type, prefixes) {name, descr, native_type, prefixes, true}
92
#else
93
#define PTYPE(name, descr, native_type, prefixes) {name, descr, 0, prefixes}
94
#define PTYPE_PRIVILEGED(name, descr, native_type, prefixes) {name, descr, 0, prefixes, true}
95
#endif
96
#include "io/elf_loader.hpp"
97
#include "linux/gpl/spec_type_descriptors.hpp"
98
#include "linux/kfunc.hpp"
99
#include "linux/linux_platform.hpp"
100
#include "platform.hpp"
101
#include "verifier.hpp"
102

103
namespace prevail {
104
// Map definitions as they appear in an ELF file, so field width matters.
105
struct BpfLoadMapDef {
106
    uint32_t type;
107
    uint32_t key_size;
108
    uint32_t value_size;
109
    uint32_t max_entries;
110
    uint32_t map_flags;
111
    uint32_t inner_map_idx;
112
    uint32_t numa_node;
113
};
114

115
static int create_map_linux(uint32_t map_type, uint32_t key_size, uint32_t value_size, uint32_t max_entries);
116

117
// Allow for comma as a separator between multiple prefixes, to make
118
// the preprocessor treat a prefix list as one macro argument.
119
#define COMMA ,
120

121
const EbpfProgramType linux_socket_filter_program_type =
122
    PTYPE("socket_filter", &g_socket_filter_descr, BPF_PROG_TYPE_SOCKET_FILTER, {"socket"});
123

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

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

128
const std::vector<EbpfProgramType> linux_program_types = {
129
    PTYPE("unspec", &g_unspec_descr, BPF_PROG_TYPE_UNSPEC, {}),
130
    linux_socket_filter_program_type,
131
    linux_xdp_program_type,
132
    PTYPE("cgroup_device", &g_cgroup_dev_descr, BPF_PROG_TYPE_CGROUP_DEVICE, {"cgroup/dev"}),
133
    PTYPE("cgroup_skb", &g_socket_filter_descr, BPF_PROG_TYPE_CGROUP_SKB, {"cgroup/skb"}),
134
    PTYPE("cgroup_sock", &g_cgroup_sock_descr, BPF_PROG_TYPE_CGROUP_SOCK, {"cgroup/sock"}),
135
    PTYPE_PRIVILEGED("kprobe", &g_kprobe_descr, BPF_PROG_TYPE_KPROBE, {"kprobe/" COMMA "kretprobe/"}),
136
    PTYPE("lwt_in", &g_lwt_inout_descr, BPF_PROG_TYPE_LWT_IN, {"lwt_in"}),
137
    PTYPE("lwt_out", &g_lwt_inout_descr, BPF_PROG_TYPE_LWT_OUT, {"lwt_out"}),
138
    PTYPE("lwt_xmit", &g_lwt_xmit_descr, BPF_PROG_TYPE_LWT_XMIT, {"lwt_xmit"}),
139
    PTYPE("perf_event", &g_perf_event_descr, BPF_PROG_TYPE_PERF_EVENT, {"perf_section" COMMA "perf_event"}),
140
    PTYPE("sched_act", &g_sched_descr, BPF_PROG_TYPE_SCHED_ACT, {"action"}),
141
    PTYPE("sched_cls", &g_sched_descr, BPF_PROG_TYPE_SCHED_CLS, {"classifier"}),
142
    PTYPE("sk_skb", &g_sk_skb_descr, BPF_PROG_TYPE_SK_SKB, {"sk_skb"}),
143
    PTYPE("sock_ops", &g_sock_ops_descr, BPF_PROG_TYPE_SOCK_OPS, {"sockops"}),
144
    PTYPE("tracepoint", &g_tracepoint_descr, BPF_PROG_TYPE_TRACEPOINT, {"tracepoint/"}),
145
    PTYPE("cgroup_sockopt", &g_sockopt_descr, BPF_PROG_TYPE_CGROUP_SOCKOPT,
146
          {"cgroup/getsockopt" COMMA "cgroup/setsockopt"}),
147
    PTYPE("sk_msg", &g_sk_msg_md, BPF_PROG_TYPE_SK_MSG, {"sk_msg"}),
148
    PTYPE_PRIVILEGED("raw_tracepoint", &g_tracepoint_descr, BPF_PROG_TYPE_RAW_TRACEPOINT,
149
                     {"raw_tracepoint/" COMMA "raw_tp/"}),
150
    PTYPE_PRIVILEGED("raw_tracepoint_writable", &g_tracepoint_descr, BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
151
                     {"raw_tracepoint.w/" COMMA "raw_tp.w/"}),
152
    PTYPE("cgroup_sock_addr", &g_sock_addr_descr, BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
153
          {"cgroup/bind" COMMA "cgroup/post_bind" COMMA "cgroup/connect" COMMA "cgroup/sendmsg" COMMA
154
           "cgroup/recvmsg" COMMA "cgroup/getpeername" COMMA "cgroup/getsockname"}),
155
    PTYPE("lwt_seg6local", &g_lwt_xmit_descr, BPF_PROG_TYPE_LWT_SEG6LOCAL, {"lwt_seg6local"}),
156
    PTYPE("lirc_mode2", &g_lirc_mode2_descr, BPF_PROG_TYPE_LIRC_MODE2, {"lirc_mode2"}),
157
    PTYPE("sk_reuseport", &g_sk_reuseport_descr, BPF_PROG_TYPE_SK_REUSEPORT, {"sk_reuseport/"}),
158
    PTYPE("flow_dissector", &g_flow_dissector_descr, BPF_PROG_TYPE_FLOW_DISSECTOR, {"flow_dissector"}),
159
    PTYPE("cgroup_sysctl", &g_cgroup_sysctl_descr, BPF_PROG_TYPE_CGROUP_SYSCTL, {"cgroup/sysctl"}),
160
    PTYPE("ext", &g_unspec_descr, BPF_PROG_TYPE_EXT, {"freplace/"}),
161
    PTYPE("tracing", &g_tracing_descr, BPF_PROG_TYPE_TRACING,
162
          {"fentry/" COMMA "fentry.s/" COMMA "fexit/" COMMA "fexit.s/" COMMA "fmod_ret/" COMMA "fmod_ret.s/" COMMA
163
           "iter/" COMMA "iter.s/" COMMA "tp_btf/" COMMA "tp_btf.s/"}),
164
    // struct_ops callbacks receive function arguments as u64 array, same as fentry/fexit.
165
    PTYPE("struct_ops", &g_tracing_descr, BPF_PROG_TYPE_STRUCT_OPS, {"struct_ops/" COMMA "struct_ops.s/"}),
166
    PTYPE("lsm", &g_tracing_descr, BPF_PROG_TYPE_LSM, {"lsm/" COMMA "lsm.s/"}),
167
    PTYPE("sk_lookup", &g_sk_lookup_descr, BPF_PROG_TYPE_SK_LOOKUP, {"sk_lookup/"}),
168
    PTYPE("syscall", &g_syscall_descr, BPF_PROG_TYPE_SYSCALL, {"syscall/"}),
169
    PTYPE("netfilter", &g_netfilter_descr, BPF_PROG_TYPE_NETFILTER, {"netfilter/"}),
170
};
171

172
static EbpfProgramType get_program_type_linux(const std::string& section, const std::string& path) {
32,416✔
173
    EbpfProgramType type{};
32,416✔
174

175
    // linux only deduces from section, but cilium and cilium_test have this information
176
    // in the filename:
177
    // * cilium/bpf_xdp.o:from-netdev is XDP
178
    // * bpf_cilium_test/bpf_lb-DLB_L3.o:from-netdev is SK_SKB
179
    if (path.find("cilium") != std::string::npos) {
32,416✔
180
        if (path.find("xdp") != std::string::npos) {
5,708✔
181
            return linux_xdp_program_type;
1,286✔
182
        }
183
        if (path.find("lxc") != std::string::npos) {
4,422✔
184
            return cilium_lxc_program_type;
904✔
185
        }
186
    }
187

188
    for (const EbpfProgramType& t : linux_program_types) {
630,326✔
189
        for (const std::string& prefix : t.section_prefixes) {
1,412,494✔
190
            if (section.find(prefix) == 0) {
812,394✔
191
                EbpfProgramType result = t;
22,882✔
192
                result.is_sleepable = prefix.find(".s/") != std::string::npos || result.name == "syscall";
22,882✔
193
                return result;
22,882✔
194
            }
22,882✔
195
        }
196
    }
197

198
    return linux_socket_filter_program_type;
7,344✔
199
}
32,416✔
200

201
#ifdef __linux__
202
#define BPF_MAP_TYPE(x) BPF_MAP_TYPE_##x, #x
203
#else
204
#define BPF_MAP_TYPE(x) 0, #x
205
#endif
206

207
static const EbpfMapType linux_map_types[] = {
208
    {BPF_MAP_TYPE(UNSPEC)},
209
    {BPF_MAP_TYPE(HASH)},
210
    {BPF_MAP_TYPE(ARRAY), true},
211
    {BPF_MAP_TYPE(PROG_ARRAY), true, EbpfMapValueType::PROGRAM},
212
    {BPF_MAP_TYPE(PERF_EVENT_ARRAY), true},
213
    {BPF_MAP_TYPE(PERCPU_HASH)},
214
    {BPF_MAP_TYPE(PERCPU_ARRAY), true},
215
    {BPF_MAP_TYPE(STACK_TRACE)},
216
    {BPF_MAP_TYPE(CGROUP_ARRAY), true},
217
    {BPF_MAP_TYPE(LRU_HASH)},
218
    {BPF_MAP_TYPE(LRU_PERCPU_HASH)},
219
    {BPF_MAP_TYPE(LPM_TRIE)},
220
    {BPF_MAP_TYPE(ARRAY_OF_MAPS), true, EbpfMapValueType::MAP},
221
    {BPF_MAP_TYPE(HASH_OF_MAPS), false, EbpfMapValueType::MAP},
222
    {BPF_MAP_TYPE(DEVMAP)},
223
    {BPF_MAP_TYPE(SOCKMAP)},
224
    {BPF_MAP_TYPE(CPUMAP)},
225
    {BPF_MAP_TYPE(XSKMAP)},
226
    {BPF_MAP_TYPE(SOCKHASH)},
227
    {BPF_MAP_TYPE(CGROUP_STORAGE)},
228
    {BPF_MAP_TYPE(REUSEPORT_SOCKARRAY)},
229
    {BPF_MAP_TYPE(PERCPU_CGROUP_STORAGE)},
230
    {BPF_MAP_TYPE(QUEUE)},
231
    {BPF_MAP_TYPE(STACK)},
232
    {BPF_MAP_TYPE(SK_STORAGE)},
233
    {BPF_MAP_TYPE(DEVMAP_HASH)},
234
    {BPF_MAP_TYPE(STRUCT_OPS)},
235
    {BPF_MAP_TYPE(RINGBUF)},
236
    {BPF_MAP_TYPE(INODE_STORAGE)},
237
    {BPF_MAP_TYPE(TASK_STORAGE)},
238
    {BPF_MAP_TYPE(BLOOM_FILTER)},
239
    {BPF_MAP_TYPE(USER_RINGBUF)},
240
    {BPF_MAP_TYPE(CGRP_STORAGE)},
241
    {BPF_MAP_TYPE(ARENA)},
242
};
243

244
EbpfMapType get_map_type_linux(const uint32_t platform_specific_type) {
119,518✔
245
    const uint32_t index = platform_specific_type;
119,518✔
246
    if (index == 0 || index >= std::size(linux_map_types)) {
177,887✔
247
        return linux_map_types[0];
2,780✔
248
    }
249
    EbpfMapType type = linux_map_types[index];
116,738✔
250
#ifdef __linux__
251
    assert(type.platform_specific_type == platform_specific_type);
116,738✔
252
#else
253
    type.platform_specific_type = platform_specific_type;
254
#endif
255
    return type;
116,738✔
256
}
116,738✔
257

258
// Assign a synthetic mock FD for a map, deduplicating against already-parsed descriptors.
259
// Pure function of the accumulated descriptor table — no external state needed.
260
static int allocate_mock_map_fd(const std::vector<EbpfMapDescriptor>& map_descriptors, const EbpfMapType& map_type,
9,754✔
261
                                const uint32_t key_size, const uint32_t value_size, const uint32_t max_entries) {
262
    const EquivalenceKey equiv{map_type.value_type, key_size, value_size, map_type.is_array ? max_entries : 0};
9,754✔
263
    for (const auto& desc : map_descriptors) {
94,784✔
264
        const EbpfMapType existing_type = get_map_type_linux(desc.type);
86,538✔
265
        const EquivalenceKey existing{existing_type.value_type, desc.key_size, desc.value_size,
86,538✔
266
                                      existing_type.is_array ? desc.max_entries : 0};
86,538✔
267
        if (existing == equiv) {
86,538✔
268
            return desc.original_fd;
1,508✔
269
        }
270
    }
86,538✔
271
    // +1 so 0 is the null FD
272
    return gsl::narrow<int>(map_descriptors.size()) + 1;
8,246✔
273
}
274

275
void parse_maps_section_linux(std::vector<EbpfMapDescriptor>& map_descriptors, const char* data,
906✔
276
                              const size_t map_def_size, const int map_count, const ebpf_platform_t* platform,
277
                              const VerifierOptions& options) {
278
    auto mapdefs = std::vector<BpfLoadMapDef>();
906✔
279
    for (int i = 0; i < map_count; i++) {
10,660✔
280
        BpfLoadMapDef def = {0};
9,754✔
281
        memcpy(&def, data + i * map_def_size, std::min(map_def_size, sizeof(def)));
11,627✔
282
        mapdefs.emplace_back(def);
9,754✔
283
    }
284

285
    for (const auto& s : mapdefs) {
10,660✔
286
        const int fd = options.mock_map_fds ? allocate_mock_map_fd(map_descriptors, get_map_type_linux(s.type),
29,262✔
287
                                                                   s.key_size, s.value_size, s.max_entries)
9,754✔
288
                                            : create_map_linux(s.type, s.key_size, s.value_size, s.max_entries);
9,754✔
289
        map_descriptors.emplace_back(EbpfMapDescriptor{
14,631✔
290
            .original_fd = fd,
291
            .type = s.type,
9,754✔
292
            .key_size = s.key_size,
9,754✔
293
            .value_size = s.value_size,
9,754✔
294
            .max_entries = s.max_entries,
9,754✔
295
            .inner_map_fd = gsl::narrow<int32_t>(s.inner_map_idx),
9,754✔
296
        });
297
    }
298
}
906✔
299

300
// Initialize the inner_map_fd in each map descriptor.
301
void resolve_inner_map_references_linux(std::vector<EbpfMapDescriptor>& map_descriptors) {
904✔
302
    for (size_t i = 0; i < map_descriptors.size(); i++) {
10,658✔
303
        const int inner = map_descriptors[i].inner_map_fd; // Get the inner_map_idx back.
9,754✔
304
        if (inner < 0 || inner >= gsl::narrow<int>(map_descriptors.size())) {
9,754✔
305
            throw UnmarshalError("bad inner map index " + std::to_string(inner) + " for map " + std::to_string(i));
×
306
        }
307
        map_descriptors[i].inner_map_fd = map_descriptors.at(inner).original_fd;
9,754✔
308
    }
309
}
904✔
310

311
#if __linux__
312
static int do_bpf(const bpf_cmd cmd, bpf_attr& attr) { return syscall(321, cmd, &attr, sizeof(attr)); }
×
313
#endif
314

315
/** Try to allocate a Linux map.
316
 *
317
 *  This function requires admin privileges.
318
 */
319
static int create_map_linux(const uint32_t map_type, const uint32_t key_size, const uint32_t value_size,
×
320
                            const uint32_t max_entries) {
321
#if __linux__
322
    bpf_attr attr{};
×
323
    memset(&attr, '\0', sizeof(attr));
×
324
    attr.map_type = map_type;
×
325
    attr.key_size = key_size;
×
326
    attr.value_size = value_size;
×
327
    attr.max_entries = 20;
×
328
    attr.map_flags = map_type == BPF_MAP_TYPE_HASH ? BPF_F_NO_PREALLOC : 0;
×
329
    const int map_fd = do_bpf(BPF_MAP_CREATE, attr);
×
330
    if (map_fd < 0) {
×
331
        std::cerr << "Failed to create map, " << strerror(errno) << "\n";
×
332
        std::cerr << "Map: \n"
333
                  << " map_type = " << attr.map_type << "\n"
×
334
                  << " key_size = " << attr.key_size << "\n"
×
335
                  << " value_size = " << attr.value_size << "\n"
×
336
                  << " max_entries = " << attr.max_entries << "\n"
×
337
                  << " map_flags = " << attr.map_flags << "\n";
×
338
        exit(2);
×
339
    }
340
    return map_fd;
×
341
#else
342
    throw RuntimeInputError(std::string("cannot create a Linux map"));
343
#endif
344
}
345

346
const EbpfMapDescriptor& get_map_descriptor_linux(const int map_fd, const std::vector<EbpfMapDescriptor>& descriptors) {
58,560✔
347
    for (const EbpfMapDescriptor& map : descriptors) {
904,354✔
348
        if (map.original_fd == map_fd) {
904,354✔
349
            return map;
58,560✔
350
        }
351
    }
352

353
    throw UnmarshalError("map_fd " + std::to_string(map_fd) + " not found");
×
354
}
355

356
static std::optional<ResolvedCall> resolve_kfunc_call_linux(const int32_t btf_id, const int16_t module,
132✔
357
                                                            const EbpfProgramType& program_type, std::string* why_not) {
358
    return make_kfunc_call(btf_id, module, program_type, why_not);
132✔
359
}
360

361
namespace {
362
constexpr int32_t LINUX_BUILTIN_CALL_MEMSET = -11;
363
constexpr int32_t LINUX_BUILTIN_CALL_MEMCPY = -12;
364
constexpr int32_t LINUX_BUILTIN_CALL_MEMMOVE = -13;
365
constexpr int32_t LINUX_BUILTIN_CALL_MEMCMP = -14;
366
constexpr int32_t LINUX_BUILTIN_CALL_EXTERN_UNSPEC = -100;
367
} // namespace
368

369
std::optional<int32_t> resolve_builtin_call_linux(const std::string& name) {
232,610✔
370
    if (name == "memset") {
232,610✔
371
        return LINUX_BUILTIN_CALL_MEMSET;
220,404✔
372
    }
373
    if (name == "memcpy") {
12,206✔
374
        return LINUX_BUILTIN_CALL_MEMCPY;
12,162✔
375
    }
376
    if (name == "memmove") {
44✔
377
        return LINUX_BUILTIN_CALL_MEMMOVE;
2✔
378
    }
379
    if (name == "memcmp") {
42✔
380
        return LINUX_BUILTIN_CALL_MEMCMP;
2✔
381
    }
382

383
    if (const auto helper_id = resolve_helper_id_linux(name)) {
40✔
384
        return *helper_id;
6✔
385
    }
386

387
    if (!name.empty()) {
34✔
388
        return LINUX_BUILTIN_CALL_EXTERN_UNSPEC;
34✔
389
    }
390

391
    return std::nullopt;
×
392
}
393

394
std::optional<KsymBtfId> resolve_ksym_btf_id_linux(const std::string& name) {
114✔
395
    // Synthetic BTF IDs for test/sample kfunc symbols.  IDs 20001-20012, 21000 are
396
    // intentionally NOT in kfunc_prototypes — they exist only to exercise the ELF
397
    // relocation rewrite path and will fail prototype lookup during verification.
398
    // IDs 1009-1010 (cpumask) have prototype entries and will verify.
399
    // In production, the platform callout resolves to real kernel/module BTF IDs.
400
    if (name == "bpf_skb_ct_lookup") {
114✔
401
        return KsymBtfId{.btf_id = 20001, .module = 0};
12✔
402
    }
403
    if (name == "bpf_ct_release") {
102✔
404
        return KsymBtfId{.btf_id = 20002, .module = 0};
12✔
405
    }
406
    if (name == "bpf_fentry_test1") {
90✔
407
        return KsymBtfId{.btf_id = 20003, .module = 0};
12✔
408
    }
409
    if (name == "bpf_cpumask_create") {
78✔
410
        return KsymBtfId{.btf_id = 1009, .module = 0};
12✔
411
    }
412
    if (name == "bpf_cpumask_release") {
66✔
413
        return KsymBtfId{.btf_id = 1010, .module = 0};
12✔
414
    }
415
    if (name == "bpf_kfunc_call_test_mem_len_pass1") {
54✔
416
        return KsymBtfId{.btf_id = 20007, .module = 0};
4✔
417
    }
418
    if (name == "tcp_cong_avoid_ai") {
50✔
419
        return KsymBtfId{.btf_id = 20008, .module = 0};
4✔
420
    }
421
    if (name == "tcp_reno_cong_avoid") {
46✔
422
        return KsymBtfId{.btf_id = 20009, .module = 0};
4✔
423
    }
424
    if (name == "tcp_reno_undo_cwnd") {
42✔
425
        return KsymBtfId{.btf_id = 20010, .module = 0};
4✔
426
    }
427
    if (name == "tcp_slow_start") {
38✔
428
        return KsymBtfId{.btf_id = 20011, .module = 0};
4✔
429
    }
430
    if (name == "bpf_map_sum_elem_count") {
34✔
431
        return KsymBtfId{.btf_id = 20012, .module = 0};
6✔
432
    }
433
    if (name == "bpf_testmod_test_mod_kfunc") {
28✔
434
        return KsymBtfId{.btf_id = 21000, .module = 1};
10✔
435
    }
436

437
    return std::nullopt;
18✔
438
}
439

440
static std::optional<ResolvedCall> get_builtin_call_linux(const int32_t id) {
8,409✔
441
    const Call key{.func = id, .kind = CallKind::builtin};
8,409✔
442
    switch (id) {
8,409✔
443
    case LINUX_BUILTIN_CALL_MEMSET:
7,966✔
444
        return ResolvedCall{
34,184✔
445
            .call = key,
446
            .name = "memset",
3,403✔
447
            .contract = {.singles = {{ArgSingle::Kind::ANYTHING, false, Reg{2}}},
448
                         .pairs = {{ArgPair::Kind::PTR_TO_WRITABLE_MEM, false, Reg{1}, Reg{3}, false}}},
449
        };
7,966✔
450
    case LINUX_BUILTIN_CALL_MEMCPY:
410✔
451
        return ResolvedCall{
1,531✔
452
            .call = key,
453
            .name = "memcpy",
173✔
454
            .contract = {.pairs = {{ArgPair::Kind::PTR_TO_WRITABLE_MEM, false, Reg{1}, Reg{3}, false},
455
                                   {ArgPair::Kind::PTR_TO_READABLE_MEM, false, Reg{2}, Reg{3}, false}}},
456
        };
410✔
457
    case LINUX_BUILTIN_CALL_MEMMOVE:
2✔
458
        return ResolvedCall{
7✔
459
            .call = key,
460
            .name = "memmove",
1✔
461
            .contract = {.pairs = {{ArgPair::Kind::PTR_TO_WRITABLE_MEM, false, Reg{1}, Reg{3}, false},
462
                                   {ArgPair::Kind::PTR_TO_READABLE_MEM, false, Reg{2}, Reg{3}, false}}},
463
        };
2✔
464
    case LINUX_BUILTIN_CALL_MEMCMP:
2✔
465
        return ResolvedCall{
7✔
466
            .call = key,
467
            .name = "memcmp",
1✔
468
            .contract = {.pairs = {{ArgPair::Kind::PTR_TO_READABLE_MEM, false, Reg{1}, Reg{3}, false},
469
                                   {ArgPair::Kind::PTR_TO_READABLE_MEM, false, Reg{2}, Reg{3}, false}}},
470
        };
2✔
471
    case LINUX_BUILTIN_CALL_EXTERN_UNSPEC:
27✔
472
        return ResolvedCall{
102✔
473
            .call = key,
474
            .name = "extern_unspecified",
11✔
475
            .contract = {.singles = {{ArgSingle::Kind::ANYTHING, false, Reg{1}},
476
                                     {ArgSingle::Kind::ANYTHING, false, Reg{2}},
477
                                     {ArgSingle::Kind::ANYTHING, false, Reg{3}},
478
                                     {ArgSingle::Kind::ANYTHING, false, Reg{4}},
479
                                     {ArgSingle::Kind::ANYTHING, false, Reg{5}}},
480
                         .reallocate_packet = true},
481
        };
27✔
482
    default: return std::nullopt;
2✔
483
    }
484
}
485

486
const ebpf_platform_t g_ebpf_platform_linux = {
487
    .get_program_type = get_program_type_linux,
488
    .get_helper_prototype = get_helper_prototype_linux,
489
    .is_helper_usable = is_helper_usable_linux,
490
    .resolve_builtin_call = resolve_builtin_call_linux,
491
    .resolve_ksym_btf_id = resolve_ksym_btf_id_linux,
492
    .get_builtin_call = get_builtin_call_linux,
493
    .resolve_kfunc_call = resolve_kfunc_call_linux,
494
    .map_record_size = sizeof(BpfLoadMapDef),
495
    .parse_maps_section = parse_maps_section_linux,
496
    .get_map_descriptor = get_map_descriptor_linux,
497
    .get_map_type = get_map_type_linux,
498
    .resolve_inner_map_references = resolve_inner_map_references_linux,
499
    .supported_conformance_groups = bpf_conformance_groups_t::default_groups | bpf_conformance_groups_t::packet,
500
};
501
} // 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