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

Alan-Jowett / ebpf-verifier / 22316701280

22 Feb 2026 08:43PM UTC coverage: 88.93% (+0.9%) from 88.002%
22316701280

push

github

web-flow
Human-friendly CLI output for bin/prevail (#1042)

* Replace CSV output with human-friendly PASS/FAIL CLI output

The default output was `{0|1},{seconds},{memory_kb}` — a CSV row for
benchmarking scripts that is unfriendly for humans. Benchmarking is
better done externally (time, /usr/bin/time -v).

New output: `PASS: section/function` or `FAIL: section/function` with
the first error and a hint line pointing to --failure-slice / -v.
Add -q/--quiet (exit code only) and --cfg (replaces --domain cfg).

Remove dead code: --domain option (linux, stats, zoneCrab selectors),
linux_verifier, memsize helpers, collect_stats/stats_headers, fnv1a64,
@headers special filename, bin/check alias, and stale benchmark scripts.

Move src/main/check.cpp → src/main.cpp (simplified, rewritten).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Elazar Gershuni <elazarg@gmail.com>

* Graduate loop3.o from skip to expected failure

The >4x performance improvement means loop3.o no longer hangs.
It now completes quickly but is rejected due to type precision
loss through the loop join (VerifierTypeTracking).

* Remove redundant install exclude, fix doc path

- Remove `PATTERN "main.cpp" EXCLUDE` from install — the glob only
  matches *.hpp/*.h so main.cpp would never be included anyway.
- Fix `./prevail` → `./bin/prevail` in docs/architecture.md for
  consistency with the actual binary location.

---------

Signed-off-by: Elazar Gershuni <elazarg@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

1 of 1 new or added line in 1 file covered. (100.0%)

198 existing lines in 11 files now uncovered.

13159 of 14797 relevant lines covered (88.93%)

4658715.43 hits per line

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

81.98
/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
                            ebpf_verifier_options_t options);
117

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

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

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

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

129
const std::vector<EbpfProgramType> linux_program_types = {
130
    PTYPE("unspec", &g_unspec_descr, BPF_PROG_TYPE_UNSPEC, {}),
131
    linux_socket_filter_program_type,
132
    linux_xdp_program_type,
133
    PTYPE("cgroup_device", &g_cgroup_dev_descr, BPF_PROG_TYPE_CGROUP_DEVICE, {"cgroup/dev"}),
134
    PTYPE("cgroup_skb", &g_socket_filter_descr, BPF_PROG_TYPE_CGROUP_SKB, {"cgroup/skb"}),
135
    PTYPE("cgroup_sock", &g_cgroup_sock_descr, BPF_PROG_TYPE_CGROUP_SOCK, {"cgroup/sock"}),
136
    PTYPE_PRIVILEGED("kprobe", &g_kprobe_descr, BPF_PROG_TYPE_KPROBE, {"kprobe/" COMMA "kretprobe/"}),
137
    PTYPE("lwt_in", &g_lwt_inout_descr, BPF_PROG_TYPE_LWT_IN, {"lwt_in"}),
138
    PTYPE("lwt_out", &g_lwt_inout_descr, BPF_PROG_TYPE_LWT_OUT, {"lwt_out"}),
139
    PTYPE("lwt_xmit", &g_lwt_xmit_descr, BPF_PROG_TYPE_LWT_XMIT, {"lwt_xmit"}),
140
    PTYPE("perf_event", &g_perf_event_descr, BPF_PROG_TYPE_PERF_EVENT, {"perf_section" COMMA "perf_event"}),
141
    PTYPE("sched_act", &g_sched_descr, BPF_PROG_TYPE_SCHED_ACT, {"action"}),
142
    PTYPE("sched_cls", &g_sched_descr, BPF_PROG_TYPE_SCHED_CLS, {"classifier"}),
143
    PTYPE("sk_skb", &g_sk_skb_descr, BPF_PROG_TYPE_SK_SKB, {"sk_skb"}),
144
    PTYPE("sock_ops", &g_sock_ops_descr, BPF_PROG_TYPE_SOCK_OPS, {"sockops"}),
145
    PTYPE("tracepoint", &g_tracepoint_descr, BPF_PROG_TYPE_TRACEPOINT, {"tracepoint/"}),
146
    PTYPE("cgroup_sockopt", &g_sockopt_descr, BPF_PROG_TYPE_CGROUP_SOCKOPT,
147
          {"cgroup/getsockopt" COMMA "cgroup/setsockopt"}),
148
    PTYPE("sk_msg", &g_sk_msg_md, BPF_PROG_TYPE_SK_MSG, {"sk_msg"}),
149
    PTYPE_PRIVILEGED("raw_tracepoint", &g_tracepoint_descr, BPF_PROG_TYPE_RAW_TRACEPOINT,
150
                     {"raw_tracepoint/" COMMA "raw_tp/"}),
151
    PTYPE_PRIVILEGED("raw_tracepoint_writable", &g_tracepoint_descr, BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
152
                     {"raw_tracepoint.w/" COMMA "raw_tp.w/"}),
153
    PTYPE("cgroup_sock_addr", &g_sock_addr_descr, BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
154
          {"cgroup/bind" COMMA "cgroup/post_bind" COMMA "cgroup/connect" COMMA "cgroup/sendmsg" COMMA
155
           "cgroup/recvmsg" COMMA "cgroup/getpeername" COMMA "cgroup/getsockname"}),
156
    PTYPE("lwt_seg6local", &g_lwt_xmit_descr, BPF_PROG_TYPE_LWT_SEG6LOCAL, {"lwt_seg6local"}),
157
    PTYPE("lirc_mode2", &g_lirc_mode2_descr, BPF_PROG_TYPE_LIRC_MODE2, {"lirc_mode2"}),
158
    PTYPE("sk_reuseport", &g_sk_reuseport_descr, BPF_PROG_TYPE_SK_REUSEPORT, {"sk_reuseport/"}),
159
    PTYPE("flow_dissector", &g_flow_dissector_descr, BPF_PROG_TYPE_FLOW_DISSECTOR, {"flow_dissector"}),
160
    PTYPE("cgroup_sysctl", &g_cgroup_sysctl_descr, BPF_PROG_TYPE_CGROUP_SYSCTL, {"cgroup/sysctl"}),
161
    PTYPE("ext", &g_unspec_descr, BPF_PROG_TYPE_EXT, {"freplace/"}),
162
    PTYPE("tracing", &g_tracing_descr, BPF_PROG_TYPE_TRACING,
163
          {"fentry/" COMMA "fexit/" COMMA "fmod_ret/" COMMA "iter/" COMMA "tp_btf/"}),
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/"}),
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,432✔
173
    EbpfProgramType type{};
32,432✔
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,432✔
180
        if (path.find("xdp") != std::string::npos) {
5,702✔
181
            return linux_xdp_program_type;
1,286✔
182
        }
183
        if (path.find("lxc") != std::string::npos) {
4,416✔
184
            return cilium_lxc_program_type;
904✔
185
        }
186
    }
187

188
    for (const EbpfProgramType& t : linux_program_types) {
630,554✔
189
        for (const std::string& prefix : t.section_prefixes) {
1,368,732✔
190
            if (section.find(prefix) == 0) {
768,420✔
191
                return t;
27,672✔
192
            }
193
        }
194
    }
195

196
    return linux_socket_filter_program_type;
7,330✔
197
}
32,432✔
198

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

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

242
EbpfMapType get_map_type_linux(uint32_t platform_specific_type) {
44,044✔
243
    const uint32_t index = platform_specific_type;
44,044✔
244
    if (index == 0 || index >= std::size(linux_map_types)) {
64,569✔
245
        return linux_map_types[0];
2,994✔
246
    }
247
    EbpfMapType type = linux_map_types[index];
41,050✔
248
#ifdef __linux__
249
    assert(type.platform_specific_type == platform_specific_type);
41,050✔
250
#else
251
    type.platform_specific_type = platform_specific_type;
252
#endif
253
    return type;
41,050✔
254
}
41,050✔
255

256
void parse_maps_section_linux(std::vector<EbpfMapDescriptor>& map_descriptors, const char* data,
906✔
257
                              const size_t map_def_size, const int map_count, const ebpf_platform_t* platform,
258
                              const ebpf_verifier_options_t options) {
259
    // Copy map definitions from the ELF section into a local list.
260
    auto mapdefs = std::vector<BpfLoadMapDef>();
906✔
261
    for (int i = 0; i < map_count; i++) {
10,660✔
262
        BpfLoadMapDef def = {0};
9,754✔
263
        memcpy(&def, data + i * map_def_size, std::min(map_def_size, sizeof(def)));
11,627✔
264
        mapdefs.emplace_back(def);
9,754✔
265
    }
266

267
    // Add map definitions into the map_descriptors list.
268
    for (const auto& s : mapdefs) {
10,660✔
269
        EbpfMapType type = get_map_type_linux(s.type);
9,754✔
270
        map_descriptors.emplace_back(EbpfMapDescriptor{
4,877✔
271
            .original_fd = create_map_linux(s.type, s.key_size, s.value_size, s.max_entries, options),
9,754✔
272
            .type = s.type,
9,754✔
273
            .key_size = s.key_size,
9,754✔
274
            .value_size = s.value_size,
9,754✔
275
            .max_entries = s.max_entries,
9,754✔
276
            .inner_map_fd = gsl::narrow<int32_t>(s.inner_map_idx) // Temporarily fill in the index. This will be
9,754✔
277
                                                                  // replaced in the resolve_inner_map_references pass.
278
        });
279
    }
9,754✔
280
}
906✔
281

282
// Initialize the inner_map_fd in each map descriptor.
283
void resolve_inner_map_references_linux(std::vector<EbpfMapDescriptor>& map_descriptors) {
904✔
284
    for (size_t i = 0; i < map_descriptors.size(); i++) {
10,658✔
285
        const int inner = map_descriptors[i].inner_map_fd; // Get the inner_map_idx back.
9,754✔
286
        if (inner < 0 || inner >= gsl::narrow<int>(map_descriptors.size())) {
9,754✔
UNCOV
287
            throw UnmarshalError("bad inner map index " + std::to_string(inner) + " for map " + std::to_string(i));
×
288
        }
289
        map_descriptors[i].inner_map_fd = map_descriptors.at(inner).original_fd;
9,754✔
290
    }
291
}
904✔
292

293
#if __linux__
UNCOV
294
static int do_bpf(const bpf_cmd cmd, bpf_attr& attr) { return syscall(321, cmd, &attr, sizeof(attr)); }
×
295
#endif
296

297
/** Try to allocate a Linux map.
298
 *
299
 *  This function requires admin privileges.
300
 */
301
static int create_map_linux(const uint32_t map_type, const uint32_t key_size, const uint32_t value_size,
9,754✔
302
                            const uint32_t max_entries, const ebpf_verifier_options_t options) {
303
    if (options.mock_map_fds) {
9,754✔
304
        const EbpfMapType type = get_map_type_linux(map_type);
9,754✔
305
        return create_map_crab(type, key_size, value_size, max_entries, options);
9,754✔
306
    }
9,754✔
307

308
#if __linux__
309
    bpf_attr attr{};
×
310
    memset(&attr, '\0', sizeof(attr));
×
311
    attr.map_type = map_type;
×
312
    attr.key_size = key_size;
×
313
    attr.value_size = value_size;
×
314
    attr.max_entries = 20;
×
315
    attr.map_flags = map_type == BPF_MAP_TYPE_HASH ? BPF_F_NO_PREALLOC : 0;
×
316
    const int map_fd = do_bpf(BPF_MAP_CREATE, attr);
×
317
    if (map_fd < 0) {
×
UNCOV
318
        std::cerr << "Failed to create map, " << strerror(errno) << "\n";
×
319
        std::cerr << "Map: \n"
320
                  << " map_type = " << attr.map_type << "\n"
×
321
                  << " key_size = " << attr.key_size << "\n"
×
322
                  << " value_size = " << attr.value_size << "\n"
×
323
                  << " max_entries = " << attr.max_entries << "\n"
×
324
                  << " map_flags = " << attr.map_flags << "\n";
×
UNCOV
325
        exit(2);
×
326
    }
327
    return map_fd;
328
#else
329
    throw std::runtime_error(std::string("cannot create a Linux map"));
330
#endif
331
}
332

333
EbpfMapDescriptor& get_map_descriptor_linux(const int map_fd) {
60,678✔
334
    // First check if we already have the map descriptor cached.
335
    EbpfMapDescriptor* map = find_map_descriptor(map_fd);
60,678✔
336
    if (map != nullptr) {
60,678✔
337
        return *map;
60,678✔
338
    }
339

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

UNCOV
346
    throw UnmarshalError("map_fd " + std::to_string(map_fd) + " not found");
×
347
}
348

349
static std::optional<Call> resolve_kfunc_call_linux(const int32_t btf_id, const ProgramInfo* info,
38✔
350
                                                    std::string* why_not) {
351
    return make_kfunc_call(btf_id, info, why_not);
38✔
352
}
353

354
namespace {
355
constexpr int32_t LINUX_BUILTIN_CALL_MEMSET = -11;
356
constexpr int32_t LINUX_BUILTIN_CALL_MEMCPY = -12;
357
constexpr int32_t LINUX_BUILTIN_CALL_MEMMOVE = -13;
358
constexpr int32_t LINUX_BUILTIN_CALL_MEMCMP = -14;
359
constexpr int32_t LINUX_BUILTIN_CALL_EXTERN_UNSPEC = -100;
360
} // namespace
361

362
std::optional<int32_t> resolve_builtin_call_linux(const std::string& name) {
232,706✔
363
    if (name == "memset") {
232,706✔
364
        return LINUX_BUILTIN_CALL_MEMSET;
220,404✔
365
    }
366
    if (name == "memcpy") {
12,302✔
367
        return LINUX_BUILTIN_CALL_MEMCPY;
12,162✔
368
    }
369
    if (name == "memmove") {
140✔
370
        return LINUX_BUILTIN_CALL_MEMMOVE;
2✔
371
    }
372
    if (name == "memcmp") {
138✔
373
        return LINUX_BUILTIN_CALL_MEMCMP;
2✔
374
    }
375

376
    if (const auto helper_id = resolve_helper_id_linux(name)) {
136✔
377
        return *helper_id;
6✔
378
    }
379

380
    if (!name.empty()) {
130✔
381
        return LINUX_BUILTIN_CALL_EXTERN_UNSPEC;
130✔
382
    }
383

UNCOV
384
    return std::nullopt;
×
385
}
386

387
static std::optional<Call> get_builtin_call_linux(const int32_t id) {
2,498✔
388
    switch (id) {
2,498✔
389
    case LINUX_BUILTIN_CALL_MEMSET:
2,324✔
390
        return Call{
9,296✔
391
            .func = id,
392
            .name = "memset",
1,162✔
393
            .singles = {{ArgSingle::Kind::ANYTHING, false, Reg{2}}},
394
            .pairs = {{ArgPair::Kind::PTR_TO_WRITABLE_MEM, false, Reg{1}, Reg{3}, false}},
395
        };
2,324✔
396
    case LINUX_BUILTIN_CALL_MEMCPY:
130✔
397
        return Call{
455✔
398
            .func = id,
399
            .name = "memcpy",
65✔
400
            .pairs = {{ArgPair::Kind::PTR_TO_WRITABLE_MEM, false, Reg{1}, Reg{3}, false},
401
                      {ArgPair::Kind::PTR_TO_READABLE_MEM, false, Reg{2}, Reg{3}, false}},
402
        };
130✔
403
    case LINUX_BUILTIN_CALL_MEMMOVE:
2✔
404
        return Call{
7✔
405
            .func = id,
406
            .name = "memmove",
1✔
407
            .pairs = {{ArgPair::Kind::PTR_TO_WRITABLE_MEM, false, Reg{1}, Reg{3}, false},
408
                      {ArgPair::Kind::PTR_TO_READABLE_MEM, false, Reg{2}, Reg{3}, false}},
409
        };
2✔
410
    case LINUX_BUILTIN_CALL_MEMCMP:
2✔
411
        return Call{
7✔
412
            .func = id,
413
            .name = "memcmp",
1✔
414
            .pairs = {{ArgPair::Kind::PTR_TO_READABLE_MEM, false, Reg{1}, Reg{3}, false},
415
                      {ArgPair::Kind::PTR_TO_READABLE_MEM, false, Reg{2}, Reg{3}, false}},
416
        };
2✔
417
    case LINUX_BUILTIN_CALL_EXTERN_UNSPEC:
38✔
418
        return Call{
133✔
419
            .func = id,
420
            .name = "extern_unspecified",
19✔
421
            .reallocate_packet = true,
422
            .singles = {{ArgSingle::Kind::ANYTHING, false, Reg{1}},
423
                        {ArgSingle::Kind::ANYTHING, false, Reg{2}},
424
                        {ArgSingle::Kind::ANYTHING, false, Reg{3}},
425
                        {ArgSingle::Kind::ANYTHING, false, Reg{4}},
426
                        {ArgSingle::Kind::ANYTHING, false, Reg{5}}},
427
        };
38✔
428
    default: return std::nullopt;
2✔
429
    }
430
}
431

432
const ebpf_platform_t g_ebpf_platform_linux = {
433
    .get_program_type = get_program_type_linux,
434
    .get_helper_prototype = get_helper_prototype_linux,
435
    .is_helper_usable = is_helper_usable_linux,
436
    .resolve_builtin_call = resolve_builtin_call_linux,
437
    .get_builtin_call = get_builtin_call_linux,
438
    .resolve_kfunc_call = resolve_kfunc_call_linux,
439
    .map_record_size = sizeof(BpfLoadMapDef),
440
    .parse_maps_section = parse_maps_section_linux,
441
    .get_map_descriptor = get_map_descriptor_linux,
442
    .get_map_type = get_map_type_linux,
443
    .resolve_inner_map_references = resolve_inner_map_references_linux,
444
    .supported_conformance_groups = bpf_conformance_groups_t::default_groups | bpf_conformance_groups_t::packet,
445
};
446
} // 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