• 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

75.63
/src/crab/ebpf_domain.cpp
1
// Copyright (c) Prevail Verifier contributors.
2
// SPDX-License-Identifier: MIT
3

4
// This file is eBPF-specific, not derived from CRAB.
5

6
#include <optional>
7
#include <utility>
8
#include <vector>
9

10
#include "boost/endian/conversion.hpp"
11

12
#include "arith/dsl_syntax.hpp"
13
#include "config.hpp"
14
#include "crab/array_domain.hpp"
15
#include "crab/ebpf_domain.hpp"
16
#include "crab/var_registry.hpp"
17
#include "ir/unmarshal.hpp"
18

19
namespace prevail {
20

21
StringInvariant EbpfDomain::to_set() const { return rcp.to_set() + stack.to_set(); }
2,076✔
22

23
EbpfDomain EbpfDomain::top() {
×
24
    EbpfDomain abs;
×
25
    abs.set_to_top();
×
26
    return abs;
×
27
}
×
28

29
EbpfDomain EbpfDomain::bottom() {
1,159,380✔
30
    EbpfDomain abs;
1,739,070✔
31
    abs.set_to_bottom();
1,739,070✔
32
    return abs;
579,690✔
33
}
34

35
EbpfDomain::EbpfDomain() {}
1,163,182✔
36

37
EbpfDomain::EbpfDomain(TypeToNumDomain rcp, ArrayDomain stack) : rcp(std::move(rcp)), stack(std::move(stack)) {}
122✔
38

39
void EbpfDomain::set_to_top() {
×
40
    rcp.values.set_to_top();
×
41
    stack.set_to_top();
×
42
}
×
43

44
void EbpfDomain::set_to_bottom() { rcp.values.set_to_bottom(); }
1,159,380✔
45

46
bool EbpfDomain::is_bottom() const { return rcp.values.is_bottom(); }
1,655,630✔
47

48
bool EbpfDomain::is_top() const { return rcp.values.is_top() && stack.is_top(); }
×
49

50
bool EbpfDomain::operator<=(const EbpfDomain& other) const {
502✔
51
    if (!(stack <= other.stack)) {
502✔
52
        return false;
53
    }
54
    return rcp <= other.rcp;
502✔
55
}
56

57
bool EbpfDomain::operator==(const EbpfDomain& other) const {
×
58
    return stack == other.stack && rcp <= other.rcp && other.rcp <= rcp;
×
59
}
60

61
void EbpfDomain::operator|=(EbpfDomain&& other) {
415,092✔
62
    if (is_bottom()) {
415,092✔
63
        stack = other.stack;
395,934✔
64
    } else if (!other.is_bottom()) {
19,158✔
65
        stack |= other.stack;
17,140✔
66
    }
67
    rcp |= std::move(other.rcp);
415,092✔
68
}
415,092✔
69

70
void EbpfDomain::operator|=(const EbpfDomain& other) {
122✔
71
    EbpfDomain tmp{other};
122✔
72
    operator|=(std::move(tmp));
122✔
73
}
122✔
74

75
EbpfDomain EbpfDomain::operator|(EbpfDomain&& other) const {
×
76
    EbpfDomain res{std::move(other)};
×
77
    res |= *this;
×
78
    return res;
×
79
}
×
80

81
EbpfDomain EbpfDomain::operator|(const EbpfDomain& other) const& {
122✔
82
    EbpfDomain res{other};
122✔
83
    res |= *this;
122✔
84
    return res;
122✔
85
}
×
86

87
EbpfDomain EbpfDomain::operator|(const EbpfDomain& other) && {
×
88
    EbpfDomain res{std::move(*this)};
×
89
    res |= other;
×
90
    return res;
×
91
}
×
92

93
EbpfDomain EbpfDomain::operator&(const EbpfDomain& other) const {
38✔
94
    auto res = rcp & other.rcp;
38✔
95
    if (!res.is_bottom()) {
38✔
96
        return EbpfDomain(res, stack & other.stack);
76✔
97
    }
98
    return bottom();
×
99
}
38✔
100

101
EbpfDomain EbpfDomain::calculate_constant_limits() {
446✔
102
    EbpfDomain inv;
446✔
103
    using namespace dsl_syntax;
223✔
104
    for (const int i : {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}) {
4,906✔
105
        const auto r = reg_pack(i);
4,460✔
106
        inv.add_value_constraint(r.svalue <= std::numeric_limits<int32_t>::max());
6,690✔
107
        inv.add_value_constraint(r.svalue >= std::numeric_limits<int32_t>::min());
6,690✔
108
        inv.add_value_constraint(r.uvalue <= std::numeric_limits<uint32_t>::max());
6,690✔
109
        inv.add_value_constraint(r.uvalue >= 0);
6,690✔
110
        inv.add_value_constraint(r.stack_offset <= EBPF_TOTAL_STACK_SIZE);
6,690✔
111
        inv.add_value_constraint(r.stack_offset >= 0);
6,690✔
112
        inv.add_value_constraint(r.shared_offset <= r.shared_region_size);
6,690✔
113
        inv.add_value_constraint(r.shared_offset >= 0);
6,690✔
114
        inv.add_value_constraint(r.packet_offset <= variable_registry->packet_size());
6,690✔
115
        inv.add_value_constraint(r.packet_offset >= 0);
6,690✔
116
        if (thread_local_options.cfg_opts.check_for_termination) {
4,460✔
117
            for (const Variable counter : variable_registry->get_loop_counters()) {
×
118
                inv.add_value_constraint(counter <= std::numeric_limits<int32_t>::max());
×
119
                inv.add_value_constraint(counter >= 0);
×
120
                inv.add_value_constraint(counter <= r.svalue);
×
121
            }
×
122
        }
123
    }
124
    return inv;
446✔
125
}
×
126

127
static const EbpfDomain constant_limits = EbpfDomain::calculate_constant_limits();
128

129
EbpfDomain EbpfDomain::widen(const EbpfDomain& other, const bool to_constants) const {
84✔
130
    EbpfDomain res{this->rcp.widen(other.rcp), stack.widen(other.stack)};
126✔
131

132
    if (to_constants) {
84✔
133
        return res & constant_limits;
38✔
134
    }
135
    return res;
46✔
136
}
84✔
137

138
EbpfDomain EbpfDomain::narrow(const EbpfDomain& other) const {
×
139
    return EbpfDomain(rcp.narrow(other.rcp), stack & other.stack);
×
140
}
141

142
void EbpfDomain::add_value_constraint(const LinearConstraint& cst) { rcp.values.add_constraint(cst); }
51,090✔
143

144
void EbpfDomain::add_type_constraint(const LinearConstraint& cst) { rcp.types.add_constraint(cst); }
2,754✔
145

146
void EbpfDomain::havoc(const Variable var) {
2,424✔
147
    // TODO: type inv?
148
    rcp.values.havoc(var);
2,424✔
149
}
1,212✔
150

151
// Get the start and end of the range of possible map fd values.
152
// In the future, it would be cleaner to use a set rather than an interval
153
// for map fds.
154
bool EbpfDomain::get_map_fd_range(const Reg& map_fd_reg, int32_t* start_fd, int32_t* end_fd) const {
13,674✔
155
    const Interval& map_fd_interval = rcp.values.eval_interval(reg_pack(map_fd_reg).map_fd);
13,674✔
156
    const auto lb = map_fd_interval.lb().number();
27,348✔
157
    const auto ub = map_fd_interval.ub().number();
27,348✔
158
    if (!lb || !lb->fits<int32_t>() || !ub || !ub->fits<int32_t>()) {
13,674✔
159
        return false;
160
    }
161
    *start_fd = lb->narrow<int32_t>();
13,674✔
162
    *end_fd = ub->narrow<int32_t>();
13,674✔
163

164
    // Cap the maximum range we'll check.
165
    constexpr int max_range = 32;
13,674✔
166
    return *map_fd_interval.finite_size() < max_range;
13,674✔
167
}
20,511✔
168

169
// All maps in the range must have the same type for us to use it.
170
std::optional<uint32_t> EbpfDomain::get_map_type(const Reg& map_fd_reg) const {
6,836✔
171
    int32_t start_fd, end_fd;
3,418✔
172
    if (!get_map_fd_range(map_fd_reg, &start_fd, &end_fd)) {
6,836✔
173
        return std::optional<uint32_t>();
×
174
    }
175

176
    std::optional<uint32_t> type;
6,836✔
177
    for (int32_t map_fd = start_fd; map_fd <= end_fd; map_fd++) {
13,708✔
178
        EbpfMapDescriptor* map = &thread_local_program_info->platform->get_map_descriptor(map_fd);
6,876✔
179
        if (map == nullptr) {
6,876✔
180
            return std::optional<uint32_t>();
×
181
        }
182
        if (!type.has_value()) {
6,876✔
183
            type = map->type;
6,836✔
184
        } else if (map->type != *type) {
40✔
185
            return std::optional<uint32_t>();
4✔
186
        }
187
    }
188
    return type;
6,832✔
189
}
190

191
// All maps in the range must have the same inner map fd for us to use it.
192
std::optional<uint32_t> EbpfDomain::get_map_inner_map_fd(const Reg& map_fd_reg) const {
32✔
193
    int start_fd, end_fd;
16✔
194
    if (!get_map_fd_range(map_fd_reg, &start_fd, &end_fd)) {
32✔
195
        return {};
×
196
    }
197

198
    std::optional<uint32_t> inner_map_fd;
32✔
199
    for (int map_fd = start_fd; map_fd <= end_fd; map_fd++) {
64✔
200
        EbpfMapDescriptor* map = &thread_local_program_info->platform->get_map_descriptor(map_fd);
32✔
201
        if (map == nullptr) {
32✔
202
            return {};
×
203
        }
204
        if (!inner_map_fd.has_value()) {
32✔
205
            inner_map_fd = map->inner_map_fd;
32✔
206
        } else if (map->type != *inner_map_fd) {
×
207
            return {};
×
208
        }
209
    }
210
    return inner_map_fd;
32✔
211
}
212

213
// We can deal with a range of key sizes.
214
Interval EbpfDomain::get_map_key_size(const Reg& map_fd_reg) const {
3,492✔
215
    int start_fd, end_fd;
1,746✔
216
    if (!get_map_fd_range(map_fd_reg, &start_fd, &end_fd)) {
3,492✔
217
        return Interval::top();
×
218
    }
219

220
    Interval result = Interval::bottom();
3,492✔
221
    for (int map_fd = start_fd; map_fd <= end_fd; map_fd++) {
7,004✔
222
        if (const EbpfMapDescriptor* map = &thread_local_program_info->platform->get_map_descriptor(map_fd)) {
3,512✔
223
            result = result | Interval{map->key_size};
7,024✔
224
        } else {
225
            return Interval::top();
×
226
        }
227
    }
228
    return result;
3,492✔
229
}
3,492✔
230

231
// We can deal with a range of value sizes.
232
Interval EbpfDomain::get_map_value_size(const Reg& map_fd_reg) const {
3,310✔
233
    int start_fd, end_fd;
1,655✔
234
    if (!get_map_fd_range(map_fd_reg, &start_fd, &end_fd)) {
3,310✔
235
        return Interval::top();
×
236
    }
237

238
    Interval result = Interval::bottom();
3,310✔
239
    for (int map_fd = start_fd; map_fd <= end_fd; map_fd++) {
6,638✔
240
        if (const EbpfMapDescriptor* map = &thread_local_program_info->platform->get_map_descriptor(map_fd)) {
3,328✔
241
            result = result | Interval(map->value_size);
6,656✔
242
        } else {
243
            return Interval::top();
×
244
        }
245
    }
246
    return result;
3,310✔
247
}
3,310✔
248

249
// We can deal with a range of max_entries values.
250
Interval EbpfDomain::get_map_max_entries(const Reg& map_fd_reg) const {
4✔
251
    int start_fd, end_fd;
2✔
252
    if (!get_map_fd_range(map_fd_reg, &start_fd, &end_fd)) {
4✔
253
        return Interval::top();
×
254
    }
255

256
    Interval result = Interval::bottom();
4✔
257
    for (int map_fd = start_fd; map_fd <= end_fd; map_fd++) {
8✔
258
        if (const EbpfMapDescriptor* map = &thread_local_program_info->platform->get_map_descriptor(map_fd)) {
4✔
259
            result = result | Interval(map->max_entries);
8✔
260
        } else {
261
            return Interval::top();
×
262
        }
263
    }
264
    return result;
4✔
265
}
4✔
266

267
ExtendedNumber EbpfDomain::get_loop_count_upper_bound() const {
160✔
268
    ExtendedNumber ub{0};
160✔
269
    for (const Variable counter : variable_registry->get_loop_counters()) {
304✔
270
        ub = std::max(ub, rcp.values.eval_interval(counter).ub());
414✔
271
    }
80✔
272
    return ub;
160✔
273
}
×
274

275
Interval EbpfDomain::get_r0() const { return rcp.values.eval_interval(reg_pack(R0_RETURN_VALUE).svalue); }
2,556✔
276

277
std::ostream& operator<<(std::ostream& o, const TypeDomain& dom) { return o << dom.inv; }
×
278

279
std::ostream& operator<<(std::ostream& o, const TypeToNumDomain& dom) {
×
280
    if (dom.is_bottom()) {
×
281
        o << "_|_";
×
282
    } else {
283
        o << dom.types << dom.values;
×
284
    }
285
    return o;
×
286
}
287

288
std::ostream& operator<<(std::ostream& o, const EbpfDomain& dom) {
×
289
    if (dom.is_bottom()) {
×
290
        o << "_|_";
×
291
    } else {
292
        o << dom.rcp << "\nStack: " << dom.stack;
×
293
    }
294
    return o;
×
295
}
296

297
void EbpfDomain::initialize_packet() {
1,212✔
298
    using namespace dsl_syntax;
606✔
299
    EbpfDomain& inv = *this;
1,212✔
300
    inv.havoc(variable_registry->packet_size());
1,212✔
301
    inv.havoc(variable_registry->meta_offset());
1,212✔
302

303
    inv.add_value_constraint(0 <= variable_registry->packet_size());
2,424✔
304
    inv.add_value_constraint(variable_registry->packet_size() < MAX_PACKET_SIZE);
1,818✔
305
    const auto info = *thread_local_program_info;
1,212✔
306
    if (info.type.context_descriptor->meta >= 0) {
1,212✔
307
        inv.add_value_constraint(variable_registry->meta_offset() <= 0);
813✔
308
        inv.add_value_constraint(variable_registry->meta_offset() >= -4098);
813✔
309
    } else {
310
        inv.rcp.values.assign(variable_registry->meta_offset(), 0);
1,005✔
311
    }
312
}
1,212✔
313

314
EbpfDomain EbpfDomain::from_constraints(const std::vector<LinearConstraint>& type_constraints,
356✔
315
                                        const std::vector<LinearConstraint>& value_constraints) {
316
    EbpfDomain inv;
356✔
317
    for (const auto& cst : type_constraints) {
798✔
318
        inv.add_type_constraint(cst);
663✔
319
    }
320
    for (const auto& cst : value_constraints) {
880✔
321
        inv.add_value_constraint(cst);
524✔
322
    }
323
    return inv;
356✔
324
}
×
325

326
EbpfDomain EbpfDomain::from_constraints(const std::set<std::string>& constraints, const bool setup_constraints) {
1,828✔
327
    EbpfDomain inv;
1,828✔
328
    if (setup_constraints) {
1,828✔
329
        inv = setup_entry(false);
666✔
330
    }
331
    auto numeric_ranges = std::vector<Interval>();
1,828✔
332
    auto [type_constraints, value_constraints] = parse_linear_constraints(constraints, numeric_ranges);
1,828✔
333
    for (const auto& cst : type_constraints) {
4,140✔
334
        inv.add_type_constraint(cst);
3,468✔
335
    }
336
    for (const auto& cst : value_constraints) {
7,810✔
337
        inv.add_value_constraint(cst);
5,982✔
338
    }
339
    for (const Interval& range : numeric_ranges) {
2,090✔
340
        const int start = range.lb().narrow<int>();
393✔
341
        const int width = 1 + range.finite_size()->narrow<int>();
262✔
342
        inv.stack.initialize_numbers(start, width);
262✔
343
    }
344
    // TODO: handle other stack type constraints
345
    return inv;
3,656✔
346
}
1,828✔
347

348
EbpfDomain EbpfDomain::setup_entry(const bool init_r1) {
1,172✔
349
    using namespace dsl_syntax;
586✔
350

351
    EbpfDomain inv;
1,172✔
352
    const auto r10 = reg_pack(R10_STACK_POINTER);
1,172✔
353
    constexpr Reg r10_reg{R10_STACK_POINTER};
1,172✔
354
    inv.rcp.values.add_constraint(EBPF_TOTAL_STACK_SIZE <= r10.svalue);
2,344✔
355
    inv.rcp.values.add_constraint(r10.svalue <= PTR_MAX);
1,758✔
356
    inv.rcp.values.assign(r10.stack_offset, EBPF_TOTAL_STACK_SIZE);
1,172✔
357
    // stack_numeric_size would be 0, but TOP has the same result
358
    // so no need to assign it.
359
    inv.rcp.types.assign_type(r10_reg, T_STACK);
1,172✔
360

361
    if (init_r1) {
1,172✔
362
        const auto r1 = reg_pack(R1_ARG);
728✔
363
        constexpr Reg r1_reg{R1_ARG};
728✔
364
        inv.rcp.values.add_constraint(1 <= r1.svalue);
1,456✔
365
        inv.rcp.values.add_constraint(r1.svalue <= PTR_MAX);
1,092✔
366
        inv.rcp.values.assign(r1.ctx_offset, 0);
728✔
367
        inv.rcp.types.assign_type(r1_reg, T_CTX);
1,092✔
368
    }
369

370
    inv.initialize_packet();
1,172✔
371
    return inv;
1,758✔
372
}
×
373

374
} // 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