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

Alan-Jowett / ebpf-verifier / 31421448145

08 Aug 2026 08:33PM UTC coverage: 85.986% (-0.8%) from 86.764%
31421448145

push

github

elazarg
Bump github/codeql-action from 4.37.3 to 4.37.6

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.37.3 to 4.37.6.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v4.37.3...v4.37.6)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.37.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

9026 of 10497 relevant lines covered (85.99%)

3151528.04 hits per line

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

84.56
/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) {
16,212✔
173
    EbpfProgramType type{};
16,212✔
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) {
16,212✔
180
        if (path.find("xdp") != std::string::npos) {
2,854✔
181
            return linux_xdp_program_type;
643✔
182
        }
183
        if (path.find("lxc") != std::string::npos) {
2,211✔
184
            return cilium_lxc_program_type;
452✔
185
        }
186
    }
187

188
    for (const EbpfProgramType& t : linux_program_types) {
315,299✔
189
        for (const std::string& prefix : t.section_prefixes) {
706,595✔
190
            if (section.find(prefix) == 0) {
406,413✔
191
                EbpfProgramType result = t;
11,441✔
192
                result.is_sleepable = prefix.find(".s/") != std::string::npos || result.name == "syscall";
11,441✔
193
                return result;
11,441✔
194
            }
11,441✔
195
        }
196
    }
197

198
    return linux_socket_filter_program_type;
3,676✔
199
}
16,212✔
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) {
60,580✔
245
    const uint32_t index = platform_specific_type;
60,580✔
246
    if (index == 0 || index >= std::size(linux_map_types)) {
60,580✔
247
        return linux_map_types[0];
1,517✔
248
    }
249
    EbpfMapType type = linux_map_types[index];
59,063✔
250
#ifdef __linux__
251
    assert(type.platform_specific_type == platform_specific_type);
59,063✔
252
#else
253
    type.platform_specific_type = platform_specific_type;
254
#endif
255
    return type;
59,063✔
256
}
59,063✔
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,
4,877✔
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};
4,877✔
263
    for (const auto& desc : map_descriptors) {
47,392✔
264
        const EbpfMapType existing_type = get_map_type_linux(desc.type);
43,269✔
265
        const EquivalenceKey existing{existing_type.value_type, desc.key_size, desc.value_size,
43,269✔
266
                                      existing_type.is_array ? desc.max_entries : 0};
43,269✔
267
        if (existing == equiv) {
43,269✔
268
            return desc.original_fd;
754✔
269
        }
270
    }
43,269✔
271
    // +1 so 0 is the null FD
272
    return gsl::narrow<int>(map_descriptors.size()) + 1;
4,123✔
273
}
274

275
void parse_maps_section_linux(std::vector<EbpfMapDescriptor>& map_descriptors, const char* data,
453✔
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>();
453✔
279
    for (int i = 0; i < map_count; i++) {
5,330✔
280
        BpfLoadMapDef def = {0};
4,877✔
281
        memcpy(&def, data + i * map_def_size, std::min(map_def_size, sizeof(def)));
6,750✔
282
        mapdefs.emplace_back(def);
4,877✔
283
    }
284

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

300
// Initialize the inner_map_fd in each map descriptor.
301
void resolve_inner_map_references_linux(std::vector<EbpfMapDescriptor>& map_descriptors) {
452✔
302
    for (size_t i = 0; i < map_descriptors.size(); i++) {
5,329✔
303
        const int inner = map_descriptors[i].inner_map_fd; // Get the inner_map_idx back.
4,877✔
304
        if (inner < 0 || inner >= gsl::narrow<int>(map_descriptors.size())) {
4,877✔
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;
4,877✔
308
    }
309
}
452✔
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) {
31,500✔
347
    for (const EbpfMapDescriptor& map : descriptors) {
489,889✔
348
        if (map.original_fd == map_fd) {
489,889✔
349
            return map;
31,500✔
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,
66✔
357
                                                            const EbpfProgramType& program_type, std::string* why_not) {
358
    return make_kfunc_call(btf_id, module, program_type, why_not);
66✔
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) {
116,305✔
370
    if (name == "memset") {
116,305✔
371
        return LINUX_BUILTIN_CALL_MEMSET;
110,202✔
372
    }
373
    if (name == "memcpy") {
6,103✔
374
        return LINUX_BUILTIN_CALL_MEMCPY;
6,081✔
375
    }
376
    if (name == "memmove") {
22✔
377
        return LINUX_BUILTIN_CALL_MEMMOVE;
1✔
378
    }
379
    if (name == "memcmp") {
21✔
380
        return LINUX_BUILTIN_CALL_MEMCMP;
1✔
381
    }
382

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

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

391
    return std::nullopt;
×
392
}
393

394
std::optional<KsymBtfId> resolve_ksym_btf_id_linux(const std::string& name) {
57✔
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") {
57✔
401
        return KsymBtfId{.btf_id = 20001, .module = 0};
6✔
402
    }
403
    if (name == "bpf_ct_release") {
51✔
404
        return KsymBtfId{.btf_id = 20002, .module = 0};
6✔
405
    }
406
    if (name == "bpf_fentry_test1") {
45✔
407
        return KsymBtfId{.btf_id = 20003, .module = 0};
6✔
408
    }
409
    if (name == "bpf_cpumask_create") {
39✔
410
        return KsymBtfId{.btf_id = 1009, .module = 0};
6✔
411
    }
412
    if (name == "bpf_cpumask_release") {
33✔
413
        return KsymBtfId{.btf_id = 1010, .module = 0};
6✔
414
    }
415
    if (name == "bpf_kfunc_call_test_mem_len_pass1") {
27✔
416
        return KsymBtfId{.btf_id = 20007, .module = 0};
2✔
417
    }
418
    if (name == "tcp_cong_avoid_ai") {
25✔
419
        return KsymBtfId{.btf_id = 20008, .module = 0};
2✔
420
    }
421
    if (name == "tcp_reno_cong_avoid") {
23✔
422
        return KsymBtfId{.btf_id = 20009, .module = 0};
2✔
423
    }
424
    if (name == "tcp_reno_undo_cwnd") {
21✔
425
        return KsymBtfId{.btf_id = 20010, .module = 0};
2✔
426
    }
427
    if (name == "tcp_slow_start") {
19✔
428
        return KsymBtfId{.btf_id = 20011, .module = 0};
2✔
429
    }
430
    if (name == "bpf_map_sum_elem_count") {
17✔
431
        return KsymBtfId{.btf_id = 20012, .module = 0};
3✔
432
    }
433
    if (name == "bpf_testmod_test_mod_kfunc") {
14✔
434
        return KsymBtfId{.btf_id = 21000, .module = 1};
5✔
435
    }
436

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

440
static std::optional<ResolvedCall> get_builtin_call_linux(const int32_t id) {
3,627✔
441
    const Call key{.func = id, .kind = CallKind::builtin};
3,627✔
442
    switch (id) {
3,627✔
443
    case LINUX_BUILTIN_CALL_MEMSET:
3,440✔
444
        return ResolvedCall{
6,880✔
445
            .call = key,
446
            .name = "memset",
3,440✔
447
            .contract = {.singles = {{ArgSingle::Kind::ANYTHING, false, Reg{2}}},
448
                         .pairs = {{ArgPair::Kind::PTR_TO_WRITABLE_MEM, false, Reg{1}, Reg{3}, false}}},
449
        };
3,440✔
450
    case LINUX_BUILTIN_CALL_MEMCPY:
173✔
451
        return ResolvedCall{
346✔
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
        };
173✔
457
    case LINUX_BUILTIN_CALL_MEMMOVE:
1✔
458
        return ResolvedCall{
2✔
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
        };
1✔
464
    case LINUX_BUILTIN_CALL_MEMCMP:
1✔
465
        return ResolvedCall{
2✔
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
        };
1✔
471
    case LINUX_BUILTIN_CALL_EXTERN_UNSPEC:
11✔
472
        return ResolvedCall{
22✔
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
        };
11✔
482
    default: return std::nullopt;
1✔
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
    .sock_common_layout = &bpf_sock_common_layout,
500
    .supported_conformance_groups = bpf_conformance_groups_t::default_groups | bpf_conformance_groups_t::packet,
501
};
502
} // namespace prevail
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc