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

vbpf / ebpf-verifier / 12845504567

18 Jan 2025 04:08PM UTC coverage: 88.169% (-1.5%) from 89.646%
12845504567

push

github

web-flow
Handle upgrade from LCOV 1.15 to LCOV 2.0 (#826)

* Testing code coverage with a comment only change
* Workaround for failing code coverage
* Testing code coverage fix

Signed-off-by: Alan Jowett <alanjo@microsoft.com>

8481 of 9619 relevant lines covered (88.17%)

7430805.79 hits per line

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

89.57
/src/crab/type_domain.cpp
1
// Copyright (c) Prevail Verifier contributors.
2
// SPDX-License-Identifier: MIT
3
// ReSharper disable CppMemberFunctionMayBeStatic
4

5
// This file is eBPF-specific, not derived from CRAB.
6
#include <functional>
7
#include <map>
8
#include <optional>
9

10
#include "crab/array_domain.hpp"
11
#include "crab/split_dbm.hpp"
12
#include "crab/type_domain.hpp"
13
#include "crab/type_encoding.hpp"
14
#include "crab/variable.hpp"
15
#include "crab_utils/debug.hpp"
16
#include "dsl_syntax.hpp"
17

18
namespace crab {
19

20
template <is_enum T>
21
static void operator++(T& t) {
99,316✔
22
    t = static_cast<T>(1 + static_cast<std::underlying_type_t<T>>(t));
99,316✔
23
}
99,316✔
24

25
std::vector<data_kind_t> iterate_kinds(const data_kind_t lb, const data_kind_t ub) {
486✔
26
    if (lb > ub) {
486✔
27
        CRAB_ERROR("lower bound ", lb, " is greater than upper bound ", ub);
×
28
    }
29
    if (lb < KIND_MIN || ub > KIND_MAX) {
486✔
30
        CRAB_ERROR("bounds ", lb, " and ", ub, " are out of range");
×
31
    }
32
    std::vector<data_kind_t> res;
486✔
33
    for (data_kind_t i = lb; i <= ub; ++i) {
5,346✔
34
        res.push_back(i);
4,860✔
35
    }
36
    return res;
486✔
37
}
×
38

39
std::vector<type_encoding_t> iterate_types(const type_encoding_t lb, const type_encoding_t ub) {
94,358✔
40
    if (lb > ub) {
94,358✔
41
        CRAB_ERROR("lower bound ", lb, " is greater than upper bound ", ub);
×
42
    }
43
    if (lb < T_MIN || ub > T_MAX) {
94,358✔
44
        CRAB_ERROR("bounds ", lb, " and ", ub, " are out of range");
×
45
    }
46
    std::vector<type_encoding_t> res;
94,358✔
47
    for (type_encoding_t i = lb; i <= ub; ++i) {
188,814✔
48
        res.push_back(i);
94,456✔
49
    }
50
    return res;
94,358✔
51
}
×
52

53
static constexpr auto S_UNINIT = "uninit";
54
static constexpr auto S_STACK = "stack";
55
static constexpr auto S_PACKET = "packet";
56
static constexpr auto S_CTX = "ctx";
57
static constexpr auto S_MAP_PROGRAMS = "map_fd_programs";
58
static constexpr auto S_MAP = "map_fd";
59
static constexpr auto S_NUM = "number";
60
static constexpr auto S_SHARED = "shared";
61

62
std::string name_of(const data_kind_t kind) {
22,041,142✔
63
    switch (kind) {
22,041,142✔
64
    case data_kind_t::ctx_offsets: return "ctx_offset";
4,256,874✔
65
    case data_kind_t::map_fds: return "map_fd";
6,084,898✔
66
    case data_kind_t::packet_offsets: return "packet_offset";
4,246,418✔
67
    case data_kind_t::shared_offsets: return "shared_offset";
4,246,506✔
68
    case data_kind_t::shared_region_sizes: return "shared_region_size";
4,246,506✔
69
    case data_kind_t::stack_numeric_sizes: return "stack_numeric_size";
6,736,176✔
70
    case data_kind_t::stack_offsets: return "stack_offset";
6,735,068✔
71
    case data_kind_t::svalues: return "svalue";
2,543,006✔
72
    case data_kind_t::types: return "type";
2,484,946✔
73
    case data_kind_t::uvalues: return "uvalue";
2,501,902✔
74
    }
75
    return {};
×
76
}
77

78
data_kind_t regkind(const std::string& s) {
4,520✔
79
    static const std::map<std::string, data_kind_t> string_to_kind{
2,260✔
80
        {"type", data_kind_t::types},
39✔
81
        {"ctx_offset", data_kind_t::ctx_offsets},
39✔
82
        {"map_fd", data_kind_t::map_fds},
39✔
83
        {"packet_offset", data_kind_t::packet_offsets},
39✔
84
        {"shared_offset", data_kind_t::shared_offsets},
39✔
85
        {"stack_offset", data_kind_t::stack_offsets},
39✔
86
        {"shared_region_size", data_kind_t::shared_region_sizes},
39✔
87
        {"stack_numeric_size", data_kind_t::stack_numeric_sizes},
39✔
88
        {"svalue", data_kind_t::svalues},
39✔
89
        {"uvalue", data_kind_t::uvalues},
39✔
90
    };
5,417✔
91
    if (string_to_kind.contains(s)) {
4,520✔
92
        return string_to_kind.at(s);
4,520✔
93
    }
94
    throw std::runtime_error(std::string() + "Bad kind: " + s);
×
95
}
78✔
96

97
std::ostream& operator<<(std::ostream& os, const type_encoding_t s) {
1,994✔
98
    switch (s) {
1,994✔
99
    case T_SHARED: return os << S_SHARED;
100✔
100
    case T_STACK: return os << S_STACK;
242✔
101
    case T_PACKET: return os << S_PACKET;
104✔
102
    case T_CTX: return os << S_CTX;
98✔
103
    case T_NUM: return os << S_NUM;
1,426✔
104
    case T_MAP: return os << S_MAP;
16✔
105
    case T_MAP_PROGRAMS: return os << S_MAP_PROGRAMS;
6✔
106
    case T_UNINIT: return os << S_UNINIT;
2✔
107
    default: CRAB_ERROR("Unsupported type encoding", s);
×
108
    }
109
}
110

111
type_encoding_t string_to_type_encoding(const std::string& s) {
2,504✔
112
    static std::map<std::string, type_encoding_t> string_to_type{
1,252✔
113
        {S_UNINIT, T_UNINIT}, {S_MAP_PROGRAMS, T_MAP_PROGRAMS},
39✔
114
        {S_MAP, T_MAP},       {S_NUM, T_NUM},
78✔
115
        {S_CTX, T_CTX},       {S_STACK, T_STACK},
78✔
116
        {S_PACKET, T_PACKET}, {S_SHARED, T_SHARED},
78✔
117
    };
3,245✔
118
    if (string_to_type.contains(s)) {
2,504✔
119
        return string_to_type[s];
2,504✔
120
    }
121
    throw std::runtime_error(std::string("Unsupported type name: ") + s);
×
122
}
78✔
123
reg_pack_t reg_pack(const int i) {
1,201,822✔
124
    return {
600,910✔
125
        variable_t::reg(data_kind_t::svalues, i),
1,201,822✔
126
        variable_t::reg(data_kind_t::uvalues, i),
1,201,822✔
127
        variable_t::reg(data_kind_t::ctx_offsets, i),
1,201,822✔
128
        variable_t::reg(data_kind_t::map_fds, i),
1,201,822✔
129
        variable_t::reg(data_kind_t::packet_offsets, i),
1,201,822✔
130
        variable_t::reg(data_kind_t::shared_offsets, i),
1,201,822✔
131
        variable_t::reg(data_kind_t::stack_offsets, i),
1,201,822✔
132
        variable_t::reg(data_kind_t::types, i),
1,201,822✔
133
        variable_t::reg(data_kind_t::shared_region_sizes, i),
1,201,822✔
134
        variable_t::reg(data_kind_t::stack_numeric_sizes, i),
1,201,822✔
135
    };
1,201,822✔
136
}
137

138
void TypeDomain::add_extra_invariant(const NumAbsDomain& dst, std::map<variable_t, interval_t>& extra_invariants,
7,358,352✔
139
                                     const variable_t type_variable, const type_encoding_t type, const data_kind_t kind,
140
                                     const NumAbsDomain& src) const {
141
    const bool dst_has_type = has_type(dst, type_variable, type);
7,358,352✔
142
    const bool src_has_type = has_type(src, type_variable, type);
7,358,352✔
143
    variable_t v = variable_t::kind_var(kind, type_variable);
7,358,352✔
144

145
    // If type is contained in exactly one of dst or src,
146
    // we need to remember the value.
147
    if (dst_has_type && !src_has_type) {
7,358,352✔
148
        extra_invariants.emplace(v, dst.eval_interval(v));
638,547✔
149
    } else if (!dst_has_type && src_has_type) {
6,932,654✔
150
        extra_invariants.emplace(v, src.eval_interval(v));
78,270✔
151
    }
152
}
7,358,352✔
153

154
void TypeDomain::selectively_join_based_on_type(NumAbsDomain& dst, NumAbsDomain&& src) const {
111,292✔
155
    // Some variables are type-specific.  Type-specific variables
156
    // for a register can exist in the domain whenever the associated
157
    // type value is present in the register's types interval (and the
158
    // value is not Top), and are absent otherwise.  That is, we want
159
    // to keep track of implications of the form
160
    // "if register R has type=T then R.T_offset has value ...".
161
    //
162
    // If a type value is legal in exactly one of the two domains, a
163
    // normal join operation would remove any type-specific variables
164
    // from the resulting merged domain since absence from the other
165
    // would be interpreted to mean Top.
166
    //
167
    // However, when the type value is not present in one domain,
168
    // any type-specific variables for that type are instead to be
169
    // interpreted as Bottom, so we want to preserve the values of any
170
    // type-specific variables from the other domain where the type
171
    // value is legal.
172
    //
173
    // Example input:
174
    //   r1.type=stack, r1.stack_offset=100
175
    //   r1.type=packet, r1.packet_offset=4
176
    // Output:
177
    //   r1.type={stack,packet}, r1.stack_offset=100, r1.packet_offset=4
178

179
    std::map<variable_t, interval_t> extra_invariants;
111,292✔
180
    if (!dst.is_bottom()) {
111,292✔
181
        for (const variable_t v : variable_t::get_type_variables()) {
936,752✔
182
            add_extra_invariant(dst, extra_invariants, v, T_CTX, data_kind_t::ctx_offsets, src);
919,794✔
183
            add_extra_invariant(dst, extra_invariants, v, T_MAP, data_kind_t::map_fds, src);
919,794✔
184
            add_extra_invariant(dst, extra_invariants, v, T_MAP_PROGRAMS, data_kind_t::map_fds, src);
919,794✔
185
            add_extra_invariant(dst, extra_invariants, v, T_PACKET, data_kind_t::packet_offsets, src);
919,794✔
186
            add_extra_invariant(dst, extra_invariants, v, T_SHARED, data_kind_t::shared_offsets, src);
919,794✔
187
            add_extra_invariant(dst, extra_invariants, v, T_STACK, data_kind_t::stack_offsets, src);
919,794✔
188
            add_extra_invariant(dst, extra_invariants, v, T_SHARED, data_kind_t::shared_region_sizes, src);
919,794✔
189
            add_extra_invariant(dst, extra_invariants, v, T_STACK, data_kind_t::stack_numeric_sizes, src);
919,794✔
190
        }
16,958✔
191
    }
192

193
    // Do a normal join operation on the domain.
194
    dst |= std::move(src);
111,292✔
195

196
    // Now add in the extra invariants saved above.
197
    for (const auto& [variable, interval] : extra_invariants) {
532,946✔
198
        dst.set(variable, interval);
421,654✔
199
    }
200
}
111,292✔
201

202
void TypeDomain::assign_type(NumAbsDomain& inv, const Reg& lhs, const Reg& rhs) {
27,260✔
203
    inv.assign(reg_pack(lhs).type, reg_pack(rhs).type);
27,260✔
204
}
27,260✔
205

206
void TypeDomain::assign_type(NumAbsDomain& inv, const std::optional<variable_t> lhs, const linear_expression_t& t) {
24,206✔
207
    inv.assign(lhs, t);
24,206✔
208
}
24,206✔
209

210
void TypeDomain::assign_type(NumAbsDomain& inv, const Reg& lhs, const std::optional<linear_expression_t>& rhs) {
83,231✔
211
    inv.assign(reg_pack(lhs).type, rhs);
83,231✔
212
}
83,231✔
213

214
void TypeDomain::havoc_type(NumAbsDomain& inv, const Reg& r) { inv -= reg_pack(r).type; }
68,742✔
215

216
type_encoding_t TypeDomain::get_type(const NumAbsDomain& inv, const linear_expression_t& v) const {
32,092✔
217
    const auto res = inv.eval_interval(v).singleton();
32,092✔
218
    if (!res) {
32,092✔
219
        return T_UNINIT;
121✔
220
    }
221
    return res->narrow<type_encoding_t>();
31,850✔
222
}
32,092✔
223

224
type_encoding_t TypeDomain::get_type(const NumAbsDomain& inv, const Reg& r) const {
10✔
225
    const auto res = inv[reg_pack(r).type].singleton();
10✔
226
    if (!res) {
10✔
227
        return T_UNINIT;
3✔
228
    }
229
    return res->narrow<type_encoding_t>();
4✔
230
}
10✔
231

232
// Check whether a given type value is within the range of a given type variable's value.
233
bool TypeDomain::has_type(const NumAbsDomain& inv, const Reg& r, const type_encoding_t type) const {
12,544✔
234
    const interval_t interval = inv[reg_pack(r).type];
12,544✔
235
    return interval.contains(type);
18,816✔
236
}
12,544✔
237

238
bool TypeDomain::has_type(const NumAbsDomain& inv, const linear_expression_t& v, const type_encoding_t type) const {
16,748,445✔
239
    const interval_t interval = inv.eval_interval(v);
16,748,445✔
240
    return interval.contains(type);
25,122,667✔
241
}
16,748,445✔
242

243
NumAbsDomain TypeDomain::join_over_types(const NumAbsDomain& inv, const Reg& reg,
94,450✔
244
                                         const std::function<void(NumAbsDomain&, type_encoding_t)>& transition) const {
245
    interval_t types = inv.eval_interval(reg_pack(reg).type);
94,450✔
246
    if (types.is_bottom()) {
94,450✔
247
        return NumAbsDomain::bottom();
×
248
    }
249
    if (types.is_top()) {
94,450✔
250
        NumAbsDomain res(inv);
118✔
251
        transition(res, T_UNINIT);
118✔
252
        return res;
118✔
253
    }
118✔
254
    NumAbsDomain res = NumAbsDomain::bottom();
94,332✔
255
    auto [lb, ub] = types.bound(T_MIN, T_MAX);
94,332✔
256
    for (type_encoding_t type : iterate_types(lb, ub)) {
188,736✔
257
        NumAbsDomain tmp(inv);
94,404✔
258
        transition(tmp, type);
94,404✔
259
        selectively_join_based_on_type(res, std::move(tmp)); // res |= tmp;
94,404✔
260
    }
188,736✔
261
    return res;
94,332✔
262
}
141,616✔
263

264
NumAbsDomain TypeDomain::join_by_if_else(const NumAbsDomain& inv, const linear_constraint_t& condition,
×
265
                                         const std::function<void(NumAbsDomain&)>& if_true,
266
                                         const std::function<void(NumAbsDomain&)>& if_false) const {
267
    NumAbsDomain true_case(inv.when(condition));
×
268
    if_true(true_case);
×
269

270
    NumAbsDomain false_case(inv.when(condition.negate()));
×
271
    if_false(false_case);
×
272

273
    return true_case | false_case;
×
274
}
×
275

276
static linear_constraint_t eq_types(const Reg& a, const Reg& b) {
14,640✔
277
    using namespace crab::dsl_syntax;
7,320✔
278
    return eq(reg_pack(a).type, reg_pack(b).type);
14,640✔
279
}
280

281
bool TypeDomain::same_type(const NumAbsDomain& inv, const Reg& a, const Reg& b) const {
14,640✔
282
    return inv.entail(eq_types(a, b));
21,960✔
283
}
284

285
bool TypeDomain::implies_type(const NumAbsDomain& inv, const linear_constraint_t& a,
2,308✔
286
                              const linear_constraint_t& b) const {
287
    return inv.when(a).entail(b);
3,462✔
288
}
289

290
bool TypeDomain::is_in_group(const NumAbsDomain& inv, const Reg& r, const TypeGroup group) const {
76,238✔
291
    using namespace crab::dsl_syntax;
38,119✔
292
    const variable_t t = reg_pack(r).type;
76,238✔
293
    switch (group) {
76,238✔
294
    case TypeGroup::number: return inv.entail(t == T_NUM);
56,673✔
295
    case TypeGroup::map_fd: return inv.entail(t == T_MAP);
3,642✔
296
    case TypeGroup::map_fd_programs: return inv.entail(t == T_MAP_PROGRAMS);
366✔
297
    case TypeGroup::ctx: return inv.entail(t == T_CTX);
6,306✔
298
    case TypeGroup::packet: return inv.entail(t == T_PACKET);
×
299
    case TypeGroup::stack: return inv.entail(t == T_STACK);
×
300
    case TypeGroup::shared: return inv.entail(t == T_SHARED);
×
301
    case TypeGroup::mem: return inv.entail(t >= T_PACKET);
9,364✔
302
    case TypeGroup::mem_or_num: return inv.entail(t >= T_NUM) && inv.entail(t != T_CTX);
2,100✔
303
    case TypeGroup::pointer: return inv.entail(t >= T_CTX);
23,296✔
304
    case TypeGroup::ptr_or_num: return inv.entail(t >= T_NUM);
22,140✔
305
    case TypeGroup::stack_or_packet: return inv.entail(t >= T_PACKET) && inv.entail(t <= T_STACK);
×
306
    case TypeGroup::singleton_ptr: return inv.entail(t >= T_CTX) && inv.entail(t <= T_STACK);
5,467✔
307
    default: CRAB_ERROR("Unsupported type group", group);
×
308
    }
309
}
310

311
std::string typeset_to_string(const std::vector<type_encoding_t>& items) {
50✔
312
    std::stringstream ss;
50✔
313
    ss << "{";
50✔
314
    for (auto it = items.begin(); it != items.end(); ++it) {
186✔
315
        ss << *it;
136✔
316
        if (std::next(it) != items.end()) {
204✔
317
            ss << ", ";
86✔
318
        }
319
    }
320
    ss << "}";
50✔
321
    return ss.str();
100✔
322
}
50✔
323

324
bool is_singleton_type(const TypeGroup t) {
1,920✔
325
    switch (t) {
1,920✔
326
    case TypeGroup::number:
934✔
327
    case TypeGroup::map_fd:
328
    case TypeGroup::map_fd_programs:
329
    case TypeGroup::ctx:
330
    case TypeGroup::packet:
331
    case TypeGroup::stack:
332
    case TypeGroup::shared: return true;
934✔
333
    default: return false;
52✔
334
    }
335
}
336

337
std::ostream& operator<<(std::ostream& os, const TypeGroup ts) {
3,772✔
338
    using namespace crab;
1,886✔
339
    static const std::map<TypeGroup, std::string> string_to_type{
1,886✔
340
        {TypeGroup::number, S_NUM},
2✔
341
        {TypeGroup::map_fd, S_MAP},
2✔
342
        {TypeGroup::map_fd_programs, S_MAP_PROGRAMS},
2✔
343
        {TypeGroup::ctx, S_CTX},
2✔
344
        {TypeGroup::packet, S_PACKET},
2✔
345
        {TypeGroup::stack, S_STACK},
2✔
346
        {TypeGroup::shared, S_SHARED},
2✔
347
        {TypeGroup::mem, typeset_to_string({T_STACK, T_PACKET, T_SHARED})},
8✔
348
        {TypeGroup::pointer, typeset_to_string({T_CTX, T_STACK, T_PACKET, T_SHARED})},
8✔
349
        {TypeGroup::ptr_or_num, typeset_to_string({T_NUM, T_CTX, T_STACK, T_PACKET, T_SHARED})},
8✔
350
        {TypeGroup::stack_or_packet, typeset_to_string({T_STACK, T_PACKET})},
8✔
351
        {TypeGroup::singleton_ptr, typeset_to_string({T_CTX, T_STACK, T_PACKET})},
8✔
352
        {TypeGroup::mem_or_num, typeset_to_string({T_NUM, T_STACK, T_PACKET, T_SHARED})},
8✔
353
    };
3,834✔
354
    if (string_to_type.contains(ts)) {
3,772✔
355
        return os << string_to_type.at(ts);
3,772✔
356
    }
357
    CRAB_ERROR("Unsupported type group", ts);
×
358
}
28✔
359

360
} // namespace crab
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