• 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

96.24
/src/crab/ebpf_transformer.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 <bitset>
7
#include <optional>
8
#include <utility>
9
#include <vector>
10

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

13
#include "asm_unmarshal.hpp"
14
#include "config.hpp"
15
#include "crab/array_domain.hpp"
16
#include "crab/ebpf_domain.hpp"
17
#include "crab_utils/num_safety.hpp"
18
#include "dsl_syntax.hpp"
19
#include "platform.hpp"
20
#include "string_constraints.hpp"
21

22
using crab::domains::NumAbsDomain;
23
namespace crab {
24

25
class ebpf_transformer final {
26
    ebpf_domain_t& dom;
27
    // shorthands:
28
    NumAbsDomain& m_inv;
29
    domains::array_domain_t& stack;
30
    TypeDomain& type_inv;
31

32
  public:
33
    explicit ebpf_transformer(ebpf_domain_t& dom)
247,776✔
34
        : dom(dom), m_inv(dom.m_inv), stack(dom.stack), type_inv(dom.type_inv) {}
247,776✔
35

36
    // abstract transformers
37
    void operator()(const Assume&);
38
    void operator()(const Atomic&);
39
    void operator()(const Bin&);
40
    void operator()(const Call&);
41
    void operator()(const CallLocal&);
42
    void operator()(const Callx&);
43
    void operator()(const Exit&);
44
    void operator()(const IncrementLoopCounter&);
45
    void operator()(const Jmp&) const;
46
    void operator()(const LoadMapFd&);
47
    void operator()(const Mem&);
48
    void operator()(const Packet&);
49
    void operator()(const Un&);
50
    void operator()(const Undefined&);
51

52
    void initialize_loop_counter(const label_t& label);
53

54
    static ebpf_domain_t setup_entry(bool init_r1);
55

56
  private:
57
    void assign(variable_t lhs, variable_t rhs);
58
    void assign(variable_t x, const linear_expression_t& e);
59
    void assign(variable_t x, int64_t e);
60

61
    void apply(arith_binop_t op, variable_t x, variable_t y, const number_t& z, int finite_width);
62
    void apply(arith_binop_t op, variable_t x, variable_t y, variable_t z, int finite_width);
63
    void apply(bitwise_binop_t op, variable_t x, variable_t y, variable_t z, int finite_width);
64
    void apply(bitwise_binop_t op, variable_t x, variable_t y, const number_t& k, int finite_width);
65
    void apply(binop_t op, variable_t x, variable_t y, const number_t& z, int finite_width);
66
    void apply(binop_t op, variable_t x, variable_t y, variable_t z, int finite_width);
67

68
    void add(const Reg& reg, int imm, int finite_width);
69
    void add(variable_t lhs, variable_t op2);
70
    void add(variable_t lhs, const number_t& op2);
71
    void sub(variable_t lhs, variable_t op2);
72
    void sub(variable_t lhs, const number_t& op2);
73
    void add_overflow(variable_t lhss, variable_t lhsu, variable_t op2, int finite_width);
74
    void add_overflow(variable_t lhss, variable_t lhsu, const number_t& op2, int finite_width);
75
    void sub_overflow(variable_t lhss, variable_t lhsu, variable_t op2, int finite_width);
76
    void sub_overflow(variable_t lhss, variable_t lhsu, const number_t& op2, int finite_width);
77
    void neg(variable_t lhss, variable_t lhsu, int finite_width);
78
    void mul(variable_t lhss, variable_t lhsu, variable_t op2, int finite_width);
79
    void mul(variable_t lhss, variable_t lhsu, const number_t& op2, int finite_width);
80
    void sdiv(variable_t lhss, variable_t lhsu, variable_t op2, int finite_width);
81
    void sdiv(variable_t lhss, variable_t lhsu, const number_t& op2, int finite_width);
82
    void udiv(variable_t lhss, variable_t lhsu, variable_t op2, int finite_width);
83
    void udiv(variable_t lhss, variable_t lhsu, const number_t& op2, int finite_width);
84
    void srem(variable_t lhss, variable_t lhsu, variable_t op2, int finite_width);
85
    void srem(variable_t lhss, variable_t lhsu, const number_t& op2, int finite_width);
86
    void urem(variable_t lhss, variable_t lhsu, variable_t op2, int finite_width);
87
    void urem(variable_t lhss, variable_t lhsu, const number_t& op2, int finite_width);
88

89
    void bitwise_and(variable_t lhss, variable_t lhsu, variable_t op2, int finite_width);
90
    void bitwise_and(variable_t lhss, variable_t lhsu, const number_t& op2);
91
    void bitwise_or(variable_t lhss, variable_t lhsu, variable_t op2, int finite_width);
92
    void bitwise_or(variable_t lhss, variable_t lhsu, const number_t& op2);
93
    void bitwise_xor(variable_t lhsss, variable_t lhsu, variable_t op2, int finite_width);
94
    void bitwise_xor(variable_t lhss, variable_t lhsu, const number_t& op2);
95
    void shl(const Reg& reg, int imm, int finite_width);
96
    void shl_overflow(variable_t lhss, variable_t lhsu, variable_t op2);
97
    void shl_overflow(variable_t lhss, variable_t lhsu, const number_t& op2);
98
    void lshr(const Reg& reg, int imm, int finite_width);
99
    void ashr(const Reg& dst_reg, const linear_expression_t& right_svalue, int finite_width);
100
    void sign_extend(const Reg& dst_reg, const linear_expression_t& right_svalue, int finite_width, Bin::Op op);
101

102
    void assume(const linear_constraint_t& cst);
103

104
    /// Forget everything we know about the value of a variable.
105
    void havoc(variable_t v);
106

107
    /// Forget everything about all offset variables for a given register.
108
    void havoc_offsets(const Reg& reg);
109

110
    void scratch_caller_saved_registers();
111
    void save_callee_saved_registers(const std::string& prefix);
112
    void restore_callee_saved_registers(const std::string& prefix);
113
    void havoc_subprogram_stack(const std::string& prefix);
114
    void forget_packet_pointers();
115
    void do_load_mapfd(const Reg& dst_reg, int mapfd, bool maybe_null);
116

117
    void assign_valid_ptr(const Reg& dst_reg, bool maybe_null);
118

119
    void recompute_stack_numeric_size(NumAbsDomain& inv, const Reg& reg) const;
120
    void recompute_stack_numeric_size(NumAbsDomain& inv, variable_t type_variable) const;
121
    void do_load_stack(NumAbsDomain& inv, const Reg& target_reg, const linear_expression_t& addr, int width,
122
                       const Reg& src_reg);
123
    void do_load_ctx(NumAbsDomain& inv, const Reg& target_reg, const linear_expression_t& addr_vague, int width);
124
    void do_load_packet_or_shared(NumAbsDomain& inv, const Reg& target_reg, const linear_expression_t& addr, int width);
125
    void do_load(const Mem& b, const Reg& target_reg);
126

127
    void do_store_stack(NumAbsDomain& inv, const linear_expression_t& addr, int width,
128
                        const linear_expression_t& val_type, const linear_expression_t& val_svalue,
129
                        const linear_expression_t& val_uvalue, const std::optional<reg_pack_t>& opt_val_reg);
130

131
    void do_mem_store(const Mem& b, const linear_expression_t& val_type, const linear_expression_t& val_svalue,
132
                      const linear_expression_t& val_uvalue, const std::optional<reg_pack_t>& opt_val_reg);
133
}; // end ebpf_domain_t
134

135
void ebpf_domain_transform(ebpf_domain_t& inv, const Instruction& ins) {
249,802✔
136
    if (inv.is_bottom()) {
249,802✔
137
        return;
1,029✔
138
    }
139
    std::visit(ebpf_transformer{inv}, ins);
247,744✔
140
    if (inv.is_bottom() && !std::holds_alternative<Assume>(ins)) {
247,742✔
141
        // Fail. raise an exception to stop the analysis.
142
        std::stringstream msg;
36✔
143
        msg << "Bug! pre-invariant:\n"
36✔
144
            << inv << "\n followed by instruction: " << ins << "\n"
36✔
145
            << "leads to bottom";
36✔
146
        throw std::logic_error(msg.str());
54✔
147
    }
36✔
148
}
149

150
/** Linear constraint for a pointer comparison.
151
 */
152
static linear_constraint_t assume_cst_offsets_reg(const Condition::Op op, const variable_t dst_offset,
1,620✔
153
                                                  const variable_t src_offset) {
154
    using namespace crab::dsl_syntax;
810✔
155
    using Op = Condition::Op;
810✔
156
    switch (op) {
1,620✔
157
    case Op::EQ: return eq(dst_offset, src_offset);
6✔
158
    case Op::NE: return neq(dst_offset, src_offset);
12✔
159
    case Op::GE: return dst_offset >= src_offset;
6✔
160
    case Op::SGE: return dst_offset >= src_offset; // pointer comparison is unsigned
×
161
    case Op::LE: return dst_offset <= src_offset;
796✔
162
    case Op::SLE: return dst_offset <= src_offset; // pointer comparison is unsigned
×
163
    case Op::GT: return dst_offset > src_offset;
1,194✔
164
    case Op::SGT: return dst_offset > src_offset; // pointer comparison is unsigned
×
165
    case Op::SLT: return src_offset > dst_offset;
×
166
    // Note: reverse the test as a workaround strange lookup:
167
    case Op::LT: return src_offset > dst_offset; // FIX unsigned
9✔
168
    default: return dst_offset - dst_offset == 0;
×
169
    }
170
}
171

172
static std::vector<linear_constraint_t> assume_bit_cst_interval(const NumAbsDomain& inv, Condition::Op op, bool is64,
52✔
173
                                                                variable_t dst_uvalue, interval_t src_interval) {
174
    using namespace crab::dsl_syntax;
26✔
175
    using Op = Condition::Op;
26✔
176

177
    auto dst_interval = inv.eval_interval(dst_uvalue);
52✔
178
    std::optional<number_t> dst_n = dst_interval.singleton();
52✔
179
    if (!dst_n || !dst_n.value().fits_cast_to<int64_t>()) {
52✔
180
        return {};
8✔
181
    }
182

183
    std::optional<number_t> src_n = src_interval.singleton();
44✔
184
    if (!src_n || !src_n->fits_cast_to<int64_t>()) {
44✔
185
        return {};
×
186
    }
187
    uint64_t src_int_value = src_n.value().cast_to<uint64_t>();
44✔
188
    if (!is64) {
44✔
189
        src_int_value = gsl::narrow_cast<uint32_t>(src_int_value);
20✔
190
    }
191

192
    bool result;
22✔
193
    switch (op) {
44✔
194
    case Op::SET: result = (dst_n.value().cast_to<uint64_t>() & src_int_value) != 0; break;
22✔
195
    case Op::NSET: result = (dst_n.value().cast_to<uint64_t>() & src_int_value) == 0; break;
22✔
196
    default: throw std::exception();
×
197
    }
198

199
    return {result ? linear_constraint_t::true_const() : linear_constraint_t::false_const()};
132✔
200
}
144✔
201

202
static std::vector<linear_constraint_t> assume_signed_64bit_eq(const NumAbsDomain& inv, const variable_t left_svalue,
8,350✔
203
                                                               const variable_t left_uvalue,
204
                                                               const interval_t& right_interval,
205
                                                               const linear_expression_t& right_svalue,
206
                                                               const linear_expression_t& right_uvalue) {
207
    using namespace crab::dsl_syntax;
4,175✔
208
    if (right_interval <= interval_t::nonnegative(64) && !right_interval.is_singleton()) {
16,696✔
209
        return {(left_svalue == right_svalue), (left_uvalue == right_uvalue), eq(left_svalue, left_uvalue)};
×
210
    } else {
211
        return {(left_svalue == right_svalue), (left_uvalue == right_uvalue)};
29,225✔
212
    }
213
}
16,700✔
214

215
static std::vector<linear_constraint_t> assume_signed_32bit_eq(const NumAbsDomain& inv, const variable_t left_svalue,
4,124✔
216
                                                               const variable_t left_uvalue,
217
                                                               const interval_t& right_interval) {
218
    using namespace crab::dsl_syntax;
2,062✔
219

220
    if (const auto rn = right_interval.singleton()) {
4,124✔
221
        const auto left_svalue_interval = inv.eval_interval(left_svalue);
3,962✔
222
        if (auto size = left_svalue_interval.finite_size()) {
3,962✔
223
            // Find the lowest 64-bit svalue whose low 32 bits match the singleton.
224

225
            // Get lower bound as a 64-bit value.
226
            int64_t lb = left_svalue_interval.lb().number()->cast_to<int64_t>();
5,656✔
227

228
            // Use the high 32-bits from the left lower bound and the low 32-bits from the right singleton.
229
            // The result might be lower than the lower bound.
230
            const int64_t lb_match = (lb & 0xFFFFFFFF00000000) | (rn->cast_to<int64_t>() & 0xFFFFFFFF);
2,828✔
231
            if (lb_match < lb) {
2,828✔
232
                // The result is lower than the left interval, so try the next higher matching 64-bit value.
233
                // It's ok if this goes higher than the left upper bound.
234
                lb += 0x100000000;
1,546✔
235
            }
236

237
            // Find the highest 64-bit svalue whose low 32 bits match the singleton.
238

239
            // Get upper bound as a 64-bit value.
240
            const int64_t ub = left_svalue_interval.ub().number()->cast_to<int64_t>();
5,656✔
241

242
            // Use the high 32-bits from the left upper bound and the low 32-bits from the right singleton.
243
            // The result might be higher than the upper bound.
244
            const int64_t ub_match = (ub & 0xFFFFFFFF00000000) | (rn->cast_to<int64_t>() & 0xFFFFFFFF);
2,828✔
245
            if (ub_match > ub) {
2,828✔
246
                // The result is higher than the left interval, so try the next lower matching 64-bit value.
247
                // It's ok if this goes lower than the left lower bound.
248
                lb -= 0x100000000;
1,429✔
249
            }
250

251
            if (to_unsigned(lb_match) <= to_unsigned(ub_match)) {
2,828✔
252
                // The interval is also valid when cast to a uvalue, meaning
253
                // both bounds are positive or both are negative.
254
                return {left_svalue >= lb_match, left_svalue <= ub_match, left_uvalue >= to_unsigned(lb_match),
1,289✔
255
                        left_uvalue <= to_unsigned(ub_match)};
14,179✔
256
            } else {
257
                // The interval can only be represented as an svalue.
258
                return {left_svalue >= lb_match, left_svalue <= ub_match};
875✔
259
            }
260
        }
3,962✔
261
    }
6,024✔
262
    return {};
1,296✔
263
}
8,359✔
264

265
// Given left and right values, get the left and right intervals, and also split
266
// the left interval into separate negative and positive intervals.
267
static void get_signed_intervals(const NumAbsDomain& inv, bool is64, const variable_t left_svalue,
15,536✔
268
                                 const variable_t left_uvalue, const linear_expression_t& right_svalue,
269
                                 interval_t& left_interval, interval_t& right_interval,
270
                                 interval_t& left_interval_positive, interval_t& left_interval_negative) {
271
    using crab::interval_t;
7,768✔
272
    using namespace crab::dsl_syntax;
7,768✔
273

274
    // Get intervals as 32-bit or 64-bit as appropriate.
275
    left_interval = inv.eval_interval(left_svalue);
23,304✔
276
    right_interval = inv.eval_interval(right_svalue);
15,536✔
277
    if (!is64) {
15,536✔
278
        if ((left_interval <= interval_t::nonnegative(32) && right_interval <= interval_t::nonnegative(32)) ||
16,880✔
279
            (left_interval <= interval_t::negative(32) && right_interval <= interval_t::negative(32))) {
10,274✔
280
            is64 = true;
8,567✔
281
            // fallthrough as 64bit, including deduction of relational information
282
        } else {
283
            left_interval = left_interval.truncate_to<int32_t>();
3,448✔
284
            right_interval = right_interval.truncate_to<int32_t>();
5,172✔
285
            // continue as 32bit
286
        }
287
    }
288

289
    if (!left_interval.is_top()) {
15,536✔
290
        left_interval_positive = left_interval & interval_t::nonnegative(64);
20,652✔
291
        left_interval_negative = left_interval & interval_t::negative(64);
27,536✔
292
    } else {
293
        left_interval = inv.eval_interval(left_uvalue);
2,652✔
294
        if (!left_interval.is_top()) {
1,768✔
295
            // The interval is TOP as a signed interval but is represented precisely as an unsigned interval,
296
            // so split into two signed intervals that can be treated separately.
297
            left_interval_positive = left_interval & interval_t::nonnegative(64);
318✔
298
            const number_t lih_ub =
106✔
299
                left_interval.ub().number() ? left_interval.ub().number()->truncate_to<int64_t>() : -1;
568✔
300
            left_interval_negative = interval_t{std::numeric_limits<int64_t>::min(), lih_ub};
424✔
301
        } else {
212✔
302
            left_interval_positive = interval_t::nonnegative(64);
1,556✔
303
            left_interval_negative = interval_t::negative(64);
2,334✔
304
        }
305
    }
306

307
    left_interval = left_interval.truncate_to<int64_t>();
15,536✔
308
    right_interval = right_interval.truncate_to<int64_t>();
15,536✔
309
}
15,536✔
310

311
// Given left and right values, get the left and right intervals, and also split
312
// the left interval into separate low and high intervals.
313
static void get_unsigned_intervals(const NumAbsDomain& inv, bool is64, const variable_t left_svalue,
15,634✔
314
                                   const variable_t left_uvalue, const linear_expression_t& right_uvalue,
315
                                   interval_t& left_interval, interval_t& right_interval, interval_t& left_interval_low,
316
                                   interval_t& left_interval_high) {
317
    using crab::interval_t;
7,817✔
318
    using namespace crab::dsl_syntax;
7,817✔
319

320
    // Get intervals as 32-bit or 64-bit as appropriate.
321
    left_interval = inv.eval_interval(left_uvalue);
23,451✔
322
    right_interval = inv.eval_interval(right_uvalue);
15,634✔
323
    if (!is64) {
15,634✔
324
        if ((left_interval <= interval_t::nonnegative(32) && right_interval <= interval_t::nonnegative(32)) ||
15,451✔
325
            (left_interval <= interval_t::unsigned_high(32) && right_interval <= interval_t::unsigned_high(32))) {
9,038✔
326
            is64 = true;
8,692✔
327
            // fallthrough as 64bit, including deduction of relational information
328
        } else {
329
            left_interval = left_interval.truncate_to<uint32_t>();
2,914✔
330
            right_interval = right_interval.truncate_to<uint32_t>();
4,371✔
331
            // continue as 32bit
332
        }
333
    }
334

335
    if (!left_interval.is_top()) {
15,634✔
336
        left_interval_low = left_interval & interval_t::nonnegative(64);
20,358✔
337
        left_interval_high = left_interval & interval_t::unsigned_high(64);
27,144✔
338
    } else {
339
        left_interval = inv.eval_interval(left_svalue);
3,093✔
340
        if (!left_interval.is_top()) {
2,062✔
341
            // The interval is TOP as an unsigned interval but is represented precisely as a signed interval,
342
            // so split into two unsigned intervals that can be treated separately.
343
            left_interval_low = interval_t(0, left_interval.ub()).truncate_to<uint64_t>();
272✔
344
            left_interval_high = interval_t(left_interval.lb(), -1).truncate_to<uint64_t>();
340✔
345
        } else {
346
            left_interval_low = interval_t::nonnegative(64);
1,926✔
347
            left_interval_high = interval_t::unsigned_high(64);
2,889✔
348
        }
349
    }
350

351
    left_interval = left_interval.truncate_to<uint64_t>();
15,634✔
352
    right_interval = right_interval.truncate_to<uint64_t>();
15,634✔
353
}
15,634✔
354

355
static std::vector<linear_constraint_t>
356
assume_signed_64bit_lt(const bool strict, const variable_t left_svalue, const variable_t left_uvalue,
756✔
357
                       const interval_t& left_interval_positive, const interval_t& left_interval_negative,
358
                       const linear_expression_t& right_svalue, const linear_expression_t& right_uvalue,
359
                       const interval_t& right_interval) {
360
    using crab::interval_t;
378✔
361
    using namespace crab::dsl_syntax;
378✔
362

363
    if (right_interval <= interval_t::negative(64)) {
1,134✔
364
        // Interval can be represented as both an svalue and a uvalue since it fits in [INT_MIN, -1].
365
        return {strict ? left_svalue < right_svalue : left_svalue <= right_svalue, 0 <= left_uvalue,
102✔
366
                strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue};
510✔
367
    } else if ((left_interval_negative | left_interval_positive) <= interval_t::nonnegative(64) &&
1,683✔
368
               right_interval <= interval_t::nonnegative(64)) {
750✔
369
        // Interval can be represented as both an svalue and a uvalue since it fits in [0, INT_MAX].
370
        return {strict ? left_svalue < right_svalue : left_svalue <= right_svalue, 0 <= left_uvalue,
96✔
371
                strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue};
480✔
372
    } else {
373
        // Interval can only be represented as an svalue.
374
        return {strict ? left_svalue < right_svalue : left_svalue <= right_svalue};
1,674✔
375
    }
376
}
1,710✔
377

378
static std::vector<linear_constraint_t>
379
assume_signed_32bit_lt(const NumAbsDomain& inv, const bool strict, const variable_t left_svalue,
376✔
380
                       const variable_t left_uvalue, const interval_t& left_interval_positive,
381
                       const interval_t& left_interval_negative, const linear_expression_t& right_svalue,
382
                       const linear_expression_t& right_uvalue, const interval_t& right_interval) {
383
    using crab::interval_t;
188✔
384
    using namespace crab::dsl_syntax;
188✔
385

386
    if (right_interval <= interval_t::negative(32)) {
564✔
387
        // Interval can be represented as both an svalue and a uvalue since it fits in [INT_MIN, -1],
388
        // aka [INT_MAX+1, UINT_MAX].
389
        return {std::numeric_limits<int32_t>::max() < left_uvalue,
17✔
390
                strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue,
34✔
391
                strict ? left_svalue < right_svalue : left_svalue <= right_svalue};
170✔
392
    } else if ((left_interval_negative | left_interval_positive) <= interval_t::nonnegative(32) &&
870✔
393
               right_interval <= interval_t::nonnegative(32)) {
372✔
394
        // Interval can be represented as both an svalue and a uvalue since it fits in [0, INT_MAX]
395
        const auto lpub = left_interval_positive.truncate_to<int32_t>().ub();
45✔
396
        return {left_svalue >= 0,
15✔
397
                strict ? left_svalue < right_svalue : left_svalue <= right_svalue,
30✔
398
                left_svalue <= left_uvalue,
399
                left_svalue >= left_uvalue,
400
                left_uvalue >= 0,
401
                strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue,
30✔
402
                left_uvalue <= *lpub.number()};
285✔
403
    } else if (inv.eval_interval(left_svalue) <= interval_t::signed_int(32) &&
933✔
404
               inv.eval_interval(right_svalue) <= interval_t::signed_int(32)) {
726✔
405
        // Interval can only be represented as an svalue.
406
        return {strict ? left_svalue < right_svalue : left_svalue <= right_svalue};
828✔
407
    } else {
408
        // We can't directly compare the svalues since they may differ in high order bits.
409
        return {};
36✔
410
    }
411
}
806✔
412

413
static std::vector<linear_constraint_t>
414
assume_signed_64bit_gt(const bool strict, const variable_t left_svalue, const variable_t left_uvalue,
772✔
415
                       const interval_t& left_interval_positive, const interval_t& left_interval_negative,
416
                       const linear_expression_t& right_svalue, const linear_expression_t& right_uvalue,
417
                       const interval_t& right_interval) {
418
    using crab::interval_t;
386✔
419
    using namespace crab::dsl_syntax;
386✔
420

421
    if (right_interval <= interval_t::nonnegative(64)) {
1,158✔
422
        // Interval can be represented as both an svalue and a uvalue since it fits in [0, INT_MAX].
423
        const auto lpub = left_interval_positive.truncate_to<int64_t>().ub();
984✔
424
        return {left_svalue >= 0,
328✔
425
                strict ? left_svalue > right_svalue : left_svalue >= right_svalue,
656✔
426
                left_svalue <= left_uvalue,
427
                left_svalue >= left_uvalue,
428
                left_uvalue >= 0,
429
                strict ? left_uvalue > right_uvalue : left_uvalue >= right_uvalue,
656✔
430
                left_uvalue <= *lpub.number()};
6,232✔
431
    } else if ((left_interval_negative | left_interval_positive) <= interval_t::negative(64) &&
619✔
432
               right_interval <= interval_t::negative(64)) {
118✔
433
        // Interval can be represented as both an svalue and a uvalue since it fits in [INT_MIN, -1],
434
        // aka [INT_MAX+1, UINT_MAX].
435
        return {std::numeric_limits<int64_t>::max() < left_uvalue,
1✔
436
                strict ? left_uvalue > right_uvalue : left_uvalue >= right_uvalue,
2✔
437
                strict ? left_svalue > right_svalue : left_svalue >= right_svalue};
9✔
438
    } else {
439
        // Interval can only be represented as an svalue.
440
        return {strict ? left_svalue > right_svalue : left_svalue >= right_svalue};
342✔
441
    }
442
}
3,185✔
443

444
static std::vector<linear_constraint_t>
445
assume_signed_32bit_gt(const NumAbsDomain& inv, const bool strict, const variable_t left_svalue,
378✔
446
                       const variable_t left_uvalue, const interval_t& left_interval_positive,
447
                       const interval_t& left_interval_negative, const linear_expression_t& right_svalue,
448
                       const linear_expression_t& right_uvalue, const interval_t& right_interval) {
449
    using crab::interval_t;
189✔
450
    using namespace crab::dsl_syntax;
189✔
451

452
    if (right_interval <= interval_t::nonnegative(32)) {
567✔
453
        // Interval can be represented as both an svalue and a uvalue since it fits in [0, INT_MAX].
454
        const auto lpub = left_interval_positive.truncate_to<int32_t>().ub();
513✔
455
        return {left_svalue >= 0,
171✔
456
                strict ? left_svalue > right_svalue : left_svalue >= right_svalue,
342✔
457
                left_svalue <= left_uvalue,
458
                left_svalue >= left_uvalue,
459
                left_uvalue >= 0,
460
                strict ? left_uvalue > right_uvalue : left_uvalue >= right_uvalue,
342✔
461
                left_uvalue <= *lpub.number()};
3,249✔
462
    } else if ((left_interval_negative | left_interval_positive) <= interval_t::negative(32) &&
262✔
463
               right_interval <= interval_t::negative(32)) {
38✔
464
        // Interval can be represented as both an svalue and a uvalue since it fits in [INT_MIN, -1],
465
        // aka [INT_MAX+1, UINT_MAX].
466
        return {left_uvalue >= number_t{std::numeric_limits<int32_t>::max()} + 1,
4✔
467
                strict ? left_uvalue > right_uvalue : left_uvalue >= right_uvalue,
2✔
468
                strict ? left_svalue > right_svalue : left_svalue >= right_svalue};
9✔
469
    } else if (inv.eval_interval(left_svalue) <= interval_t::signed_int(32) &&
92✔
470
               inv.eval_interval(right_svalue) <= interval_t::signed_int(32)) {
55✔
471
        // Interval can only be represented as an svalue.
472
        return {strict ? left_svalue > right_svalue : left_svalue >= right_svalue};
42✔
473
    } else {
474
        // We can't directly compare the svalues since they may differ in high order bits.
475
        return {};
20✔
476
    }
477
}
1,575✔
478

479
static std::vector<linear_constraint_t> assume_signed_cst_interval(const NumAbsDomain& inv, Condition::Op op, bool is64,
14,940✔
480
                                                                   variable_t left_svalue, variable_t left_uvalue,
481
                                                                   const linear_expression_t& right_svalue,
482
                                                                   const linear_expression_t& right_uvalue) {
483
    using crab::interval_t;
7,470✔
484
    using namespace crab::dsl_syntax;
7,470✔
485

486
    interval_t left_interval = interval_t::bottom();
14,940✔
487
    interval_t right_interval = interval_t::bottom();
14,940✔
488
    interval_t left_interval_positive = interval_t::bottom();
14,940✔
489
    interval_t left_interval_negative = interval_t::bottom();
14,940✔
490
    get_signed_intervals(inv, is64, left_svalue, left_uvalue, right_svalue, left_interval, right_interval,
14,940✔
491
                         left_interval_positive, left_interval_negative);
492

493
    if (op == Condition::Op::EQ) {
14,940✔
494
        // Handle svalue == right.
495
        if (is64) {
12,474✔
496
            return assume_signed_64bit_eq(inv, left_svalue, left_uvalue, right_interval, right_svalue, right_uvalue);
8,350✔
497
        } else {
498
            return assume_signed_32bit_eq(inv, left_svalue, left_uvalue, right_interval);
4,124✔
499
        }
500
    }
501

502
    const bool is_lt = op == Condition::Op::SLT || op == Condition::Op::SLE;
2,466✔
503
    bool strict = op == Condition::Op::SLT || op == Condition::Op::SGT;
2,466✔
504

505
    auto llb = left_interval.lb();
2,466✔
506
    auto lub = left_interval.ub();
2,466✔
507
    auto rlb = right_interval.lb();
2,466✔
508
    auto rub = right_interval.ub();
2,466✔
509
    if (!is_lt && (strict ? lub <= rlb : lub < rlb)) {
2,889✔
510
        // Left signed interval is lower than right signed interval.
511
        return {linear_constraint_t::false_const()};
125✔
512
    } else if (is_lt && (strict ? llb >= rub : llb > rub)) {
2,597✔
513
        // Left signed interval is higher than right signed interval.
514
        return {linear_constraint_t::false_const()};
95✔
515
    }
516
    if (is_lt && (strict ? lub < rlb : lub <= rlb)) {
2,798✔
517
        // Left signed interval is lower than right signed interval.
518
        return {linear_constraint_t::true_const()};
145✔
519
    } else if (!is_lt && (strict ? llb > rub : llb >= rub)) {
2,501✔
520
        // Left signed interval is higher than right signed interval.
521
        return {linear_constraint_t::true_const()};
95✔
522
    }
523

524
    if (is64) {
2,282✔
525
        if (is_lt) {
1,528✔
526
            return assume_signed_64bit_lt(strict, left_svalue, left_uvalue, left_interval_positive,
378✔
527
                                          left_interval_negative, right_svalue, right_uvalue, right_interval);
756✔
528
        } else {
529
            return assume_signed_64bit_gt(strict, left_svalue, left_uvalue, left_interval_positive,
386✔
530
                                          left_interval_negative, right_svalue, right_uvalue, right_interval);
772✔
531
        }
532
    } else {
533
        // 32-bit compare.
534
        if (is_lt) {
754✔
535
            return assume_signed_32bit_lt(inv, strict, left_svalue, left_uvalue, left_interval_positive,
188✔
536
                                          left_interval_negative, right_svalue, right_uvalue, right_interval);
376✔
537
        } else {
538
            return assume_signed_32bit_gt(inv, strict, left_svalue, left_uvalue, left_interval_positive,
189✔
539
                                          left_interval_negative, right_svalue, right_uvalue, right_interval);
378✔
540
        }
541
    }
542
    return {};
543
}
38,767✔
544

545
static std::vector<linear_constraint_t>
546
assume_unsigned_64bit_lt(const NumAbsDomain& inv, bool strict, variable_t left_svalue, variable_t left_uvalue,
1,364✔
547
                         const interval_t& left_interval_low, const interval_t& left_interval_high,
548
                         const linear_expression_t& right_svalue, const linear_expression_t& right_uvalue,
549
                         const interval_t& right_interval) {
550
    using crab::interval_t;
682✔
551
    using namespace crab::dsl_syntax;
682✔
552

553
    auto rub = right_interval.ub();
1,364✔
554
    auto lllb = left_interval_low.truncate_to<uint64_t>().lb();
2,046✔
555
    if (right_interval <= interval_t::nonnegative(64) && (strict ? lllb >= rub : lllb > rub)) {
2,348✔
556
        // The high interval is out of range.
557
        if (auto lsubn = inv.eval_interval(left_svalue).ub().number()) {
×
558
            return {left_uvalue >= 0, (strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue),
×
559
                    left_uvalue <= *lsubn, left_svalue >= 0};
×
560
        } else {
561
            return {left_uvalue >= 0, (strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue),
×
562
                    left_svalue >= 0};
×
563
        }
×
564
    }
565
    auto lhlb = left_interval_high.truncate_to<uint64_t>().lb();
2,046✔
566
    if (right_interval <= interval_t::unsigned_high(64) && (strict ? lhlb >= rub : lhlb > rub)) {
2,047✔
567
        // The high interval is out of range.
568
        if (auto lsubn = inv.eval_interval(left_svalue).ub().number()) {
10✔
569
            return {left_uvalue >= 0, (strict ? left_uvalue < *rub.number() : left_uvalue <= *rub.number()),
6✔
570
                    left_uvalue <= *lsubn, left_svalue >= 0};
24✔
571
        } else {
572
            return {left_uvalue >= 0, (strict ? left_uvalue < *rub.number() : left_uvalue <= *rub.number()),
×
573
                    left_svalue >= 0};
×
574
        }
4✔
575
    }
576
    if (right_interval <= interval_t::signed_int(64)) {
2,040✔
577
        // Interval can be represented as both an svalue and a uvalue since it fits in [0, INT_MAX].
578
        auto llub = left_interval_low.truncate_to<uint64_t>().ub();
1,677✔
579
        return {0 <= left_uvalue, strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue,
1,118✔
580
                left_uvalue <= *llub.number(), 0 <= left_svalue,
1,118✔
581
                strict ? left_svalue < right_svalue : left_svalue <= right_svalue};
7,826✔
582
    } else if (left_interval_low.is_bottom() && right_interval <= interval_t::unsigned_high(64)) {
803✔
583
        // Interval can be represented as both an svalue and a uvalue since it fits in [INT_MAX+1, UINT_MAX].
584
        return {0 <= left_uvalue, strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue,
2✔
585
                strict ? left_svalue < right_svalue : left_svalue <= right_svalue};
10✔
586
    } else if ((left_interval_low | left_interval_high) == interval_t::unsigned_int(64)) {
480✔
587
        // Interval can only be represented as a uvalue, and was TOP before.
588
        return {strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue};
48✔
589
    } else {
590
        // Interval can only be represented as a uvalue.
591
        return {0 <= left_uvalue, strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue};
896✔
592
    }
593
}
8,250✔
594

595
static std::vector<linear_constraint_t> assume_unsigned_32bit_lt(const NumAbsDomain& inv, const bool strict,
340✔
596
                                                                 const variable_t left_svalue,
597
                                                                 const variable_t left_uvalue,
598
                                                                 const linear_expression_t& right_svalue,
599
                                                                 const linear_expression_t& right_uvalue) {
600
    using crab::interval_t;
170✔
601
    using namespace crab::dsl_syntax;
170✔
602

603
    if (inv.eval_interval(left_uvalue) <= interval_t::nonnegative(32) &&
984✔
604
        inv.eval_interval(right_uvalue) <= interval_t::nonnegative(32)) {
742✔
605
        // Interval can be represented as both an svalue and a uvalue since it fits in [0, INT32_MAX].
606
        return {0 <= left_uvalue, strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue,
266✔
607
                strict ? left_svalue < right_svalue : left_svalue <= right_svalue};
1,330✔
608
    } else if (inv.eval_interval(left_svalue) <= interval_t::negative(32) &&
186✔
609
               inv.eval_interval(right_svalue) <= interval_t::negative(32)) {
77✔
610
        // Interval can be represented as both an svalue and a uvalue since it fits in [INT32_MIN, -1].
611
        return {0 <= left_uvalue, strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue,
2✔
612
                strict ? left_svalue < right_svalue : left_svalue <= right_svalue};
10✔
613
    } else if (inv.eval_interval(left_uvalue) <= interval_t::unsigned_int(32) &&
214✔
614
               inv.eval_interval(right_uvalue) <= interval_t::unsigned_int(32)) {
174✔
615
        // Interval can only be represented as a uvalue.
616
        return {0 <= left_uvalue, strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue};
256✔
617
    } else {
618
        // We can't directly compare the uvalues since they may differ in high order bits.
619
        return {};
8✔
620
    }
621
}
1,130✔
622

623
static std::vector<linear_constraint_t>
624
assume_unsigned_64bit_gt(const NumAbsDomain& inv, const bool strict, const variable_t left_svalue,
6,370✔
625
                         const variable_t left_uvalue, const interval_t& left_interval_low,
626
                         const interval_t& left_interval_high, const linear_expression_t& right_svalue,
627
                         const linear_expression_t& right_uvalue, const interval_t& right_interval) {
628
    using crab::interval_t;
3,185✔
629
    using namespace crab::dsl_syntax;
3,185✔
630

631
    const auto rlb = right_interval.lb();
6,370✔
632
    const auto llub = left_interval_low.truncate_to<uint64_t>().ub();
9,555✔
633
    const auto lhlb = left_interval_high.truncate_to<uint64_t>().lb();
9,555✔
634

635
    if (right_interval <= interval_t::nonnegative(64) && (strict ? llub <= rlb : llub < rlb)) {
9,685✔
636
        // The low interval is out of range.
637
        return {strict ? left_uvalue > right_uvalue : left_uvalue >= right_uvalue,
16✔
638
                *lhlb.number() == std::numeric_limits<uint64_t>::max() ? left_uvalue == *lhlb.number()
46✔
639
                                                                       : left_uvalue >= *lhlb.number(),
6✔
640
                left_svalue < 0};
72✔
641
    } else if (right_interval <= interval_t::unsigned_high(64)) {
9,531✔
642
        // Interval can be represented as both an svalue and a uvalue since it fits in [INT_MAX+1, UINT_MAX].
643
        return {0 <= left_uvalue, strict ? left_uvalue > right_uvalue : left_uvalue >= right_uvalue,
4✔
644
                strict ? left_svalue > right_svalue : left_svalue >= right_svalue};
18✔
645
    } else if ((left_interval_low | left_interval_high) <= interval_t::nonnegative(64) &&
18,601✔
646
               right_interval <= interval_t::nonnegative(64)) {
11,802✔
647
        // Interval can be represented as both an svalue and a uvalue since it fits in [0, INT_MAX].
648
        return {0 <= left_uvalue, strict ? left_uvalue > right_uvalue : left_uvalue >= right_uvalue,
5,230✔
649
                strict ? left_svalue > right_svalue : left_svalue >= right_svalue};
23,535✔
650
    } else {
651
        // Interval can only be represented as a uvalue.
652
        return {0 <= left_uvalue, strict ? left_uvalue > right_uvalue : left_uvalue >= right_uvalue};
4,480✔
653
    }
654
}
22,331✔
655

656
static std::vector<linear_constraint_t>
657
assume_unsigned_32bit_gt(const NumAbsDomain& inv, const bool strict, const variable_t left_svalue,
3,202✔
658
                         const variable_t left_uvalue, const interval_t& left_interval_low,
659
                         const interval_t& left_interval_high, const linear_expression_t& right_svalue,
660
                         const linear_expression_t& right_uvalue, const interval_t& right_interval) {
661
    using crab::interval_t;
1,601✔
662
    using namespace crab::dsl_syntax;
1,601✔
663

664
    if (right_interval <= interval_t::unsigned_high(32)) {
4,803✔
665
        // Interval can be represented as both an svalue and a uvalue since it fits in [INT_MAX+1, UINT_MAX].
666
        return {0 <= left_uvalue, strict ? left_uvalue > right_uvalue : left_uvalue >= right_uvalue,
4✔
667
                strict ? left_svalue > right_svalue : left_svalue >= right_svalue};
18✔
668
    } else if (inv.eval_interval(left_uvalue) <= interval_t::unsigned_int(32) &&
8,961✔
669
               inv.eval_interval(right_uvalue) <= interval_t::unsigned_int(32)) {
6,096✔
670
        // Interval can only be represented as a uvalue.
671
        return {0 <= left_uvalue, strict ? left_uvalue > right_uvalue : left_uvalue >= right_uvalue};
7,712✔
672
    } else {
673
        // We can't directly compare the uvalues since they may differ in high order bits.
674
        return {};
1,270✔
675
    };
676
}
5,796✔
677

678
static std::vector<linear_constraint_t> assume_unsigned_cst_interval(const NumAbsDomain& inv, Condition::Op op,
15,634✔
679
                                                                     bool is64, variable_t left_svalue,
680
                                                                     variable_t left_uvalue,
681
                                                                     const linear_expression_t& right_svalue,
682
                                                                     const linear_expression_t& right_uvalue) {
683
    using crab::interval_t;
7,817✔
684
    using namespace crab::dsl_syntax;
7,817✔
685

686
    interval_t left_interval = interval_t::bottom();
15,634✔
687
    interval_t right_interval = interval_t::bottom();
15,634✔
688
    interval_t left_interval_low = interval_t::bottom();
15,634✔
689
    interval_t left_interval_high = interval_t::bottom();
15,634✔
690
    get_unsigned_intervals(inv, is64, left_svalue, left_uvalue, right_uvalue, left_interval, right_interval,
15,634✔
691
                           left_interval_low, left_interval_high);
692

693
    // Handle uvalue != right.
694
    if (op == Condition::Op::NE) {
15,634✔
695
        if (auto rn = right_interval.singleton()) {
12,512✔
696
            if (rn == left_interval.truncate_to_uint(is64 ? 64 : 32).lb().number()) {
32,853✔
697
                // "NE lower bound" is equivalent to "GT lower bound".
698
                op = Condition::Op::GT;
8,484✔
699
                right_interval = interval_t{left_interval.lb()};
16,968✔
700
            } else if (rn == left_interval.ub().number()) {
7,732✔
701
                // "NE upper bound" is equivalent to "LT upper bound".
702
                op = Condition::Op::LT;
402✔
703
                right_interval = interval_t{left_interval.ub()};
804✔
704
            } else {
705
                return {};
3,464✔
706
            }
707
        } else {
708
            return {};
162✔
709
        }
12,512✔
710
    }
711

712
    const bool is_lt = op == Condition::Op::LT || op == Condition::Op::LE;
12,008✔
713
    bool strict = op == Condition::Op::LT || op == Condition::Op::GT;
12,008✔
714

715
    auto [llb, lub] = left_interval.pair();
18,012✔
716
    auto [rlb, rub] = right_interval.pair();
18,012✔
717
    if (is_lt ? (strict ? llb >= rub : llb > rub) : (strict ? lub <= rlb : lub < rlb)) {
12,725✔
718
        // Left unsigned interval is lower than right unsigned interval.
719
        return {linear_constraint_t::false_const()};
1,210✔
720
    }
721
    if (is_lt && (strict ? lub < rlb : lub <= rlb)) {
11,938✔
722
        // Left unsigned interval is lower than right unsigned interval. We still add a
723
        // relationship for use when widening, such as is used in the prime conformance test.
724
        if (is64) {
114✔
725
            return {strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue};
246✔
726
        }
727
        return {};
32✔
728
    } else if (!is_lt && (strict ? llb > rub : llb >= rub)) {
16,070✔
729
        // Left unsigned interval is higher than right unsigned interval. We still add a
730
        // relationship for use when widening, such as is used in the prime conformance test.
731
        if (is64) {
134✔
732
            return {strict ? left_uvalue > right_uvalue : left_uvalue >= right_uvalue};
276✔
733
        } else {
734
            return {};
42✔
735
        }
736
    }
737

738
    if (is64) {
11,276✔
739
        if (is_lt) {
7,734✔
740
            return assume_unsigned_64bit_lt(inv, strict, left_svalue, left_uvalue, left_interval_low,
682✔
741
                                            left_interval_high, right_svalue, right_uvalue, right_interval);
1,364✔
742
        } else {
743
            return assume_unsigned_64bit_gt(inv, strict, left_svalue, left_uvalue, left_interval_low,
3,185✔
744
                                            left_interval_high, right_svalue, right_uvalue, right_interval);
6,370✔
745
        }
746
    } else {
747
        if (is_lt) {
3,542✔
748
            return assume_unsigned_32bit_lt(inv, strict, left_svalue, left_uvalue, right_svalue, right_uvalue);
340✔
749
        } else {
750
            return assume_unsigned_32bit_gt(inv, strict, left_svalue, left_uvalue, left_interval_low,
1,601✔
751
                                            left_interval_high, right_svalue, right_uvalue, right_interval);
3,202✔
752
        }
753
    }
754
}
51,838✔
755

756
/** Linear constraints for a comparison with a constant.
757
 */
758
static std::vector<linear_constraint_t> assume_cst_imm(const NumAbsDomain& inv, const Condition::Op op, const bool is64,
28,798✔
759
                                                       const variable_t dst_svalue, const variable_t dst_uvalue,
760
                                                       const int64_t imm) {
761
    using namespace crab::dsl_syntax;
14,399✔
762
    using Op = Condition::Op;
14,399✔
763
    switch (op) {
28,798✔
764
    case Op::EQ:
14,696✔
765
    case Op::SGE:
7,348✔
766
    case Op::SLE:
7,348✔
767
    case Op::SGT:
7,348✔
768
    case Op::SLT:
7,348✔
769
        return assume_signed_cst_interval(inv, op, is64, dst_svalue, dst_uvalue, imm, gsl::narrow_cast<uint64_t>(imm));
14,696✔
770
    case Op::SET:
28✔
771
    case Op::NSET: return assume_bit_cst_interval(inv, op, is64, dst_uvalue, interval_t{imm});
42✔
772
    case Op::NE:
14,074✔
773
    case Op::GE:
7,037✔
774
    case Op::LE:
7,037✔
775
    case Op::GT:
7,037✔
776
    case Op::LT:
7,037✔
777
        return assume_unsigned_cst_interval(inv, op, is64, dst_svalue, dst_uvalue, imm,
7,037✔
778
                                            gsl::narrow_cast<uint64_t>(imm));
14,074✔
779
    }
780
    return {};
×
781
}
782

783
/** Linear constraint for a numerical comparison between registers.
784
 */
785
static std::vector<linear_constraint_t> assume_cst_reg(const NumAbsDomain& inv, const Condition::Op op, const bool is64,
6,436✔
786
                                                       const variable_t dst_svalue, const variable_t dst_uvalue,
787
                                                       const variable_t src_svalue, const variable_t src_uvalue) {
788
    using namespace crab::dsl_syntax;
3,218✔
789
    using Op = Condition::Op;
3,218✔
790
    if (is64) {
6,436✔
791
        switch (op) {
5,804✔
792
        case Op::EQ: {
866✔
793
            const interval_t src_interval = inv.eval_interval(src_svalue);
866✔
794
            if (!src_interval.is_singleton() && src_interval <= interval_t::nonnegative(64)) {
1,316✔
795
                return {eq(dst_svalue, src_svalue), eq(dst_uvalue, src_uvalue), eq(dst_svalue, dst_uvalue)};
558✔
796
            } else {
797
                return {eq(dst_svalue, src_svalue), eq(dst_uvalue, src_uvalue)};
2,597✔
798
            }
799
        }
866✔
800
        case Op::NE: return {neq(dst_svalue, src_svalue)};
2,175✔
801
        case Op::SGE: return {dst_svalue >= src_svalue};
35✔
802
        case Op::SLE: return {dst_svalue <= src_svalue};
3,555✔
803
        case Op::SGT: return {dst_svalue > src_svalue};
3,555✔
804
        // Note: reverse the test as a workaround strange lookup:
805
        case Op::SLT: return {src_svalue > dst_svalue};
35✔
806
        case Op::SET:
12✔
807
        case Op::NSET: return assume_bit_cst_interval(inv, op, is64, dst_uvalue, inv.eval_interval(src_uvalue));
18✔
808
        case Op::GE:
1,184✔
809
        case Op::LE:
592✔
810
        case Op::GT:
592✔
811
        case Op::LT: return assume_unsigned_cst_interval(inv, op, is64, dst_svalue, dst_uvalue, src_svalue, src_uvalue);
1,184✔
812
        }
813
    } else {
814
        switch (op) {
632✔
815
        case Op::EQ:
244✔
816
        case Op::SGE:
122✔
817
        case Op::SLE:
122✔
818
        case Op::SGT:
122✔
819
        case Op::SLT: return assume_signed_cst_interval(inv, op, is64, dst_svalue, dst_uvalue, src_svalue, src_uvalue);
244✔
820
        case Op::SET:
12✔
821
        case Op::NSET: return assume_bit_cst_interval(inv, op, is64, dst_uvalue, inv.eval_interval(src_uvalue));
18✔
822
        case Op::NE:
376✔
823
        case Op::GE:
188✔
824
        case Op::LE:
188✔
825
        case Op::GT:
188✔
826
        case Op::LT: return assume_unsigned_cst_interval(inv, op, is64, dst_svalue, dst_uvalue, src_svalue, src_uvalue);
376✔
827
        }
828
    }
829
    assert(false);
×
830
    throw std::exception();
831
}
8,205✔
832

833
void ebpf_transformer::assign(const variable_t x, const linear_expression_t& e) { m_inv.assign(x, e); }
×
834
void ebpf_transformer::assign(const variable_t x, const int64_t e) { m_inv.set(x, interval_t(e)); }
116,124✔
835

836
void ebpf_transformer::apply(const arith_binop_t op, const variable_t x, const variable_t y, const number_t& z,
×
837
                             const int finite_width) {
838
    m_inv.apply(op, x, y, z, finite_width);
×
839
}
×
840

841
void ebpf_transformer::apply(const arith_binop_t op, const variable_t x, const variable_t y, const variable_t z,
×
842
                             const int finite_width) {
843
    m_inv.apply(op, x, y, z, finite_width);
×
844
}
×
845

846
void ebpf_transformer::apply(const bitwise_binop_t op, const variable_t x, const variable_t y, const variable_t z,
×
847
                             const int finite_width) {
848
    m_inv.apply(op, x, y, z, finite_width);
×
849
}
×
850

851
void ebpf_transformer::apply(const bitwise_binop_t op, const variable_t x, const variable_t y, const number_t& k,
×
852
                             const int finite_width) {
853
    m_inv.apply(op, x, y, k, finite_width);
×
854
}
×
855

856
void ebpf_transformer::apply(binop_t op, variable_t x, variable_t y, const number_t& z, int finite_width) {
×
857
    std::visit([&](auto top) { apply(top, x, y, z, finite_width); }, op);
×
858
}
×
859

860
void ebpf_transformer::apply(binop_t op, variable_t x, variable_t y, variable_t z, int finite_width) {
×
861
    std::visit([&](auto top) { apply(top, x, y, z, finite_width); }, op);
×
862
}
×
863

864
static void havoc_offsets(NumAbsDomain& inv, const Reg& reg) {
188,936✔
865
    const reg_pack_t r = reg_pack(reg);
188,936✔
866
    inv -= r.ctx_offset;
188,936✔
867
    inv -= r.map_fd;
188,936✔
868
    inv -= r.packet_offset;
188,936✔
869
    inv -= r.shared_offset;
188,936✔
870
    inv -= r.shared_region_size;
188,936✔
871
    inv -= r.stack_offset;
188,936✔
872
    inv -= r.stack_numeric_size;
188,936✔
873
}
188,936✔
874
static void havoc_register(NumAbsDomain& inv, const Reg& reg) {
91,088✔
875
    const reg_pack_t r = reg_pack(reg);
91,088✔
876
    havoc_offsets(inv, reg);
91,088✔
877
    inv -= r.svalue;
91,088✔
878
    inv -= r.uvalue;
91,088✔
879
}
91,088✔
880

881
void ebpf_transformer::scratch_caller_saved_registers() {
13,730✔
882
    for (int i = R1_ARG; i <= R5_ARG; i++) {
82,380✔
883
        Reg r{gsl::narrow<uint8_t>(i)};
68,650✔
884
        havoc_register(m_inv, r);
68,650✔
885
        type_inv.havoc_type(m_inv, r);
68,650✔
886
    }
887
}
13,730✔
888

889
void ebpf_transformer::save_callee_saved_registers(const std::string& prefix) {
58✔
890
    // Create variables specific to the new call stack frame that store
891
    // copies of the states of r6 through r9.
892
    for (int r = R6; r <= R9; r++) {
290✔
893
        for (const data_kind_t kind : iterate_kinds()) {
2,552✔
894
            const variable_t src_var = variable_t::reg(kind, r);
2,320✔
895
            if (!m_inv[src_var].is_top()) {
4,640✔
896
                assign(variable_t::stack_frame_var(kind, r, prefix), src_var);
100✔
897
            }
898
        }
232✔
899
    }
900
}
58✔
901

902
void ebpf_transformer::restore_callee_saved_registers(const std::string& prefix) {
58✔
903
    for (int r = R6; r <= R9; r++) {
290✔
904
        for (const data_kind_t kind : iterate_kinds()) {
2,552✔
905
            const variable_t src_var = variable_t::stack_frame_var(kind, r, prefix);
2,320✔
906
            if (!m_inv[src_var].is_top()) {
4,640✔
907
                assign(variable_t::reg(kind, r), src_var);
100✔
908
            } else {
909
                havoc(variable_t::reg(kind, r));
2,220✔
910
            }
911
            havoc(src_var);
2,320✔
912
        }
232✔
913
    }
914
}
58✔
915

916
void ebpf_transformer::havoc_subprogram_stack(const std::string& prefix) {
58✔
917
    const variable_t r10_stack_offset = reg_pack(R10_STACK_POINTER).stack_offset;
58✔
918
    const auto intv = m_inv.eval_interval(r10_stack_offset);
58✔
919
    if (!intv.is_singleton()) {
58✔
920
        return;
36✔
921
    }
922
    const int64_t stack_start = intv.singleton()->cast_to<int64_t>() - EBPF_SUBPROGRAM_STACK_SIZE;
22✔
923
    for (const data_kind_t kind : iterate_kinds()) {
242✔
924
        stack.havoc(m_inv, kind, stack_start, EBPF_SUBPROGRAM_STACK_SIZE);
220✔
925
    }
22✔
926
}
58✔
927

928
void ebpf_transformer::forget_packet_pointers() {
38✔
929
    using namespace crab::dsl_syntax;
19✔
930

931
    for (const variable_t type_variable : variable_t::get_type_variables()) {
1,258✔
932
        if (type_inv.has_type(m_inv, type_variable, T_PACKET)) {
1,220✔
933
            havoc(variable_t::kind_var(data_kind_t::types, type_variable));
720✔
934
            havoc(variable_t::kind_var(data_kind_t::packet_offsets, type_variable));
720✔
935
            havoc(variable_t::kind_var(data_kind_t::svalues, type_variable));
720✔
936
            havoc(variable_t::kind_var(data_kind_t::uvalues, type_variable));
1,330✔
937
        }
938
    }
38✔
939

940
    dom.initialize_packet();
38✔
941
}
38✔
942

943
static void overflow_bounds(NumAbsDomain& inv, variable_t lhs, number_t span, int finite_width, bool issigned) {
187,070✔
944
    using namespace crab::dsl_syntax;
93,535✔
945
    auto interval = inv[lhs];
187,070✔
946
    if (interval.ub() - interval.lb() >= span) {
467,675✔
947
        // Interval covers the full space.
948
        inv -= lhs;
19,376✔
949
        return;
9,688✔
950
    }
951
    if (interval.is_bottom()) {
167,694✔
952
        inv -= lhs;
88✔
953
        return;
44✔
954
    }
955
    number_t lb_value = interval.lb().number().value();
335,212✔
956
    number_t ub_value = interval.ub().number().value();
335,212✔
957

958
    // Compute the interval, taking overflow into account.
959
    // For a signed result, we need to ensure the signed and unsigned results match
960
    // so for a 32-bit operation, 0x80000000 should be a positive 64-bit number not
961
    // a sign extended negative one.
962
    number_t lb = lb_value.truncate_to_uint(finite_width);
167,606✔
963
    number_t ub = ub_value.truncate_to_uint(finite_width);
167,606✔
964
    if (issigned) {
167,606✔
965
        lb = lb.truncate_to<int64_t>();
67,824✔
966
        ub = ub.truncate_to<int64_t>();
67,824✔
967
    }
968
    if (lb > ub) {
167,606✔
969
        // Range wraps in the middle, so we cannot represent as an unsigned interval.
970
        inv -= lhs;
3,438✔
971
        return;
3,438✔
972
    }
973
    auto new_interval = interval_t{lb, ub};
410,420✔
974
    if (new_interval != interval) {
164,168✔
975
        // Update the variable, which will lose any relationships to other variables.
976
        inv.set(lhs, new_interval);
1,352✔
977
    }
978
}
277,749✔
979

980
static void overflow_signed(NumAbsDomain& inv, const variable_t lhs, const int finite_width) {
77,556✔
981
    const auto span{finite_width == 64   ? number_t{std::numeric_limits<uint64_t>::max()}
77,556✔
982
                    : finite_width == 32 ? number_t{std::numeric_limits<uint32_t>::max()}
5,454✔
983
                                         : throw std::exception()};
77,556✔
984
    overflow_bounds(inv, lhs, span, finite_width, true);
116,334✔
985
}
77,556✔
986

987
static void overflow_unsigned(NumAbsDomain& inv, const variable_t lhs, const int finite_width) {
109,514✔
988
    const auto span{finite_width == 64   ? number_t{std::numeric_limits<uint64_t>::max()}
109,514✔
989
                    : finite_width == 32 ? number_t{std::numeric_limits<uint32_t>::max()}
11,368✔
990
                                         : throw std::exception()};
109,514✔
991
    overflow_bounds(inv, lhs, span, finite_width, false);
164,271✔
992
}
109,514✔
993
static void apply_signed(NumAbsDomain& inv, const binop_t& op, const variable_t xs, const variable_t xu,
31,158✔
994
                         const variable_t y, const number_t& z, const int finite_width) {
995
    inv.apply(op, xs, y, z, finite_width);
31,158✔
996
    if (finite_width) {
24,076✔
997
        inv.assign(xu, xs);
16,994✔
998
        overflow_signed(inv, xs, finite_width);
16,994✔
999
        overflow_unsigned(inv, xu, finite_width);
16,994✔
1000
    }
1001
}
24,076✔
1002

1003
static void apply_unsigned(NumAbsDomain& inv, const binop_t& op, const variable_t xs, const variable_t xu,
42,674✔
1004
                           const variable_t y, const number_t& z, const int finite_width) {
1005
    inv.apply(op, xu, y, z, finite_width);
42,674✔
1006
    if (finite_width) {
42,674✔
1007
        inv.assign(xs, xu);
42,674✔
1008
        overflow_signed(inv, xs, finite_width);
42,674✔
1009
        overflow_unsigned(inv, xu, finite_width);
42,674✔
1010
    }
1011
}
42,674✔
1012

1013
static void apply_signed(NumAbsDomain& inv, const binop_t& op, const variable_t xs, const variable_t xu,
3,382✔
1014
                         const variable_t y, const variable_t z, const int finite_width) {
1015
    inv.apply(op, xs, y, z, finite_width);
3,382✔
1016
    if (finite_width) {
3,373✔
1017
        inv.assign(xu, xs);
3,364✔
1018
        overflow_signed(inv, xs, finite_width);
3,364✔
1019
        overflow_unsigned(inv, xu, finite_width);
3,364✔
1020
    }
1021
}
3,373✔
1022

1023
static void apply_unsigned(NumAbsDomain& inv, const binop_t& op, const variable_t xs, const variable_t xu,
5,846✔
1024
                           const variable_t y, const variable_t z, const int finite_width) {
1025
    inv.apply(op, xu, y, z, finite_width);
5,846✔
1026
    if (finite_width) {
5,846✔
1027
        inv.assign(xs, xu);
5,846✔
1028
        overflow_signed(inv, xs, finite_width);
5,846✔
1029
        overflow_unsigned(inv, xu, finite_width);
5,846✔
1030
    }
1031
}
5,846✔
1032

1033
void ebpf_transformer::add(const variable_t lhs, const variable_t op2) {
×
1034
    apply_signed(m_inv, arith_binop_t::ADD, lhs, lhs, lhs, op2, 0);
×
1035
}
×
1036
void ebpf_transformer::add(const variable_t lhs, const number_t& op2) {
13,292✔
1037
    apply_signed(m_inv, arith_binop_t::ADD, lhs, lhs, lhs, op2, 0);
13,292✔
1038
}
13,292✔
1039
void ebpf_transformer::sub(const variable_t lhs, const variable_t op2) {
4✔
1040
    apply_signed(m_inv, arith_binop_t::SUB, lhs, lhs, lhs, op2, 0);
4✔
1041
}
4✔
1042
void ebpf_transformer::sub(const variable_t lhs, const number_t& op2) {
872✔
1043
    apply_signed(m_inv, arith_binop_t::SUB, lhs, lhs, lhs, op2, 0);
872✔
1044
}
872✔
1045

1046
// Add/subtract with overflow are both signed and unsigned. We can use either one of the two to compute the
1047
// result before adjusting for overflow, though if one is top we want to use the other to retain precision.
1048
void ebpf_transformer::add_overflow(const variable_t lhss, const variable_t lhsu, const variable_t op2,
2,180✔
1049
                                    const int finite_width) {
1050
    apply_signed(m_inv, arith_binop_t::ADD, lhss, lhsu, !m_inv.eval_interval(lhss).is_top() ? lhss : lhsu, op2,
4,360✔
1051
                 finite_width);
1052
}
2,180✔
1053
void ebpf_transformer::add_overflow(const variable_t lhss, const variable_t lhsu, const number_t& op2,
16,732✔
1054
                                    const int finite_width) {
1055
    apply_signed(m_inv, arith_binop_t::ADD, lhss, lhsu, !m_inv.eval_interval(lhss).is_top() ? lhss : lhsu, op2,
33,464✔
1056
                 finite_width);
1057
}
16,732✔
1058
void ebpf_transformer::sub_overflow(const variable_t lhss, const variable_t lhsu, const variable_t op2,
4✔
1059
                                    const int finite_width) {
1060
    apply_signed(m_inv, arith_binop_t::SUB, lhss, lhsu, !m_inv.eval_interval(lhss).is_top() ? lhss : lhsu, op2,
8✔
1061
                 finite_width);
1062
}
4✔
1063
void ebpf_transformer::sub_overflow(const variable_t lhss, const variable_t lhsu, const number_t& op2,
×
1064
                                    const int finite_width) {
1065
    apply_signed(m_inv, arith_binop_t::SUB, lhss, lhsu, !m_inv.eval_interval(lhss).is_top() ? lhss : lhsu, op2,
×
1066
                 finite_width);
1067
}
×
1068

1069
void ebpf_transformer::neg(const variable_t lhss, const variable_t lhsu, const int finite_width) {
120✔
1070
    apply_signed(m_inv, arith_binop_t::MUL, lhss, lhsu, lhss, -1, finite_width);
120✔
1071
}
120✔
1072
void ebpf_transformer::mul(const variable_t lhss, const variable_t lhsu, const variable_t op2, const int finite_width) {
74✔
1073
    apply_signed(m_inv, arith_binop_t::MUL, lhss, lhsu, lhss, op2, finite_width);
74✔
1074
}
74✔
1075
void ebpf_transformer::mul(const variable_t lhss, const variable_t lhsu, const number_t& op2, const int finite_width) {
90✔
1076
    apply_signed(m_inv, arith_binop_t::MUL, lhss, lhsu, lhss, op2, finite_width);
90✔
1077
}
90✔
1078
void ebpf_transformer::sdiv(const variable_t lhss, const variable_t lhsu, const variable_t op2,
36✔
1079
                            const int finite_width) {
1080
    apply_signed(m_inv, arith_binop_t::SDIV, lhss, lhsu, lhss, op2, finite_width);
36✔
1081
}
36✔
1082
void ebpf_transformer::sdiv(const variable_t lhss, const variable_t lhsu, const number_t& op2, const int finite_width) {
18✔
1083
    apply_signed(m_inv, arith_binop_t::SDIV, lhss, lhsu, lhss, op2, finite_width);
18✔
1084
}
18✔
1085
void ebpf_transformer::udiv(const variable_t lhss, const variable_t lhsu, const variable_t op2,
108✔
1086
                            const int finite_width) {
1087
    apply_unsigned(m_inv, arith_binop_t::UDIV, lhss, lhsu, lhsu, op2, finite_width);
108✔
1088
}
108✔
1089
void ebpf_transformer::udiv(const variable_t lhss, const variable_t lhsu, const number_t& op2, const int finite_width) {
1,130✔
1090
    apply_unsigned(m_inv, arith_binop_t::UDIV, lhss, lhsu, lhsu, op2, finite_width);
1,130✔
1091
}
1,130✔
1092
void ebpf_transformer::srem(const variable_t lhss, const variable_t lhsu, const variable_t op2,
48✔
1093
                            const int finite_width) {
1094
    apply_signed(m_inv, arith_binop_t::SREM, lhss, lhsu, lhss, op2, finite_width);
48✔
1095
}
48✔
1096
void ebpf_transformer::srem(const variable_t lhss, const variable_t lhsu, const number_t& op2, const int finite_width) {
34✔
1097
    apply_signed(m_inv, arith_binop_t::SREM, lhss, lhsu, lhss, op2, finite_width);
34✔
1098
}
34✔
1099
void ebpf_transformer::urem(const variable_t lhss, const variable_t lhsu, const variable_t op2,
36✔
1100
                            const int finite_width) {
1101
    apply_unsigned(m_inv, arith_binop_t::UREM, lhss, lhsu, lhsu, op2, finite_width);
36✔
1102
}
36✔
1103
void ebpf_transformer::urem(const variable_t lhss, const variable_t lhsu, const number_t& op2, const int finite_width) {
24✔
1104
    apply_unsigned(m_inv, arith_binop_t::UREM, lhss, lhsu, lhsu, op2, finite_width);
24✔
1105
}
24✔
1106

1107
void ebpf_transformer::bitwise_and(const variable_t lhss, const variable_t lhsu, const variable_t op2,
468✔
1108
                                   const int finite_width) {
1109
    apply_unsigned(m_inv, bitwise_binop_t::AND, lhss, lhsu, lhsu, op2, finite_width);
468✔
1110
}
468✔
1111
void ebpf_transformer::bitwise_and(const variable_t lhss, const variable_t lhsu, const number_t& op2) {
38,362✔
1112
    // Use finite width 64 to make the svalue be set as well as the uvalue.
1113
    apply_unsigned(m_inv, bitwise_binop_t::AND, lhss, lhsu, lhsu, op2, 64);
38,362✔
1114
}
38,362✔
1115
void ebpf_transformer::bitwise_or(const variable_t lhss, const variable_t lhsu, const variable_t op2,
4,440✔
1116
                                  const int finite_width) {
1117
    apply_unsigned(m_inv, bitwise_binop_t::OR, lhss, lhsu, lhsu, op2, finite_width);
4,440✔
1118
}
4,440✔
1119
void ebpf_transformer::bitwise_or(const variable_t lhss, const variable_t lhsu, const number_t& op2) {
1,542✔
1120
    apply_unsigned(m_inv, bitwise_binop_t::OR, lhss, lhsu, lhsu, op2, 64);
1,542✔
1121
}
1,542✔
1122
void ebpf_transformer::bitwise_xor(const variable_t lhss, const variable_t lhsu, const variable_t op2,
248✔
1123
                                   const int finite_width) {
1124
    apply_unsigned(m_inv, bitwise_binop_t::XOR, lhss, lhsu, lhsu, op2, finite_width);
248✔
1125
}
248✔
1126
void ebpf_transformer::bitwise_xor(const variable_t lhss, const variable_t lhsu, const number_t& op2) {
224✔
1127
    apply_unsigned(m_inv, bitwise_binop_t::XOR, lhss, lhsu, lhsu, op2, 64);
224✔
1128
}
224✔
1129
void ebpf_transformer::shl_overflow(const variable_t lhss, const variable_t lhsu, const variable_t op2) {
546✔
1130
    apply_unsigned(m_inv, bitwise_binop_t::SHL, lhss, lhsu, lhsu, op2, 64);
546✔
1131
}
546✔
1132
void ebpf_transformer::shl_overflow(const variable_t lhss, const variable_t lhsu, const number_t& op2) {
1,392✔
1133
    apply_unsigned(m_inv, bitwise_binop_t::SHL, lhss, lhsu, lhsu, op2, 64);
1,392✔
1134
}
1,392✔
1135

1136
static void assume(NumAbsDomain& inv, const linear_constraint_t& cst) { inv += cst; }
79,342✔
1137
void ebpf_transformer::assume(const linear_constraint_t& cst) { crab::assume(m_inv, cst); }
79,342✔
1138

1139
/// Forget everything we know about the value of a variable.
1140
void ebpf_transformer::havoc(const variable_t v) { m_inv -= v; }
360,934✔
1141
void ebpf_transformer::havoc_offsets(const Reg& reg) { crab::havoc_offsets(m_inv, reg); }
48,687✔
1142

1143
void ebpf_transformer::assign(const variable_t lhs, const variable_t rhs) { m_inv.assign(lhs, rhs); }
80,684✔
1144

1145
static linear_constraint_t type_is_pointer(const reg_pack_t& r) {
346✔
1146
    using namespace crab::dsl_syntax;
173✔
1147
    return r.type >= T_CTX;
519✔
1148
}
1149

1150
static linear_constraint_t type_is_number(const reg_pack_t& r) {
14,512✔
1151
    using namespace crab::dsl_syntax;
7,256✔
1152
    return r.type == T_NUM;
14,512✔
1153
}
1154

1155
static linear_constraint_t type_is_number(const Reg& r) { return type_is_number(reg_pack(r)); }
6,170✔
1156

1157
static linear_constraint_t type_is_not_stack(const reg_pack_t& r) {
330✔
1158
    using namespace crab::dsl_syntax;
165✔
1159
    return r.type != T_STACK;
330✔
1160
}
1161

1162
void ebpf_transformer::operator()(const Assume& s) {
36,882✔
1163
    const Condition cond = s.cond;
36,882✔
1164
    const auto dst = reg_pack(cond.left);
36,882✔
1165
    if (const auto psrc_reg = std::get_if<Reg>(&cond.right)) {
36,882✔
1166
        const auto src_reg = *psrc_reg;
8,084✔
1167
        const auto src = reg_pack(src_reg);
8,084✔
1168
        if (type_inv.same_type(m_inv, cond.left, std::get<Reg>(cond.right))) {
8,084✔
1169
            m_inv = type_inv.join_over_types(m_inv, cond.left, [&](NumAbsDomain& inv, const type_encoding_t type) {
16,104✔
1170
                if (type == T_NUM) {
8,056✔
1171
                    for (const linear_constraint_t& cst :
11,121✔
1172
                         assume_cst_reg(m_inv, cond.op, cond.is64, dst.svalue, dst.uvalue, src.svalue, src.uvalue)) {
19,024✔
1173
                        inv += cst;
9,370✔
1174
                    }
6,436✔
1175
                } else {
1176
                    // Either pointers to a singleton region,
1177
                    // or an equality comparison on map descriptors/pointers to non-singleton locations
1178
                    if (const auto dst_offset = dom.get_type_offset_variable(cond.left, type)) {
1,620✔
1179
                        if (const auto src_offset = dom.get_type_offset_variable(src_reg, type)) {
1,620✔
1180
                            inv += assume_cst_offsets_reg(cond.op, dst_offset.value(), src_offset.value());
2,430✔
1181
                        }
1182
                    }
1183
                }
1184
            });
16,108✔
1185
        } else {
1186
            // We should only reach here if `--assume-assert` is off
1187
            assert(!thread_local_options.assume_assertions || dom.is_bottom());
32✔
1188
            // be sound in any case, it happens to flush out bugs:
1189
            m_inv.set_to_top();
32✔
1190
        }
1191
    } else {
1192
        const int64_t imm = gsl::narrow_cast<int64_t>(std::get<Imm>(cond.right).v);
28,798✔
1193
        for (const linear_constraint_t& cst : assume_cst_imm(m_inv, cond.op, cond.is64, dst.svalue, dst.uvalue, imm)) {
91,156✔
1194
            assume(cst);
93,537✔
1195
        }
28,798✔
1196
    }
1197
}
36,882✔
1198

1199
void ebpf_transformer::operator()(const Undefined& a) {}
2,362✔
1200

1201
// Simple truncation function usable with swap_endianness().
1202
template <class T>
1203
constexpr T truncate(T x) noexcept {
24✔
1204
    return x;
24✔
1205
}
1206

1207
void ebpf_transformer::operator()(const Un& stmt) {
1,812✔
1208
    const auto dst = reg_pack(stmt.dst);
1,812✔
1209
    auto swap_endianness = [&](const variable_t v, auto be_or_le) {
4,274✔
1210
        if (m_inv.entail(type_is_number(stmt.dst))) {
5,052✔
1211
            if (const auto n = m_inv.eval_interval(v).singleton()) {
6,786✔
1212
                if (n->fits_cast_to<int64_t>()) {
244✔
1213
                    m_inv.set(v, interval_t{be_or_le(n->cast_to<int64_t>())});
244✔
1214
                    return;
244✔
1215
                }
1216
            }
1217
        }
1218
        havoc(v);
3,124✔
1219
        havoc_offsets(stmt.dst);
3,124✔
1220
    };
1,812✔
1221
    // Swap bytes if needed.  For 64-bit types we need the weights to fit in a
1222
    // signed int64, but for smaller types we don't want sign extension,
1223
    // so we use unsigned which still fits in a signed int64.
1224
    switch (stmt.op) {
1,812✔
1225
    case Un::Op::BE16:
1,536✔
1226
        if (!thread_local_options.big_endian) {
1,536✔
1227
            swap_endianness(dst.svalue, boost::endian::endian_reverse<uint16_t>);
1,532✔
1228
            swap_endianness(dst.uvalue, boost::endian::endian_reverse<uint16_t>);
1,532✔
1229
        } else {
1230
            swap_endianness(dst.svalue, truncate<uint16_t>);
4✔
1231
            swap_endianness(dst.uvalue, truncate<uint16_t>);
4✔
1232
        }
1233
        break;
768✔
1234
    case Un::Op::BE32:
96✔
1235
        if (!thread_local_options.big_endian) {
96✔
1236
            swap_endianness(dst.svalue, boost::endian::endian_reverse<uint32_t>);
94✔
1237
            swap_endianness(dst.uvalue, boost::endian::endian_reverse<uint32_t>);
94✔
1238
        } else {
1239
            swap_endianness(dst.svalue, truncate<uint32_t>);
2✔
1240
            swap_endianness(dst.uvalue, truncate<uint32_t>);
2✔
1241
        }
1242
        break;
48✔
1243
    case Un::Op::BE64:
10✔
1244
        if (!thread_local_options.big_endian) {
10✔
1245
            swap_endianness(dst.svalue, boost::endian::endian_reverse<int64_t>);
8✔
1246
            swap_endianness(dst.uvalue, boost::endian::endian_reverse<uint64_t>);
8✔
1247
        }
1248
        break;
5✔
1249
    case Un::Op::LE16:
12✔
1250
        if (thread_local_options.big_endian) {
12✔
1251
            swap_endianness(dst.svalue, boost::endian::endian_reverse<uint16_t>);
2✔
1252
            swap_endianness(dst.uvalue, boost::endian::endian_reverse<uint16_t>);
2✔
1253
        } else {
1254
            swap_endianness(dst.svalue, truncate<uint16_t>);
10✔
1255
            swap_endianness(dst.uvalue, truncate<uint16_t>);
10✔
1256
        }
1257
        break;
6✔
1258
    case Un::Op::LE32:
8✔
1259
        if (thread_local_options.big_endian) {
8✔
1260
            swap_endianness(dst.svalue, boost::endian::endian_reverse<uint32_t>);
2✔
1261
            swap_endianness(dst.uvalue, boost::endian::endian_reverse<uint32_t>);
2✔
1262
        } else {
1263
            swap_endianness(dst.svalue, truncate<uint32_t>);
6✔
1264
            swap_endianness(dst.uvalue, truncate<uint32_t>);
6✔
1265
        }
1266
        break;
4✔
1267
    case Un::Op::LE64:
8✔
1268
        if (thread_local_options.big_endian) {
8✔
1269
            swap_endianness(dst.svalue, boost::endian::endian_reverse<int64_t>);
2✔
1270
            swap_endianness(dst.uvalue, boost::endian::endian_reverse<uint64_t>);
2✔
1271
        }
1272
        break;
4✔
1273
    case Un::Op::SWAP16:
10✔
1274
        swap_endianness(dst.svalue, boost::endian::endian_reverse<uint16_t>);
10✔
1275
        swap_endianness(dst.uvalue, boost::endian::endian_reverse<uint16_t>);
10✔
1276
        break;
5✔
1277
    case Un::Op::SWAP32:
6✔
1278
        swap_endianness(dst.svalue, boost::endian::endian_reverse<uint32_t>);
6✔
1279
        swap_endianness(dst.uvalue, boost::endian::endian_reverse<uint32_t>);
6✔
1280
        break;
3✔
1281
    case Un::Op::SWAP64:
6✔
1282
        swap_endianness(dst.svalue, boost::endian::endian_reverse<int64_t>);
6✔
1283
        swap_endianness(dst.uvalue, boost::endian::endian_reverse<uint64_t>);
6✔
1284
        break;
3✔
1285
    case Un::Op::NEG:
120✔
1286
        neg(dst.svalue, dst.uvalue, stmt.is64 ? 64 : 32);
132✔
1287
        havoc_offsets(stmt.dst);
120✔
1288
        break;
60✔
1289
    }
1290
}
1,812✔
1291

1292
void ebpf_transformer::operator()(const Exit& a) {
1,218✔
1293
    // Clean up any state for the current stack frame.
1294
    const std::string prefix = a.stack_frame_prefix;
1,218✔
1295
    if (prefix.empty()) {
1,218✔
1296
        return;
1,160✔
1297
    }
1298
    havoc_subprogram_stack(prefix);
58✔
1299
    restore_callee_saved_registers(prefix);
58✔
1300

1301
    // Restore r10.
1302
    constexpr Reg r10_reg{R10_STACK_POINTER};
58✔
1303
    add(r10_reg, EBPF_SUBPROGRAM_STACK_SIZE, 64);
58✔
1304
}
1,218✔
1305

1306
void ebpf_transformer::operator()(const Jmp&) const {
10,844✔
1307
    // This is a NOP. It only exists to hold the jump preconditions.
1308
}
10,844✔
1309

1310
void ebpf_transformer::operator()(const Packet& a) {
224✔
1311
    const auto reg = reg_pack(R0_RETURN_VALUE);
224✔
1312
    constexpr Reg r0_reg{R0_RETURN_VALUE};
224✔
1313
    type_inv.assign_type(m_inv, r0_reg, T_NUM);
224✔
1314
    havoc_offsets(r0_reg);
224✔
1315
    havoc(reg.svalue);
224✔
1316
    havoc(reg.uvalue);
224✔
1317
    scratch_caller_saved_registers();
224✔
1318
}
224✔
1319

1320
void ebpf_transformer::do_load_stack(NumAbsDomain& inv, const Reg& target_reg, const linear_expression_t& addr,
9,846✔
1321
                                     const int width, const Reg& src_reg) {
1322
    type_inv.assign_type(inv, target_reg, stack.load(inv, data_kind_t::types, addr, width));
9,846✔
1323
    using namespace crab::dsl_syntax;
4,923✔
1324
    if (inv.entail(width <= reg_pack(src_reg).stack_numeric_size)) {
19,692✔
1325
        type_inv.assign_type(inv, target_reg, T_NUM);
357✔
1326
    }
1327

1328
    const reg_pack_t& target = reg_pack(target_reg);
9,846✔
1329
    if (width == 1 || width == 2 || width == 4 || width == 8) {
9,846✔
1330
        // Use the addr before we havoc the destination register since we might be getting the
1331
        // addr from that same register.
1332
        const std::optional<linear_expression_t> sresult = stack.load(inv, data_kind_t::svalues, addr, width);
9,846✔
1333
        const std::optional<linear_expression_t> uresult = stack.load(inv, data_kind_t::uvalues, addr, width);
9,846✔
1334
        havoc_register(inv, target_reg);
9,846✔
1335
        inv.assign(target.svalue, sresult);
9,846✔
1336
        inv.assign(target.uvalue, uresult);
9,846✔
1337

1338
        if (type_inv.has_type(inv, target.type, T_CTX)) {
9,846✔
1339
            inv.assign(target.ctx_offset, stack.load(inv, data_kind_t::ctx_offsets, addr, width));
2,268✔
1340
        }
1341
        if (type_inv.has_type(inv, target.type, T_MAP) || type_inv.has_type(inv, target.type, T_MAP_PROGRAMS)) {
9,846✔
1342
            inv.assign(target.map_fd, stack.load(inv, data_kind_t::map_fds, addr, width));
267✔
1343
        }
1344
        if (type_inv.has_type(inv, target.type, T_PACKET)) {
9,846✔
1345
            inv.assign(target.packet_offset, stack.load(inv, data_kind_t::packet_offsets, addr, width));
156✔
1346
        }
1347
        if (type_inv.has_type(inv, target.type, T_SHARED)) {
9,846✔
1348
            inv.assign(target.shared_offset, stack.load(inv, data_kind_t::shared_offsets, addr, width));
1,119✔
1349
            inv.assign(target.shared_region_size, stack.load(inv, data_kind_t::shared_region_sizes, addr, width));
1,119✔
1350
        }
1351
        if (type_inv.has_type(inv, target.type, T_STACK)) {
9,846✔
1352
            inv.assign(target.stack_offset, stack.load(inv, data_kind_t::stack_offsets, addr, width));
165✔
1353
            inv.assign(target.stack_numeric_size, stack.load(inv, data_kind_t::stack_numeric_sizes, addr, width));
165✔
1354
        }
1355
    } else {
9,846✔
1356
        havoc_register(inv, target_reg);
×
1357
    }
1358
}
9,846✔
1359

1360
void ebpf_transformer::do_load_ctx(NumAbsDomain& inv, const Reg& target_reg, const linear_expression_t& addr_vague,
3,016✔
1361
                                   const int width) {
1362
    using namespace crab::dsl_syntax;
1,508✔
1363
    if (inv.is_bottom()) {
3,016✔
1364
        return;
1,420✔
1365
    }
1366

1367
    const ebpf_context_descriptor_t* desc = thread_local_program_info->type.context_descriptor;
3,016✔
1368

1369
    const reg_pack_t& target = reg_pack(target_reg);
3,016✔
1370

1371
    if (desc->end < 0) {
3,016✔
1372
        havoc_register(inv, target_reg);
290✔
1373
        type_inv.assign_type(inv, target_reg, T_NUM);
290✔
1374
        return;
290✔
1375
    }
1376

1377
    const interval_t interval = inv.eval_interval(addr_vague);
2,726✔
1378
    const std::optional<number_t> maybe_addr = interval.singleton();
2,726✔
1379
    havoc_register(inv, target_reg);
2,726✔
1380

1381
    const bool may_touch_ptr =
1,363✔
1382
        interval.contains(desc->data) || interval.contains(desc->meta) || interval.contains(desc->end);
4,649✔
1383

1384
    if (!maybe_addr) {
2,726✔
1385
        if (may_touch_ptr) {
2✔
1386
            type_inv.havoc_type(inv, target_reg);
2✔
1387
        } else {
1388
            type_inv.assign_type(inv, target_reg, T_NUM);
×
1389
        }
1390
        return;
2✔
1391
    }
1392

1393
    const number_t addr = *maybe_addr;
2,724✔
1394

1395
    // We use offsets for packet data, data_end, and meta during verification,
1396
    // but at runtime they will be 64-bit pointers.  We can use the offset values
1397
    // for verification like we use map_fd's as a proxy for maps which
1398
    // at runtime are actually 64-bit memory pointers.
1399
    const int offset_width = desc->end - desc->data;
2,724✔
1400
    if (addr == desc->data) {
2,724✔
1401
        if (width == offset_width) {
794✔
1402
            inv.assign(target.packet_offset, 0);
1,191✔
1403
        }
1404
    } else if (addr == desc->end) {
1,930✔
1405
        if (width == offset_width) {
788✔
1406
            inv.assign(target.packet_offset, variable_t::packet_size());
1,176✔
1407
        }
1408
    } else if (addr == desc->meta) {
1,142✔
1409
        if (width == offset_width) {
14✔
1410
            inv.assign(target.packet_offset, variable_t::meta_offset());
21✔
1411
        }
1412
    } else {
1413
        if (may_touch_ptr) {
1,128✔
1414
            type_inv.havoc_type(inv, target_reg);
×
1415
        } else {
1416
            type_inv.assign_type(inv, target_reg, T_NUM);
1,692✔
1417
        }
1418
        return;
1,128✔
1419
    }
1420
    if (width == offset_width) {
1,596✔
1421
        type_inv.assign_type(inv, target_reg, T_PACKET);
1,592✔
1422
        inv += 4098 <= target.svalue;
3,184✔
1423
        inv += target.svalue <= PTR_MAX;
2,388✔
1424
    }
1425
}
5,782✔
1426

1427
void ebpf_transformer::do_load_packet_or_shared(NumAbsDomain& inv, const Reg& target_reg,
8,956✔
1428
                                                const linear_expression_t& addr, const int width) {
1429
    if (inv.is_bottom()) {
8,956✔
1430
        return;
×
1431
    }
1432
    const reg_pack_t& target = reg_pack(target_reg);
8,956✔
1433

1434
    type_inv.assign_type(inv, target_reg, T_NUM);
8,956✔
1435
    havoc_register(inv, target_reg);
8,956✔
1436

1437
    // A 1 or 2 byte copy results in a limited range of values that may be used as array indices.
1438
    if (width == 1) {
8,956✔
1439
        const interval_t full = interval_t::full<uint8_t>();
3,326✔
1440
        inv.set(target.svalue, full);
3,326✔
1441
        inv.set(target.uvalue, full);
3,326✔
1442
    } else if (width == 2) {
8,956✔
1443
        const interval_t full = interval_t::full<uint16_t>();
1,978✔
1444
        inv.set(target.svalue, full);
1,978✔
1445
        inv.set(target.uvalue, full);
1,978✔
1446
    }
1,978✔
1447
}
1448

1449
void ebpf_transformer::do_load(const Mem& b, const Reg& target_reg) {
21,830✔
1450
    using namespace crab::dsl_syntax;
10,915✔
1451

1452
    const auto mem_reg = reg_pack(b.access.basereg);
21,830✔
1453
    const int width = b.access.width;
21,830✔
1454
    const int offset = b.access.offset;
21,830✔
1455

1456
    if (b.access.basereg.v == R10_STACK_POINTER) {
21,830✔
1457
        const linear_expression_t addr = mem_reg.stack_offset + offset;
14,313✔
1458
        do_load_stack(m_inv, target_reg, addr, width, b.access.basereg);
9,542✔
1459
        return;
9,542✔
1460
    }
9,542✔
1461

1462
    m_inv = type_inv.join_over_types(m_inv, b.access.basereg, [&](NumAbsDomain& inv, type_encoding_t type) {
24,576✔
1463
        switch (type) {
12,306✔
1464
        case T_UNINIT: return;
15✔
1465
        case T_MAP: return;
1466
        case T_MAP_PROGRAMS: return;
1467
        case T_NUM: return;
1468
        case T_CTX: {
3,016✔
1469
            linear_expression_t addr = mem_reg.ctx_offset + offset;
4,524✔
1470
            do_load_ctx(inv, target_reg, addr, width);
3,016✔
1471
            break;
3,016✔
1472
        }
3,016✔
1473
        case T_STACK: {
304✔
1474
            linear_expression_t addr = mem_reg.stack_offset + offset;
456✔
1475
            do_load_stack(inv, target_reg, addr, width, b.access.basereg);
304✔
1476
            break;
304✔
1477
        }
304✔
1478
        case T_PACKET: {
2,918✔
1479
            linear_expression_t addr = mem_reg.packet_offset + offset;
4,377✔
1480
            do_load_packet_or_shared(inv, target_reg, addr, width);
2,918✔
1481
            break;
2,918✔
1482
        }
2,918✔
1483
        default: {
6,038✔
1484
            linear_expression_t addr = mem_reg.shared_offset + offset;
9,057✔
1485
            do_load_packet_or_shared(inv, target_reg, addr, width);
6,038✔
1486
            break;
6,038✔
1487
        }
6,038✔
1488
        }
1489
    });
12,288✔
1490
}
1491

1492
void ebpf_transformer::do_store_stack(NumAbsDomain& inv, const linear_expression_t& addr, const int width,
24,206✔
1493
                                      const linear_expression_t& val_type, const linear_expression_t& val_svalue,
1494
                                      const linear_expression_t& val_uvalue,
1495
                                      const std::optional<reg_pack_t>& opt_val_reg) {
1496
    {
12,103✔
1497
        const std::optional<variable_t> var = stack.store_type(inv, addr, width, val_type);
24,206✔
1498
        type_inv.assign_type(inv, var, val_type);
24,206✔
1499
    }
1500
    if (width == 8) {
24,206✔
1501
        inv.assign(stack.store(inv, data_kind_t::svalues, addr, width, val_svalue), val_svalue);
9,714✔
1502
        inv.assign(stack.store(inv, data_kind_t::uvalues, addr, width, val_uvalue), val_uvalue);
9,714✔
1503

1504
        if (opt_val_reg && type_inv.has_type(m_inv, val_type, T_CTX)) {
9,714✔
1505
            inv.assign(stack.store(inv, data_kind_t::ctx_offsets, addr, width, opt_val_reg->ctx_offset),
399✔
1506
                       opt_val_reg->ctx_offset);
266✔
1507
        } else {
1508
            stack.havoc(inv, data_kind_t::ctx_offsets, addr, width);
9,448✔
1509
        }
1510

1511
        if (opt_val_reg &&
19,418✔
1512
            (type_inv.has_type(m_inv, val_type, T_MAP) || type_inv.has_type(m_inv, val_type, T_MAP_PROGRAMS))) {
19,379✔
1513
            inv.assign(stack.store(inv, data_kind_t::map_fds, addr, width, opt_val_reg->map_fd), opt_val_reg->map_fd);
117✔
1514
        } else {
1515
            stack.havoc(inv, data_kind_t::map_fds, addr, width);
9,636✔
1516
        }
1517

1518
        if (opt_val_reg && type_inv.has_type(m_inv, val_type, T_PACKET)) {
9,714✔
1519
            inv.assign(stack.store(inv, data_kind_t::packet_offsets, addr, width, opt_val_reg->packet_offset),
30✔
1520
                       opt_val_reg->packet_offset);
20✔
1521
        } else {
1522
            stack.havoc(inv, data_kind_t::packet_offsets, addr, width);
9,694✔
1523
        }
1524

1525
        if (opt_val_reg && type_inv.has_type(m_inv, val_type, T_SHARED)) {
9,714✔
1526
            inv.assign(stack.store(inv, data_kind_t::shared_offsets, addr, width, opt_val_reg->shared_offset),
171✔
1527
                       opt_val_reg->shared_offset);
114✔
1528
            inv.assign(stack.store(inv, data_kind_t::shared_region_sizes, addr, width, opt_val_reg->shared_region_size),
171✔
1529
                       opt_val_reg->shared_region_size);
114✔
1530
        } else {
1531
            stack.havoc(inv, data_kind_t::shared_region_sizes, addr, width);
9,600✔
1532
            stack.havoc(inv, data_kind_t::shared_offsets, addr, width);
9,600✔
1533
        }
1534

1535
        if (opt_val_reg && type_inv.has_type(m_inv, val_type, T_STACK)) {
9,714✔
1536
            inv.assign(stack.store(inv, data_kind_t::stack_offsets, addr, width, opt_val_reg->stack_offset),
39✔
1537
                       opt_val_reg->stack_offset);
26✔
1538
            inv.assign(stack.store(inv, data_kind_t::stack_numeric_sizes, addr, width, opt_val_reg->stack_numeric_size),
39✔
1539
                       opt_val_reg->stack_numeric_size);
26✔
1540
        } else {
1541
            stack.havoc(inv, data_kind_t::stack_offsets, addr, width);
9,688✔
1542
            stack.havoc(inv, data_kind_t::stack_numeric_sizes, addr, width);
9,688✔
1543
        }
1544
    } else {
1545
        if ((width == 1 || width == 2 || width == 4) && type_inv.get_type(m_inv, val_type) == T_NUM) {
14,492✔
1546
            // Keep track of numbers on the stack that might be used as array indices.
1547
            inv.assign(stack.store(inv, data_kind_t::svalues, addr, width, val_svalue), val_svalue);
14,362✔
1548
            inv.assign(stack.store(inv, data_kind_t::uvalues, addr, width, val_uvalue), val_uvalue);
14,362✔
1549
        } else {
1550
            stack.havoc(inv, data_kind_t::svalues, addr, width);
130✔
1551
            stack.havoc(inv, data_kind_t::uvalues, addr, width);
130✔
1552
        }
1553
        stack.havoc(inv, data_kind_t::ctx_offsets, addr, width);
14,492✔
1554
        stack.havoc(inv, data_kind_t::map_fds, addr, width);
14,492✔
1555
        stack.havoc(inv, data_kind_t::packet_offsets, addr, width);
14,492✔
1556
        stack.havoc(inv, data_kind_t::shared_offsets, addr, width);
14,492✔
1557
        stack.havoc(inv, data_kind_t::stack_offsets, addr, width);
14,492✔
1558
        stack.havoc(inv, data_kind_t::shared_region_sizes, addr, width);
14,492✔
1559
        stack.havoc(inv, data_kind_t::stack_numeric_sizes, addr, width);
14,492✔
1560
    }
1561

1562
    // Update stack_numeric_size for any stack type variables.
1563
    // stack_numeric_size holds the number of continuous bytes starting from stack_offset that are known to be numeric.
1564
    auto updated_lb = m_inv.eval_interval(addr).lb();
36,309✔
1565
    auto updated_ub = m_inv.eval_interval(addr).ub() + width;
60,515✔
1566
    for (const variable_t type_variable : variable_t::get_type_variables()) {
1,320,794✔
1567
        if (!type_inv.has_type(inv, type_variable, T_STACK)) {
1,296,588✔
1568
            continue;
667,923✔
1569
        }
1570
        const variable_t stack_offset_variable = variable_t::kind_var(data_kind_t::stack_offsets, type_variable);
628,665✔
1571
        const variable_t stack_numeric_size_variable =
314,333✔
1572
            variable_t::kind_var(data_kind_t::stack_numeric_sizes, type_variable);
628,665✔
1573

1574
        using namespace crab::dsl_syntax;
314,333✔
1575
        // See if the variable's numeric interval overlaps with changed bytes.
1576
        if (m_inv.intersect(dsl_syntax::operator<=(addr, stack_offset_variable + stack_numeric_size_variable)) &&
1,885,510✔
1577
            m_inv.intersect(operator>=(addr + width, stack_offset_variable))) {
1,256,360✔
1578
            havoc(stack_numeric_size_variable);
603,751✔
1579
            recompute_stack_numeric_size(m_inv, type_variable);
603,751✔
1580
        }
1581
    }
24,206✔
1582
}
24,206✔
1583

1584
void ebpf_transformer::operator()(const Mem& b) {
50,334✔
1585
    if (m_inv.is_bottom()) {
50,334✔
1586
        return;
1587
    }
1588
    if (const auto preg = std::get_if<Reg>(&b.value)) {
50,334✔
1589
        if (b.is_load) {
50,298✔
1590
            do_load(b, *preg);
21,830✔
1591
        } else {
1592
            const auto data_reg = reg_pack(*preg);
28,468✔
1593
            do_mem_store(b, data_reg.type, data_reg.svalue, data_reg.uvalue, data_reg);
28,468✔
1594
        }
1595
    } else {
1596
        const uint64_t imm = std::get<Imm>(b.value).v;
36✔
1597
        do_mem_store(b, T_NUM, to_signed(imm), imm, {});
36✔
1598
    }
1599
}
1600

1601
void ebpf_transformer::do_mem_store(const Mem& b, const linear_expression_t& val_type,
28,504✔
1602
                                    const linear_expression_t& val_svalue, const linear_expression_t& val_uvalue,
1603
                                    const std::optional<reg_pack_t>& opt_val_reg) {
1604
    if (m_inv.is_bottom()) {
28,504✔
1605
        return;
24,064✔
1606
    }
1607
    const int width = b.access.width;
28,504✔
1608
    const number_t offset{b.access.offset};
28,504✔
1609
    if (b.access.basereg.v == R10_STACK_POINTER) {
28,504✔
1610
        const auto r10_stack_offset = reg_pack(b.access.basereg).stack_offset;
24,064✔
1611
        const auto r10_interval = m_inv.eval_interval(r10_stack_offset);
24,064✔
1612
        if (r10_interval.is_singleton()) {
24,064✔
1613
            const int32_t stack_offset = r10_interval.singleton()->cast_to<int32_t>();
24,060✔
1614
            const number_t base_addr{stack_offset};
24,060✔
1615
            do_store_stack(m_inv, base_addr + offset, width, val_type, val_svalue, val_uvalue, opt_val_reg);
36,090✔
1616
        }
24,060✔
1617
        return;
24,064✔
1618
    }
24,064✔
1619
    m_inv = type_inv.join_over_types(m_inv, b.access.basereg, [&](NumAbsDomain& inv, const type_encoding_t type) {
8,880✔
1620
        if (type == T_STACK) {
4,440✔
1621
            const auto base_addr = linear_expression_t(dom.get_type_offset_variable(b.access.basereg, type).value());
219✔
1622
            do_store_stack(inv, dsl_syntax::operator+(base_addr, offset), width, val_type, val_svalue, val_uvalue,
219✔
1623
                           opt_val_reg);
1624
        }
146✔
1625
        // do nothing for any other type
1626
    });
8,880✔
1627
}
28,504✔
1628

1629
// Construct a Bin operation that does the main operation that a given Atomic operation does atomically.
1630
static Bin atomic_to_bin(const Atomic& a) {
84✔
1631
    Bin bin{.dst = Reg{R11_ATOMIC_SCRATCH}, .v = a.valreg, .is64 = a.access.width == sizeof(uint64_t), .lddw = false};
84✔
1632
    switch (a.op) {
84✔
1633
    case Atomic::Op::ADD: bin.op = Bin::Op::ADD; break;
16✔
1634
    case Atomic::Op::OR: bin.op = Bin::Op::OR; break;
16✔
1635
    case Atomic::Op::AND: bin.op = Bin::Op::AND; break;
16✔
1636
    case Atomic::Op::XOR: bin.op = Bin::Op::XOR; break;
16✔
1637
    case Atomic::Op::XCHG:
10✔
1638
    case Atomic::Op::CMPXCHG: bin.op = Bin::Op::MOV; break;
10✔
1639
    default: throw std::exception();
×
1640
    }
1641
    return bin;
84✔
1642
}
1643

1644
void ebpf_transformer::operator()(const Atomic& a) {
346✔
1645
    if (m_inv.is_bottom()) {
346✔
1646
        return;
262✔
1647
    }
1648
    if (!m_inv.entail(type_is_pointer(reg_pack(a.access.basereg))) ||
1,209✔
1649
        !m_inv.entail(type_is_number(reg_pack(a.valreg)))) {
855✔
1650
        return;
8✔
1651
    }
1652
    if (m_inv.entail(type_is_not_stack(reg_pack(a.access.basereg)))) {
495✔
1653
        // Shared memory regions are volatile so we can just havoc
1654
        // any register that will be updated.
1655
        if (a.op == Atomic::Op::CMPXCHG) {
246✔
1656
            havoc_register(m_inv, Reg{R0_RETURN_VALUE});
4✔
1657
        } else if (a.fetch) {
242✔
1658
            havoc_register(m_inv, a.valreg);
20✔
1659
        }
1660
        return;
246✔
1661
    }
1662

1663
    // Fetch the current value into the R11 pseudo-register.
1664
    constexpr Reg r11{R11_ATOMIC_SCRATCH};
84✔
1665
    (*this)(Mem{.access = a.access, .value = r11, .is_load = true});
84✔
1666

1667
    // Compute the new value in R11.
1668
    (*this)(atomic_to_bin(a));
84✔
1669

1670
    if (a.op == Atomic::Op::CMPXCHG) {
84✔
1671
        // For CMPXCHG, store the original value in r0.
1672
        (*this)(Mem{.access = a.access, .value = Reg{R0_RETURN_VALUE}, .is_load = true});
12✔
1673

1674
        // For the destination, there are 3 possibilities:
1675
        // 1) dst.value == r0.value : set R11 to valreg
1676
        // 2) dst.value != r0.value : don't modify R11
1677
        // 3) dst.value may or may not == r0.value : set R11 to the union of R11 and valreg
1678
        // For now we just havoc the value of R11.
1679
        havoc_register(m_inv, r11);
12✔
1680
    } else if (a.fetch) {
72✔
1681
        // For other FETCH operations, store the original value in the src register.
1682
        (*this)(Mem{.access = a.access, .value = a.valreg, .is_load = true});
40✔
1683
    }
1684

1685
    // Store the new value back in the original shared memory location.
1686
    // Note that do_mem_store() currently doesn't track shared memory values,
1687
    // but stack memory values are tracked and are legal here.
1688
    (*this)(Mem{.access = a.access, .value = r11, .is_load = false});
84✔
1689

1690
    // Clear the R11 pseudo-register.
1691
    havoc_register(m_inv, r11);
84✔
1692
    type_inv.havoc_type(m_inv, r11);
84✔
1693
}
1694

1695
void ebpf_transformer::operator()(const Call& call) {
13,508✔
1696
    using namespace crab::dsl_syntax;
6,754✔
1697
    if (m_inv.is_bottom()) {
13,508✔
1698
        return;
×
1699
    }
1700
    std::optional<Reg> maybe_fd_reg{};
13,508✔
1701
    for (ArgSingle param : call.singles) {
45,350✔
1702
        switch (param.kind) {
31,842✔
1703
        case ArgSingle::Kind::MAP_FD: maybe_fd_reg = param.reg; break;
23,377✔
1704
        case ArgSingle::Kind::ANYTHING:
12,193✔
1705
        case ArgSingle::Kind::MAP_FD_PROGRAMS:
1706
        case ArgSingle::Kind::PTR_TO_MAP_KEY:
1707
        case ArgSingle::Kind::PTR_TO_MAP_VALUE:
1708
        case ArgSingle::Kind::PTR_TO_CTX:
1709
            // Do nothing. We don't track the content of relevant memory regions
1710
            break;
12,193✔
1711
        }
1712
    }
1713
    for (ArgPair param : call.pairs) {
17,494✔
1714
        switch (param.kind) {
3,986✔
1715
        case ArgPair::Kind::PTR_TO_READABLE_MEM_OR_NULL:
1,564✔
1716
        case ArgPair::Kind::PTR_TO_READABLE_MEM:
1717
            // Do nothing. No side effect allowed.
1718
            break;
1,564✔
1719

1720
        case ArgPair::Kind::PTR_TO_WRITABLE_MEM: {
858✔
1721
            bool store_numbers = true;
858✔
1722
            auto variable = dom.get_type_offset_variable(param.mem);
858✔
1723
            if (!variable.has_value()) {
858✔
1724
                // checked by the checker
1725
                break;
1726
            }
1727
            variable_t addr = variable.value();
858✔
1728
            variable_t width = reg_pack(param.size).svalue;
858✔
1729

1730
            m_inv = type_inv.join_over_types(m_inv, param.mem, [&](NumAbsDomain& inv, const type_encoding_t type) {
1,716✔
1731
                if (type == T_STACK) {
858✔
1732
                    // Pointer to a memory region that the called function may change,
1733
                    // so we must havoc.
1734
                    stack.havoc(inv, data_kind_t::types, addr, width);
846✔
1735
                    stack.havoc(inv, data_kind_t::svalues, addr, width);
846✔
1736
                    stack.havoc(inv, data_kind_t::uvalues, addr, width);
846✔
1737
                    stack.havoc(inv, data_kind_t::ctx_offsets, addr, width);
846✔
1738
                    stack.havoc(inv, data_kind_t::map_fds, addr, width);
846✔
1739
                    stack.havoc(inv, data_kind_t::packet_offsets, addr, width);
846✔
1740
                    stack.havoc(inv, data_kind_t::shared_offsets, addr, width);
846✔
1741
                    stack.havoc(inv, data_kind_t::stack_offsets, addr, width);
846✔
1742
                    stack.havoc(inv, data_kind_t::shared_region_sizes, addr, width);
846✔
1743
                } else {
1744
                    store_numbers = false;
12✔
1745
                }
1746
            });
1,716✔
1747
            if (store_numbers) {
858✔
1748
                // Functions are not allowed to write sensitive data,
1749
                // and initialization is guaranteed
1750
                stack.store_numbers(m_inv, addr, width);
846✔
1751
            }
1752
        }
1753
        }
1754
    }
1755

1756
    constexpr Reg r0_reg{R0_RETURN_VALUE};
13,508✔
1757
    const auto r0_pack = reg_pack(r0_reg);
13,508✔
1758
    havoc(r0_pack.stack_numeric_size);
13,508✔
1759
    if (call.is_map_lookup) {
13,508✔
1760
        // This is the only way to get a null pointer
1761
        if (maybe_fd_reg) {
2,882✔
1762
            if (const auto map_type = dom.get_map_type(*maybe_fd_reg)) {
2,882✔
1763
                if (thread_local_program_info->platform->get_map_type(*map_type).value_type == EbpfMapValueType::MAP) {
2,876✔
1764
                    if (const auto inner_map_fd = dom.get_map_inner_map_fd(*maybe_fd_reg)) {
22✔
1765
                        do_load_mapfd(r0_reg, to_signed(*inner_map_fd), true);
22✔
1766
                        goto out;
22✔
1767
                    }
1768
                } else {
1769
                    assign_valid_ptr(r0_reg, true);
2,854✔
1770
                    assign(r0_pack.shared_offset, 0);
2,854✔
1771
                    m_inv.set(r0_pack.shared_region_size, dom.get_map_value_size(*maybe_fd_reg));
2,854✔
1772
                    type_inv.assign_type(m_inv, r0_reg, T_SHARED);
4,281✔
1773
                }
1774
            }
1775
        }
1776
        assign_valid_ptr(r0_reg, true);
2,858✔
1777
        assign(r0_pack.shared_offset, 0);
2,858✔
1778
        type_inv.assign_type(m_inv, r0_reg, T_SHARED);
4,287✔
1779
    } else {
1780
        havoc(r0_pack.svalue);
10,626✔
1781
        havoc(r0_pack.uvalue);
10,626✔
1782
        havoc_offsets(r0_reg);
10,626✔
1783
        type_inv.assign_type(m_inv, r0_reg, T_NUM);
15,939✔
1784
        // assume(r0_pack.value < 0); for INTEGER_OR_NO_RETURN_IF_SUCCEED.
1785
    }
1786
out:
13,506✔
1787
    scratch_caller_saved_registers();
13,506✔
1788
    if (call.reallocate_packet) {
13,506✔
1789
        forget_packet_pointers();
38✔
1790
    }
1791
}
1792

1793
void ebpf_transformer::operator()(const CallLocal& call) {
58✔
1794
    using namespace crab::dsl_syntax;
29✔
1795
    if (m_inv.is_bottom()) {
58✔
1796
        return;
×
1797
    }
1798
    save_callee_saved_registers(call.stack_frame_prefix);
58✔
1799

1800
    // Update r10.
1801
    constexpr Reg r10_reg{R10_STACK_POINTER};
58✔
1802
    add(r10_reg, -EBPF_SUBPROGRAM_STACK_SIZE, 64);
58✔
1803
}
1804

1805
void ebpf_transformer::operator()(const Callx& callx) {
50✔
1806
    using namespace crab::dsl_syntax;
25✔
1807
    if (m_inv.is_bottom()) {
50✔
1808
        return;
4✔
1809
    }
1810

1811
    // Look up the helper function id.
1812
    const reg_pack_t& reg = reg_pack(callx.func);
50✔
1813
    const auto src_interval = m_inv.eval_interval(reg.svalue);
50✔
1814
    if (const auto sn = src_interval.singleton()) {
50✔
1815
        if (sn->fits<int32_t>()) {
48✔
1816
            // We can now process it as if the id was immediate.
1817
            const int32_t imm = sn->cast_to<int32_t>();
48✔
1818
            if (!thread_local_program_info->platform->is_helper_usable(imm)) {
48✔
1819
                return;
4✔
1820
            }
1821
            const Call call = make_call(imm, *thread_local_program_info->platform);
44✔
1822
            (*this)(call);
44✔
1823
        }
44✔
1824
    }
50✔
1825
}
50✔
1826

1827
void ebpf_transformer::do_load_mapfd(const Reg& dst_reg, const int mapfd, const bool maybe_null) {
7,788✔
1828
    const EbpfMapDescriptor& desc = thread_local_program_info->platform->get_map_descriptor(mapfd);
7,788✔
1829
    const EbpfMapType& type = thread_local_program_info->platform->get_map_type(desc.type);
7,788✔
1830
    if (type.value_type == EbpfMapValueType::PROGRAM) {
7,788✔
1831
        type_inv.assign_type(m_inv, dst_reg, T_MAP_PROGRAMS);
504✔
1832
    } else {
1833
        type_inv.assign_type(m_inv, dst_reg, T_MAP);
11,178✔
1834
    }
1835
    const reg_pack_t& dst = reg_pack(dst_reg);
7,788✔
1836
    assign(dst.map_fd, mapfd);
7,788✔
1837
    assign_valid_ptr(dst_reg, maybe_null);
7,788✔
1838
}
7,788✔
1839

1840
void ebpf_transformer::operator()(const LoadMapFd& ins) { do_load_mapfd(ins.dst, ins.mapfd, false); }
7,766✔
1841

1842
void ebpf_transformer::assign_valid_ptr(const Reg& dst_reg, const bool maybe_null) {
13,500✔
1843
    using namespace crab::dsl_syntax;
6,750✔
1844
    const reg_pack_t& reg = reg_pack(dst_reg);
13,500✔
1845
    havoc(reg.svalue);
13,500✔
1846
    havoc(reg.uvalue);
13,500✔
1847
    if (maybe_null) {
13,500✔
1848
        m_inv += 0 <= reg.svalue;
11,468✔
1849
    } else {
1850
        m_inv += 0 < reg.svalue;
15,532✔
1851
    }
1852
    m_inv += reg.svalue <= PTR_MAX;
20,250✔
1853
    assign(reg.uvalue, reg.svalue);
13,500✔
1854
}
13,500✔
1855

1856
// If nothing is known of the stack_numeric_size,
1857
// try to recompute the stack_numeric_size.
1858
void ebpf_transformer::recompute_stack_numeric_size(NumAbsDomain& inv, const variable_t type_variable) const {
616,885✔
1859
    const variable_t stack_numeric_size_variable =
308,443✔
1860
        variable_t::kind_var(data_kind_t::stack_numeric_sizes, type_variable);
616,885✔
1861

1862
    if (!inv.eval_interval(stack_numeric_size_variable).is_top()) {
1,233,771✔
1863
        return;
×
1864
    }
1865

1866
    if (type_inv.has_type(inv, type_variable, T_STACK)) {
616,885✔
1867
        const int numeric_size =
308,002✔
1868
            stack.min_all_num_size(inv, variable_t::kind_var(data_kind_t::stack_offsets, type_variable));
616,003✔
1869
        if (numeric_size > 0) {
616,003✔
1870
            inv.assign(stack_numeric_size_variable, numeric_size);
18,315✔
1871
        }
1872
    }
1873
}
1874

1875
void ebpf_transformer::recompute_stack_numeric_size(NumAbsDomain& inv, const Reg& reg) const {
13,128✔
1876
    recompute_stack_numeric_size(inv, reg_pack(reg).type);
13,128✔
1877
}
13,128✔
1878

1879
void ebpf_transformer::add(const Reg& reg, const int imm, const int finite_width) {
16,732✔
1880
    const auto dst = reg_pack(reg);
16,732✔
1881
    const auto offset = dom.get_type_offset_variable(reg);
16,732✔
1882
    add_overflow(dst.svalue, dst.uvalue, imm, finite_width);
16,732✔
1883
    if (offset.has_value()) {
16,732✔
1884
        add(offset.value(), imm);
13,128✔
1885
        if (imm > 0) {
13,128✔
1886
            // Since the start offset is increasing but
1887
            // the end offset is not, the numeric size decreases.
1888
            sub(dst.stack_numeric_size, imm);
1,308✔
1889
        } else if (imm < 0) {
12,256✔
1890
            havoc(dst.stack_numeric_size);
12,256✔
1891
        }
1892
        recompute_stack_numeric_size(m_inv, reg);
13,128✔
1893
    }
1894
}
16,732✔
1895

1896
void ebpf_transformer::shl(const Reg& dst_reg, int imm, const int finite_width) {
4,144✔
1897
    const reg_pack_t dst = reg_pack(dst_reg);
4,144✔
1898

1899
    // The BPF ISA requires masking the imm.
1900
    imm &= finite_width - 1;
4,144✔
1901

1902
    if (m_inv.entail(type_is_number(dst))) {
6,216✔
1903
        const auto interval = m_inv.eval_interval(dst.uvalue);
4,142✔
1904
        if (interval.finite_size()) {
4,142✔
1905
            const number_t lb = interval.lb().number().value();
5,504✔
1906
            const number_t ub = interval.ub().number().value();
5,504✔
1907
            uint64_t lb_n = lb.cast_to<uint64_t>();
2,752✔
1908
            uint64_t ub_n = ub.cast_to<uint64_t>();
2,752✔
1909
            const uint64_t uint_max = finite_width == 64 ? uint64_t{std::numeric_limits<uint64_t>::max()}
2,938✔
1910
                                                         : uint64_t{std::numeric_limits<uint32_t>::max()};
186✔
1911
            if (lb_n >> (finite_width - imm) != ub_n >> (finite_width - imm)) {
2,752✔
1912
                // The bits that will be shifted out to the left are different,
1913
                // which means all combinations of remaining bits are possible.
1914
                lb_n = 0;
134✔
1915
                ub_n = uint_max << imm & uint_max;
134✔
1916
            } else {
1917
                // The bits that will be shifted out to the left are identical
1918
                // for all values in the interval, so we can safely shift left
1919
                // to get a new interval.
1920
                lb_n = lb_n << imm & uint_max;
2,618✔
1921
                ub_n = ub_n << imm & uint_max;
2,618✔
1922
            }
1923
            m_inv.set(dst.uvalue, interval_t{lb_n, ub_n});
2,752✔
1924
            m_inv.assign(dst.svalue, dst.uvalue);
2,752✔
1925
            overflow_signed(m_inv, dst.svalue, finite_width);
2,752✔
1926
            overflow_unsigned(m_inv, dst.uvalue, finite_width);
2,752✔
1927
            return;
2,752✔
1928
        }
2,752✔
1929
    }
4,142✔
1930
    shl_overflow(dst.svalue, dst.uvalue, imm);
1,392✔
1931
    havoc_offsets(dst_reg);
1,392✔
1932
}
1933

1934
void ebpf_transformer::lshr(const Reg& dst_reg, int imm, int finite_width) {
3,248✔
1935
    reg_pack_t dst = reg_pack(dst_reg);
3,248✔
1936

1937
    // The BPF ISA requires masking the imm.
1938
    imm &= finite_width - 1;
3,248✔
1939

1940
    if (m_inv.entail(type_is_number(dst))) {
4,872✔
1941
        auto interval = m_inv.eval_interval(dst.uvalue);
3,246✔
1942
        number_t lb_n{0};
3,246✔
1943
        number_t ub_n{std::numeric_limits<uint64_t>::max() >> imm};
3,246✔
1944
        if (interval.finite_size()) {
3,246✔
1945
            number_t lb = interval.lb().number().value();
3,172✔
1946
            number_t ub = interval.ub().number().value();
3,172✔
1947
            if (finite_width == 64) {
1,586✔
1948
                lb_n = lb.cast_to<uint64_t>() >> imm;
1,154✔
1949
                ub_n = ub.cast_to<uint64_t>() >> imm;
1,154✔
1950
            } else {
1951
                number_t lb_w = lb.cast_to_sint(finite_width);
432✔
1952
                number_t ub_w = ub.cast_to_sint(finite_width);
432✔
1953
                lb_n = lb_w.cast_to<uint32_t>() >> imm;
432✔
1954
                ub_n = ub_w.cast_to<uint32_t>() >> imm;
432✔
1955

1956
                // The interval must be valid since a signed range crossing 0
1957
                // was earlier converted to a full unsigned range.
1958
                assert(lb_n <= ub_n);
432✔
1959
            }
432✔
1960
        }
1,586✔
1961
        m_inv.set(dst.uvalue, interval_t{lb_n, ub_n});
8,115✔
1962
        m_inv.assign(dst.svalue, dst.uvalue);
3,246✔
1963
        overflow_signed(m_inv, dst.svalue, finite_width);
3,246✔
1964
        overflow_unsigned(m_inv, dst.uvalue, finite_width);
3,246✔
1965
        return;
3,246✔
1966
    }
4,869✔
1967
    havoc(dst.svalue);
2✔
1968
    havoc(dst.uvalue);
2✔
1969
    havoc_offsets(dst_reg);
2✔
1970
}
1971

1972
static int _movsx_bits(const Bin::Op op) {
4,136✔
1973
    switch (op) {
4,136✔
1974
    case Bin::Op::MOVSX8: return 8;
8✔
1975
    case Bin::Op::MOVSX16: return 16;
8✔
1976
    case Bin::Op::MOVSX32: return 32;
2,052✔
1977
    default: throw std::exception();
×
1978
    }
1979
}
1980

1981
void ebpf_transformer::sign_extend(const Reg& dst_reg, const linear_expression_t& right_svalue, const int finite_width,
2,086✔
1982
                                   const Bin::Op op) {
1983
    using namespace crab;
1,043✔
1984

1985
    const int bits = _movsx_bits(op);
2,086✔
1986
    const reg_pack_t dst = reg_pack(dst_reg);
2,086✔
1987
    interval_t right_interval = m_inv.eval_interval(right_svalue);
2,086✔
1988
    type_inv.assign_type(m_inv, dst_reg, T_NUM);
2,086✔
1989
    havoc_offsets(dst_reg);
2,086✔
1990
    const int64_t span = 1ULL << bits;
2,086✔
1991
    if (right_interval.ub() - right_interval.lb() >= span) {
4,172✔
1992
        // Interval covers the full space.
1993
        if (bits == 64) {
2,024✔
1994
            havoc(dst.svalue);
×
1995
            return;
×
1996
        }
1997
        right_interval = interval_t::signed_int(bits);
3,036✔
1998
    }
1999
    const int64_t mask = 1ULL << (bits - 1);
2,086✔
2000

2001
    // Sign extend each bound.
2002
    int64_t lb = right_interval.lb().number().value().cast_to<int64_t>();
4,172✔
2003
    lb &= span - 1;
2,086✔
2004
    lb = (lb ^ mask) - mask;
2,086✔
2005
    int64_t ub = right_interval.ub().number().value().cast_to<int64_t>();
4,172✔
2006
    ub &= span - 1;
2,086✔
2007
    ub = (ub ^ mask) - mask;
2,086✔
2008
    m_inv.set(dst.svalue, interval_t{lb, ub});
2,086✔
2009

2010
    if (finite_width) {
2,086✔
2011
        m_inv.assign(dst.uvalue, dst.svalue);
2,086✔
2012
        overflow_signed(m_inv, dst.svalue, finite_width);
2,086✔
2013
        overflow_unsigned(m_inv, dst.uvalue, finite_width);
2,086✔
2014
    }
2015
}
2,086✔
2016

2017
void ebpf_transformer::ashr(const Reg& dst_reg, const linear_expression_t& right_svalue, int finite_width) {
606✔
2018
    using namespace crab;
303✔
2019

2020
    reg_pack_t dst = reg_pack(dst_reg);
606✔
2021
    if (m_inv.entail(type_is_number(dst))) {
909✔
2022
        interval_t left_interval = interval_t::bottom();
596✔
2023
        interval_t right_interval = interval_t::bottom();
596✔
2024
        interval_t left_interval_positive = interval_t::bottom();
596✔
2025
        interval_t left_interval_negative = interval_t::bottom();
596✔
2026
        get_signed_intervals(m_inv, finite_width == 64, dst.svalue, dst.uvalue, right_svalue, left_interval,
596✔
2027
                             right_interval, left_interval_positive, left_interval_negative);
2028
        if (auto sn = right_interval.singleton()) {
596✔
2029
            // The BPF ISA requires masking the imm.
2030
            int64_t imm = sn->cast_to<int64_t>() & (finite_width - 1);
594✔
2031

2032
            int64_t lb_n = std::numeric_limits<int64_t>::min() >> imm;
594✔
2033
            int64_t ub_n = std::numeric_limits<int64_t>::max() >> imm;
594✔
2034
            if (left_interval.finite_size()) {
594✔
2035
                const auto [lb, ub] = left_interval.pair_number();
594✔
2036
                if (finite_width == 64) {
594✔
2037
                    lb_n = lb.cast_to<int64_t>() >> imm;
546✔
2038
                    ub_n = ub.cast_to<int64_t>() >> imm;
546✔
2039
                } else {
2040
                    number_t lb_w = lb.cast_to_sint(finite_width) >> gsl::narrow<int>(imm);
72✔
2041
                    number_t ub_w = ub.cast_to_sint(finite_width) >> gsl::narrow<int>(imm);
72✔
2042
                    if (lb_w.cast_to<uint32_t>() <= ub_w.cast_to<uint32_t>()) {
48✔
2043
                        lb_n = lb_w.cast_to<uint32_t>();
28✔
2044
                        ub_n = ub_w.cast_to<uint32_t>();
28✔
2045
                    }
2046
                }
48✔
2047
            }
594✔
2048
            m_inv.set(dst.svalue, interval_t{lb_n, ub_n});
594✔
2049
            m_inv.assign(dst.uvalue, dst.svalue);
594✔
2050
            overflow_signed(m_inv, dst.svalue, finite_width);
594✔
2051
            overflow_unsigned(m_inv, dst.uvalue, finite_width);
594✔
2052
            return;
594✔
2053
        }
596✔
2054
    }
2,381✔
2055
    havoc(dst.svalue);
12✔
2056
    havoc(dst.uvalue);
12✔
2057
    havoc_offsets(dst_reg);
12✔
2058
}
2059

2060
static void apply(NumAbsDomain& inv, const binop_t& op, const variable_t x, const variable_t y, const variable_t z) {
542✔
2061
    inv.apply(op, x, y, z, 0);
542✔
2062
}
271✔
2063

2064
void ebpf_transformer::operator()(const Bin& bin) {
109,318✔
2065
    using namespace crab::dsl_syntax;
54,659✔
2066

2067
    auto dst = reg_pack(bin.dst);
109,318✔
2068
    int finite_width = bin.is64 ? 64 : 32;
109,318✔
2069

2070
    if (auto pimm = std::get_if<Imm>(&bin.v)) {
109,318✔
2071
        // dst += K
2072
        int64_t imm;
32,118✔
2073
        if (bin.is64) {
64,236✔
2074
            // Use the full signed value.
2075
            imm = to_signed(pimm->v);
51,692✔
2076
        } else {
2077
            // Use only the low 32 bits of the value.
2078
            imm = gsl::narrow_cast<int32_t>(pimm->v);
12,544✔
2079
            bitwise_and(dst.svalue, dst.uvalue, std::numeric_limits<uint32_t>::max());
12,544✔
2080
            // If this is a 32-bit operation and the destination is not a number, forget everything about the register.
2081
            if (!type_inv.has_type(m_inv, bin.dst, T_NUM)) {
12,544✔
2082
                havoc_register(m_inv, bin.dst);
500✔
2083
                havoc_offsets(bin.dst);
500✔
2084
                havoc(dst.type);
500✔
2085
            }
2086
        }
2087
        switch (bin.op) {
64,236✔
2088
        case Bin::Op::MOV:
31,958✔
2089
            assign(dst.svalue, imm);
31,958✔
2090
            assign(dst.uvalue, imm);
31,958✔
2091
            overflow_unsigned(m_inv, dst.uvalue, bin.is64 ? 64 : 32);
34,915✔
2092
            type_inv.assign_type(m_inv, bin.dst, T_NUM);
31,958✔
2093
            havoc_offsets(bin.dst);
31,958✔
2094
            break;
15,979✔
2095
        case Bin::Op::MOVSX8:
×
2096
        case Bin::Op::MOVSX16:
2097
        case Bin::Op::MOVSX32: CRAB_ERROR("Unsupported operation");
×
2098
        case Bin::Op::ADD:
16,594✔
2099
            if (imm == 0) {
16,594✔
2100
                return;
130✔
2101
            }
2102
            add(bin.dst, gsl::narrow<int>(imm), finite_width);
16,590✔
2103
            break;
8,295✔
2104
        case Bin::Op::SUB:
28✔
2105
            if (imm == 0) {
28✔
2106
                return;
1✔
2107
            }
2108
            add(bin.dst, gsl::narrow<int>(-imm), finite_width);
26✔
2109
            break;
13✔
2110
        case Bin::Op::MUL:
90✔
2111
            mul(dst.svalue, dst.uvalue, imm, finite_width);
90✔
2112
            havoc_offsets(bin.dst);
90✔
2113
            break;
45✔
2114
        case Bin::Op::UDIV:
1,130✔
2115
            udiv(dst.svalue, dst.uvalue, imm, finite_width);
1,130✔
2116
            havoc_offsets(bin.dst);
1,130✔
2117
            break;
565✔
2118
        case Bin::Op::UMOD:
24✔
2119
            urem(dst.svalue, dst.uvalue, imm, finite_width);
24✔
2120
            havoc_offsets(bin.dst);
24✔
2121
            break;
12✔
2122
        case Bin::Op::SDIV:
18✔
2123
            sdiv(dst.svalue, dst.uvalue, imm, finite_width);
18✔
2124
            havoc_offsets(bin.dst);
18✔
2125
            break;
9✔
2126
        case Bin::Op::SMOD:
34✔
2127
            srem(dst.svalue, dst.uvalue, imm, finite_width);
34✔
2128
            havoc_offsets(bin.dst);
34✔
2129
            break;
17✔
2130
        case Bin::Op::OR:
1,542✔
2131
            bitwise_or(dst.svalue, dst.uvalue, imm);
1,542✔
2132
            havoc_offsets(bin.dst);
1,542✔
2133
            break;
771✔
2134
        case Bin::Op::AND:
4,710✔
2135
            // FIX: what to do with ptr&-8 as in counter/simple_loop_unrolled?
2136
            bitwise_and(dst.svalue, dst.uvalue, imm);
4,710✔
2137
            if (gsl::narrow<int32_t>(imm) > 0) {
4,710✔
2138
                // AND with immediate is only a 32-bit operation so svalue and uvalue are the same.
2139
                assume(dst.svalue <= imm);
6,369✔
2140
                assume(dst.uvalue <= imm);
6,369✔
2141
                assume(0 <= dst.svalue);
8,492✔
2142
                assume(0 <= dst.uvalue);
8,492✔
2143
            }
2144
            havoc_offsets(bin.dst);
4,710✔
2145
            break;
2,355✔
2146
        case Bin::Op::LSH: shl(bin.dst, gsl::narrow<int32_t>(imm), finite_width); break;
4,098✔
2147
        case Bin::Op::RSH: lshr(bin.dst, gsl::narrow<int32_t>(imm), finite_width); break;
3,214✔
2148
        case Bin::Op::ARSH: ashr(bin.dst, gsl::narrow<int32_t>(imm), finite_width); break;
572✔
2149
        case Bin::Op::XOR:
224✔
2150
            bitwise_xor(dst.svalue, dst.uvalue, imm);
224✔
2151
            havoc_offsets(bin.dst);
224✔
2152
            break;
112✔
2153
        }
2154
    } else {
2155
        // dst op= src
2156
        auto src_reg = std::get<Reg>(bin.v);
45,082✔
2157
        auto src = reg_pack(src_reg);
45,082✔
2158
        switch (bin.op) {
45,082✔
2159
        case Bin::Op::ADD: {
2,726✔
2160
            if (type_inv.same_type(m_inv, bin.dst, std::get<Reg>(bin.v))) {
2,726✔
2161
                // both must be numbers
2162
                add_overflow(dst.svalue, dst.uvalue, src.svalue, finite_width);
2,180✔
2163
            } else {
2164
                // Here we're not sure that lhs and rhs are the same type; they might be.
2165
                // But previous assertions should fail unless we know that exactly one of lhs or rhs is a pointer.
2166
                m_inv =
546✔
2167
                    type_inv.join_over_types(m_inv, bin.dst, [&](NumAbsDomain& inv, const type_encoding_t dst_type) {
1,092✔
2168
                        inv = type_inv.join_over_types(
1,100✔
2169
                            inv, src_reg, [&](NumAbsDomain& inv, const type_encoding_t src_type) {
550✔
2170
                                if (dst_type == T_NUM && src_type != T_NUM) {
556✔
2171
                                    // num += ptr
2172
                                    type_inv.assign_type(inv, bin.dst, src_type);
12✔
2173
                                    if (const auto dst_offset = dom.get_type_offset_variable(bin.dst, src_type)) {
12✔
2174
                                        crab::apply(inv, arith_binop_t::ADD, dst_offset.value(), dst.svalue,
20✔
2175
                                                    dom.get_type_offset_variable(src_reg, src_type).value());
20✔
2176
                                    }
2177
                                    if (src_type == T_SHARED) {
12✔
2178
                                        inv.assign(dst.shared_region_size, src.shared_region_size);
×
2179
                                    }
2180
                                } else if (dst_type != T_NUM && src_type == T_NUM) {
550✔
2181
                                    // ptr += num
2182
                                    type_inv.assign_type(inv, bin.dst, dst_type);
532✔
2183
                                    if (const auto dst_offset = dom.get_type_offset_variable(bin.dst, dst_type)) {
532✔
2184
                                        crab::apply(inv, arith_binop_t::ADD, dst_offset.value(), dst_offset.value(),
798✔
2185
                                                    src.svalue);
532✔
2186
                                        if (dst_type == T_STACK) {
532✔
2187
                                            // Reduce the numeric size.
2188
                                            using namespace crab::dsl_syntax;
8✔
2189
                                            if (m_inv.intersect(src.svalue < 0)) {
24✔
2190
                                                inv -= dst.stack_numeric_size;
2✔
2191
                                                recompute_stack_numeric_size(inv, dst.type);
2✔
2192
                                            } else {
2193
                                                apply_signed(inv, arith_binop_t::SUB, dst.stack_numeric_size,
14✔
2194
                                                             dst.stack_numeric_size, dst.stack_numeric_size, src.svalue,
14✔
2195
                                                             0);
2196
                                            }
2197
                                        }
2198
                                    }
2199
                                } else if (dst_type == T_NUM && src_type == T_NUM) {
544✔
2200
                                    // dst and src don't necessarily have the same type, but among the possibilities
2201
                                    // enumerated is the case where they are both numbers.
2202
                                    apply_signed(inv, arith_binop_t::ADD, dst.svalue, dst.uvalue, dst.svalue,
2✔
2203
                                                 src.svalue, finite_width);
2✔
2204
                                } else {
2205
                                    // We ignore the cases here that do not match the assumption described
2206
                                    // above.  Joining bottom with another results will leave the other
2207
                                    // results unchanged.
2208
                                    inv.set_to_bottom();
10✔
2209
                                }
2210
                            });
1,106✔
2211
                    });
1,096✔
2212
                // careful: change dst.value only after dealing with offset
2213
                apply_signed(m_inv, arith_binop_t::ADD, dst.svalue, dst.uvalue, dst.svalue, src.svalue, finite_width);
546✔
2214
            }
2215
            break;
1,363✔
2216
        }
2217
        case Bin::Op::SUB: {
480✔
2218
            if (type_inv.same_type(m_inv, bin.dst, std::get<Reg>(bin.v))) {
480✔
2219
                // src and dest have the same type.
2220
                m_inv = type_inv.join_over_types(m_inv, bin.dst, [&](NumAbsDomain& inv, const type_encoding_t type) {
940✔
2221
                    switch (type) {
474✔
2222
                    case T_NUM:
264✔
2223
                        // This is: sub_overflow(inv, dst.value, src.value, finite_width);
2224
                        apply_signed(inv, arith_binop_t::SUB, dst.svalue, dst.uvalue, dst.svalue, src.svalue,
264✔
2225
                                     finite_width);
2226
                        type_inv.assign_type(inv, bin.dst, T_NUM);
264✔
2227
                        crab::havoc_offsets(inv, bin.dst);
264✔
2228
                        break;
264✔
2229
                    default:
210✔
2230
                        // ptr -= ptr
2231
                        // Assertions should make sure we only perform this on non-shared pointers.
2232
                        if (const auto dst_offset = dom.get_type_offset_variable(bin.dst, type)) {
210✔
2233
                            apply_signed(inv, arith_binop_t::SUB, dst.svalue, dst.uvalue, dst_offset.value(),
210✔
2234
                                         dom.get_type_offset_variable(src_reg, type).value(), finite_width);
315✔
2235
                            inv -= dst_offset.value();
210✔
2236
                        }
2237
                        crab::havoc_offsets(inv, bin.dst);
210✔
2238
                        type_inv.assign_type(inv, bin.dst, T_NUM);
210✔
2239
                        break;
210✔
2240
                    }
2241
                });
944✔
2242
            } else {
2243
                // We're not sure that lhs and rhs are the same type.
2244
                // Either they're different, or at least one is not a singleton.
2245
                if (type_inv.get_type(m_inv, std::get<Reg>(bin.v)) != T_NUM) {
10✔
2246
                    type_inv.havoc_type(m_inv, bin.dst);
6✔
2247
                    havoc(dst.svalue);
6✔
2248
                    havoc(dst.uvalue);
6✔
2249
                    havoc_offsets(bin.dst);
6✔
2250
                } else {
2251
                    sub_overflow(dst.svalue, dst.uvalue, src.svalue, finite_width);
4✔
2252
                    if (auto dst_offset = dom.get_type_offset_variable(bin.dst)) {
4✔
2253
                        sub(dst_offset.value(), src.svalue);
4✔
2254
                        if (type_inv.has_type(m_inv, dst.type, T_STACK)) {
4✔
2255
                            // Reduce the numeric size.
2256
                            using namespace crab::dsl_syntax;
2✔
2257
                            if (m_inv.intersect(src.svalue > 0)) {
8✔
2258
                                m_inv -= dst.stack_numeric_size;
4✔
2259
                                recompute_stack_numeric_size(m_inv, dst.type);
4✔
2260
                            } else {
2261
                                crab::apply(m_inv, arith_binop_t::ADD, dst.stack_numeric_size, dst.stack_numeric_size,
×
2262
                                            src.svalue);
2263
                            }
2264
                        }
2265
                    }
2266
                }
2267
            }
2268
            break;
240✔
2269
        }
2270
        case Bin::Op::MUL:
74✔
2271
            mul(dst.svalue, dst.uvalue, src.svalue, finite_width);
74✔
2272
            havoc_offsets(bin.dst);
74✔
2273
            break;
37✔
2274
        case Bin::Op::UDIV:
108✔
2275
            udiv(dst.svalue, dst.uvalue, src.uvalue, finite_width);
108✔
2276
            havoc_offsets(bin.dst);
108✔
2277
            break;
54✔
2278
        case Bin::Op::UMOD:
36✔
2279
            urem(dst.svalue, dst.uvalue, src.uvalue, finite_width);
36✔
2280
            havoc_offsets(bin.dst);
36✔
2281
            break;
18✔
2282
        case Bin::Op::SDIV:
36✔
2283
            sdiv(dst.svalue, dst.uvalue, src.svalue, finite_width);
36✔
2284
            havoc_offsets(bin.dst);
36✔
2285
            break;
18✔
2286
        case Bin::Op::SMOD:
48✔
2287
            srem(dst.svalue, dst.uvalue, src.svalue, finite_width);
48✔
2288
            havoc_offsets(bin.dst);
48✔
2289
            break;
24✔
2290
        case Bin::Op::OR:
4,440✔
2291
            bitwise_or(dst.svalue, dst.uvalue, src.uvalue, finite_width);
4,440✔
2292
            havoc_offsets(bin.dst);
4,440✔
2293
            break;
2,220✔
2294
        case Bin::Op::AND:
468✔
2295
            bitwise_and(dst.svalue, dst.uvalue, src.uvalue, finite_width);
468✔
2296
            havoc_offsets(bin.dst);
468✔
2297
            break;
234✔
2298
        case Bin::Op::LSH:
592✔
2299
            if (m_inv.entail(type_is_number(src_reg))) {
888✔
2300
                auto src_interval = m_inv.eval_interval(src.uvalue);
588✔
2301
                if (std::optional<number_t> sn = src_interval.singleton()) {
588✔
2302
                    // truncate to uint64?
2303
                    uint64_t imm = sn->cast_to<uint64_t>() & (bin.is64 ? 63 : 31);
46✔
2304
                    if (imm <= std::numeric_limits<int32_t>::max()) {
46✔
2305
                        if (!bin.is64) {
46✔
2306
                            // Use only the low 32 bits of the value.
2307
                            bitwise_and(dst.svalue, dst.uvalue, std::numeric_limits<uint32_t>::max());
39✔
2308
                        }
2309
                        shl(bin.dst, gsl::narrow_cast<int32_t>(imm), finite_width);
46✔
2310
                        break;
46✔
2311
                    }
2312
                }
588✔
2313
            }
588✔
2314
            shl_overflow(dst.svalue, dst.uvalue, src.uvalue);
546✔
2315
            havoc_offsets(bin.dst);
546✔
2316
            break;
273✔
2317
        case Bin::Op::RSH:
80✔
2318
            if (m_inv.entail(type_is_number(src_reg))) {
120✔
2319
                auto src_interval = m_inv.eval_interval(src.uvalue);
78✔
2320
                if (std::optional<number_t> sn = src_interval.singleton()) {
78✔
2321
                    uint64_t imm = sn->cast_to<uint64_t>() & (bin.is64 ? 63 : 31);
34✔
2322
                    if (imm <= std::numeric_limits<int32_t>::max()) {
34✔
2323
                        if (!bin.is64) {
34✔
2324
                            // Use only the low 32 bits of the value.
2325
                            bitwise_and(dst.svalue, dst.uvalue, std::numeric_limits<uint32_t>::max());
24✔
2326
                        }
2327
                        lshr(bin.dst, gsl::narrow_cast<int32_t>(imm), finite_width);
34✔
2328
                        break;
34✔
2329
                    }
2330
                }
78✔
2331
            }
78✔
2332
            havoc(dst.svalue);
46✔
2333
            havoc(dst.uvalue);
46✔
2334
            havoc_offsets(bin.dst);
46✔
2335
            break;
23✔
2336
        case Bin::Op::ARSH:
36✔
2337
            if (m_inv.entail(type_is_number(src_reg))) {
54✔
2338
                ashr(bin.dst, src.svalue, finite_width);
34✔
2339
                break;
34✔
2340
            }
2341
            havoc(dst.svalue);
2✔
2342
            havoc(dst.uvalue);
2✔
2343
            havoc_offsets(bin.dst);
2✔
2344
            break;
1✔
2345
        case Bin::Op::XOR:
248✔
2346
            bitwise_xor(dst.svalue, dst.uvalue, src.uvalue, finite_width);
248✔
2347
            havoc_offsets(bin.dst);
248✔
2348
            break;
124✔
2349
        case Bin::Op::MOVSX8:
2,098✔
2350
        case Bin::Op::MOVSX16:
1,049✔
2351
        case Bin::Op::MOVSX32:
1,049✔
2352
            // Keep relational information if operation is a no-op.
2353
            if (dst.svalue == src.svalue &&
3,123✔
2354
                m_inv.eval_interval(dst.svalue) <= interval_t::signed_int(_movsx_bits(bin.op))) {
5,173✔
2355
                return;
124✔
2356
            }
2357
            if (m_inv.entail(type_is_number(src_reg))) {
3,141✔
2358
                sign_extend(bin.dst, src.svalue, finite_width, bin.op);
2,086✔
2359
                break;
2,086✔
2360
            }
2361
            havoc(dst.svalue);
8✔
2362
            havoc(dst.uvalue);
8✔
2363
            havoc_offsets(bin.dst);
22,483✔
2364
            break;
4✔
2365
        case Bin::Op::MOV:
33,612✔
2366
            // Keep relational information if operation is a no-op.
2367
            if (dst.svalue == src.svalue &&
34,246✔
2368
                m_inv.eval_interval(dst.uvalue) <= interval_t::unsigned_int(bin.is64 ? 64 : 32)) {
36,141✔
2369
                return;
60✔
2370
            }
2371
            assign(dst.svalue, src.svalue);
33,492✔
2372
            assign(dst.uvalue, src.uvalue);
33,492✔
2373
            havoc_offsets(bin.dst);
33,492✔
2374
            m_inv = type_inv.join_over_types(m_inv, src_reg, [&](NumAbsDomain& inv, const type_encoding_t type) {
66,984✔
2375
                switch (type) {
33,510✔
2376
                case T_CTX:
4,540✔
2377
                    if (bin.is64) {
4,540✔
2378
                        inv.assign(dst.type, type);
4,536✔
2379
                        inv.assign(dst.ctx_offset, src.ctx_offset);
6,804✔
2380
                    }
2381
                    break;
2,270✔
2382
                case T_MAP:
202✔
2383
                case T_MAP_PROGRAMS:
101✔
2384
                    if (bin.is64) {
202✔
2385
                        inv.assign(dst.type, type);
198✔
2386
                        inv.assign(dst.map_fd, src.map_fd);
297✔
2387
                    }
2388
                    break;
101✔
2389
                case T_PACKET:
872✔
2390
                    if (bin.is64) {
872✔
2391
                        inv.assign(dst.type, type);
870✔
2392
                        inv.assign(dst.packet_offset, src.packet_offset);
1,305✔
2393
                    }
2394
                    break;
436✔
2395
                case T_SHARED:
776✔
2396
                    if (bin.is64) {
776✔
2397
                        inv.assign(dst.type, type);
774✔
2398
                        inv.assign(dst.shared_region_size, src.shared_region_size);
774✔
2399
                        inv.assign(dst.shared_offset, src.shared_offset);
1,161✔
2400
                    }
2401
                    break;
388✔
2402
                case T_STACK:
12,992✔
2403
                    if (bin.is64) {
12,992✔
2404
                        inv.assign(dst.type, type);
12,990✔
2405
                        inv.assign(dst.stack_offset, src.stack_offset);
12,990✔
2406
                        inv.assign(dst.stack_numeric_size, src.stack_numeric_size);
19,485✔
2407
                    }
2408
                    break;
6,496✔
2409
                default: inv.assign(dst.type, type); break;
21,192✔
2410
                }
2411
            });
33,492✔
2412
            if (bin.is64) {
33,492✔
2413
                // Add dst.type=src.type invariant.
2414
                if (bin.dst.v != std::get<Reg>(bin.v).v || type_inv.get_type(m_inv, dst.type) == T_UNINIT) {
27,261✔
2415
                    // Only forget the destination type if we're copying from a different register,
2416
                    // or from the same uninitialized register.
2417
                    havoc(dst.type);
27,258✔
2418
                }
2419
                type_inv.assign_type(m_inv, bin.dst, std::get<Reg>(bin.v));
27,260✔
2420
            }
2421
            break;
16,746✔
2422
        }
2423
    }
2424
    if (!bin.is64) {
109,188✔
2425
        bitwise_and(dst.svalue, dst.uvalue, std::numeric_limits<uint32_t>::max());
31,599✔
2426
    }
2427
}
2428

2429
void ebpf_transformer::initialize_loop_counter(const label_t& label) {
32✔
2430
    m_inv.assign(variable_t::loop_counter(to_string(label)), 0);
48✔
2431
}
32✔
2432

2433
void ebpf_transformer::operator()(const IncrementLoopCounter& ins) {
164✔
2434
    const auto counter = variable_t::loop_counter(to_string(ins.name));
164✔
2435
    this->add(counter, 1);
164✔
2436
}
164✔
2437

2438
void ebpf_domain_initialize_loop_counter(ebpf_domain_t& dom, const label_t& label) {
32✔
2439
    ebpf_transformer{dom}.initialize_loop_counter(label);
32✔
2440
}
32✔
2441

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