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

vbpf / ebpf-verifier / 12951530094

24 Jan 2025 02:26PM UTC coverage: 88.133% (-0.04%) from 88.169%
12951530094

push

github

web-flow
add support for 64bit immediate with type 2 (#820)

Add support for 64bit immediate with type 2

From the ISA RFC:
5.4.  64-bit immediate instructions

   Instructions with the IMM 'mode' modifier use the wide instruction
   encoding defined in Instruction encoding (Section 3), and use the
   'src_reg' field of the basic instruction to hold an opcode subtype.

   The following table defines a set of {IMM, DW, LD} instructions with
   opcode subtypes in the 'src_reg' field, using new terms such as "map"
   defined further below:

    +=========+================================+==========+==========+
    | src_reg | pseudocode                     | imm type | dst type |
    +=========+================================+==========+==========+
    | 0x0     | dst = (next_imm << 32) | imm   | integer  | integer  |
    +---------+--------------------------------+----------+----------+
    | 0x1     | dst = map_by_fd(imm)           | map fd   | map      |
    +---------+--------------------------------+----------+----------+
    | 0x2     | dst = map_val(map_by_fd(imm))  | map fd   | data     |
    |         | + next_imm                     |          | address  |
    +---------+--------------------------------+----------+----------+
    | 0x3     | dst = var_addr(imm)            | variable | data     |
    |         |                                | id       | address  |
    +---------+--------------------------------+----------+----------+
    | 0x4     | dst = code_addr(imm)           | integer  | code     |
    |         |                                |          | address  |
    +---------+--------------------------------+----------+----------+
    | 0x5     | dst = map_by_idx(imm)          | map      | map      |
    |         |                                | index    |          |
    +---------+--------------------------------+----------+----------+
    | 0x6     | dst = map_val(map_by_idx(imm)) | map      | data     |
    |         | + next_imm    ... (continued)

81 of 92 new or added lines in 12 files covered. (88.04%)

7 existing lines in 1 file now uncovered.

8533 of 9682 relevant lines covered (88.13%)

7382636.56 hits per line

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

85.51
/src/asm_marshal.cpp
1
// Copyright (c) Prevail Verifier contributors.
2
// SPDX-License-Identifier: MIT
3
#include <cassert>
4
#include <map>
5
#include <variant>
6
#include <vector>
7

8
#include "asm_marshal.hpp"
9
#include "crab_utils/num_safety.hpp"
10

11
using std::vector;
12

13
static uint8_t op(const Condition::Op op) {
88✔
14
    using Op = Condition::Op;
44✔
15
    switch (op) {
88✔
16
    case Op::EQ: return 0x1;
4✔
17
    case Op::GT: return 0x2;
4✔
18
    case Op::GE: return 0x3;
4✔
19
    case Op::SET: return 0x4;
4✔
20
    case Op::NSET: assert(false);
21
    case Op::NE: return 0x5;
4✔
22
    case Op::SGT: return 0x6;
4✔
23
    case Op::SGE: return 0x7;
4✔
24
    case Op::LT: return 0xa;
4✔
25
    case Op::LE: return 0xb;
4✔
26
    case Op::SLT: return 0xc;
4✔
27
    case Op::SLE: return 0xd;
4✔
28
    }
29
    assert(false);
30
    return {};
31
}
32

33
static uint8_t op(const Bin::Op op) {
186✔
34
    using Op = Bin::Op;
93✔
35
    switch (op) {
186✔
36
    case Op::ADD: return 0x0;
7✔
37
    case Op::SUB: return 0x1;
6✔
38
    case Op::MUL: return 0x2;
6✔
39
    case Op::SDIV:
12✔
40
    case Op::UDIV: return 0x3;
12✔
41
    case Op::OR: return 0x4;
6✔
42
    case Op::AND: return 0x5;
6✔
43
    case Op::LSH: return 0x6;
6✔
44
    case Op::RSH: return 0x7;
6✔
45
    case Op::SMOD:
12✔
46
    case Op::UMOD: return 0x9;
12✔
47
    case Op::XOR: return 0xa;
6✔
48
    case Op::MOV:
14✔
49
    case Op::MOVSX8:
50
    case Op::MOVSX16:
51
    case Op::MOVSX32: return 0xb;
14✔
52
    case Op::ARSH: return 0xc;
6✔
53
    }
54
    assert(false);
55
    return {};
56
}
57

58
static int16_t offset(const Bin::Op op) {
186✔
59
    using Op = Bin::Op;
93✔
60
    switch (op) {
186✔
61
    case Op::SDIV:
12✔
62
    case Op::SMOD: return 1;
12✔
63
    case Op::MOVSX8: return 8;
2✔
64
    case Op::MOVSX16: return 16;
2✔
65
    case Op::MOVSX32: return 32;
3✔
66
    default: return 0;
74✔
67
    }
68
}
69

70
static uint8_t imm_endian(const Un::Op op) {
18✔
71
    using Op = Un::Op;
9✔
72
    switch (op) {
18✔
73
    case Op::NEG: assert(false); return 0;
74
    case Op::BE16:
6✔
75
    case Op::LE16:
3✔
76
    case Op::SWAP16: return 16;
6✔
77
    case Op::BE32:
6✔
78
    case Op::LE32:
3✔
79
    case Op::SWAP32: return 32;
6✔
80
    case Op::BE64:
6✔
81
    case Op::LE64:
3✔
82
    case Op::SWAP64: return 64;
6✔
83
    }
84
    assert(false);
85
    return {};
86
}
87

88
struct MarshalVisitor {
214✔
89
  private:
90
    static vector<ebpf_inst> makeLddw(const Reg dst, const uint8_t type, const int32_t imm, const int32_t next_imm) {
10✔
91
        return {ebpf_inst{.opcode = gsl::narrow<uint8_t>(INST_CLS_LD | width_to_opcode(8)),
10✔
92
                          .dst = dst.v,
10✔
93
                          .src = type,
10✔
94
                          .offset = 0,
95
                          .imm = imm},
96
                ebpf_inst{.opcode = 0, .dst = 0, .src = 0, .offset = 0, .imm = next_imm}};
20✔
97
    }
98

99
  public:
100
    std::function<auto(label_t)->int16_t> label_to_offset16;
101
    std::function<auto(label_t)->int32_t> label_to_offset32;
102

103
    vector<ebpf_inst> operator()(Undefined const& a) const {
×
104
        assert(false);
×
105
        return {};
106
    }
107

108
    vector<ebpf_inst> operator()(LoadMapFd const& b) const { return makeLddw(b.dst, INST_LD_MODE_MAP_FD, b.mapfd, 0); }
2✔
109

110
    vector<ebpf_inst> operator()(LoadMapAddress const& b) const {
2✔
111
        return makeLddw(b.dst, INST_LD_MODE_MAP_VALUE, b.mapfd, b.offset);
2✔
112
    }
113

114
    vector<ebpf_inst> operator()(Bin const& b) const {
192✔
115
        if (b.lddw) {
192✔
116
            const auto pimm = std::get_if<Imm>(&b.v);
6✔
117
            assert(pimm != nullptr);
6✔
118
            auto [imm, next_imm] = split(pimm->v);
6✔
119
            return makeLddw(b.dst, INST_LD_MODE_IMM, imm, next_imm);
6✔
120
        }
121

122
        ebpf_inst res{.opcode = gsl::narrow<uint8_t>((b.is64 ? INST_CLS_ALU64 : INST_CLS_ALU) | (op(b.op) << 4)),
325✔
123
                      .dst = b.dst.v,
186✔
124
                      .src = 0,
125
                      .offset = offset(b.op),
279✔
126
                      .imm = 0};
186✔
127
        std::visit(overloaded{[&](const Reg right) {
222✔
128
                                  res.opcode |= INST_SRC_REG;
72✔
129
                                  res.src = right.v;
72✔
130
                              },
36✔
131
                              [&](const Imm right) { res.imm = gsl::narrow<int32_t>(right.v); }},
114✔
132
                   b.v);
186✔
133
        return {res};
372✔
134
    }
135

136
    vector<ebpf_inst> operator()(Un const& b) const {
22✔
137
        switch (b.op) {
22✔
138
        case Un::Op::NEG:
4✔
139
            return {ebpf_inst{
2✔
140
                .opcode = gsl::narrow<uint8_t>((b.is64 ? INST_CLS_ALU64 : INST_CLS_ALU) | INST_ALU_OP_NEG),
5✔
141
                .dst = b.dst.v,
4✔
142
                .src = 0,
143
                .offset = 0,
144
                .imm = 0,
145
            }};
8✔
146
        case Un::Op::LE16:
6✔
147
        case Un::Op::LE32:
3✔
148
        case Un::Op::LE64:
3✔
149
            return {ebpf_inst{
3✔
150
                .opcode = gsl::narrow<uint8_t>(INST_CLS_ALU | INST_END_LE | INST_ALU_OP_END),
151
                .dst = b.dst.v,
6✔
152
                .src = 0,
153
                .offset = 0,
154
                .imm = imm_endian(b.op),
9✔
155
            }};
12✔
156
        case Un::Op::BE16:
6✔
157
        case Un::Op::BE32:
3✔
158
        case Un::Op::BE64:
3✔
159
            return {ebpf_inst{
3✔
160
                .opcode = gsl::narrow<uint8_t>(INST_CLS_ALU | INST_END_BE | INST_ALU_OP_END),
161
                .dst = b.dst.v,
6✔
162
                .src = 0,
163
                .offset = 0,
164
                .imm = imm_endian(b.op),
9✔
165
            }};
12✔
166
        case Un::Op::SWAP16:
6✔
167
        case Un::Op::SWAP32:
3✔
168
        case Un::Op::SWAP64:
3✔
169
            return {ebpf_inst{
3✔
170
                .opcode = gsl::narrow<uint8_t>(INST_CLS_ALU64 | INST_ALU_OP_END),
171
                .dst = b.dst.v,
6✔
172
                .src = 0,
173
                .offset = 0,
174
                .imm = imm_endian(b.op),
9✔
175
            }};
12✔
176
        }
177
        assert(false);
×
178
        return {};
179
    }
180

181
    vector<ebpf_inst> operator()(Call const& b) const {
4✔
182
        return {ebpf_inst{.opcode = gsl::narrow<uint8_t>(INST_OP_CALL),
2✔
183
                          .dst = 0,
184
                          .src = INST_CALL_STATIC_HELPER,
185
                          .offset = 0,
186
                          .imm = b.func}};
8✔
187
    }
188

189
    vector<ebpf_inst> operator()(CallLocal const& b) const {
×
190
        return {ebpf_inst{.opcode = gsl::narrow<uint8_t>(INST_OP_CALL),
191
                          .dst = 0,
192
                          .src = INST_CALL_LOCAL,
193
                          .offset = 0,
194
                          .imm = label_to_offset32(b.target)}};
×
195
    }
196

197
    vector<ebpf_inst> operator()(Callx const& b) const {
6✔
198
        // callx is defined to have the register in 'dst' not in 'src'.
199
        return {ebpf_inst{.opcode = gsl::narrow<uint8_t>(INST_OP_CALLX),
3✔
200
                          .dst = b.func.v,
6✔
201
                          .src = INST_CALL_STATIC_HELPER,
202
                          .offset = 0}};
12✔
203
    }
204

205
    vector<ebpf_inst> operator()(Exit const& b) const {
2✔
206
        return {ebpf_inst{.opcode = INST_OP_EXIT, .dst = 0, .src = 0, .offset = 0, .imm = 0}};
4✔
207
    }
208

209
    vector<ebpf_inst> operator()(Assume const&) const { throw std::invalid_argument("Cannot marshal assumptions"); }
×
210

211
    vector<ebpf_inst> operator()(Jmp const& b) const {
92✔
212
        if (b.cond) {
92✔
213
            ebpf_inst res{
88✔
214
                .opcode = gsl::narrow<uint8_t>(INST_CLS_JMP | (op(b.cond->op) << 4)),
132✔
215
                .dst = b.cond->left.v,
132✔
216
                .src = 0,
217
                .offset = label_to_offset16(b.target),
88✔
218
            };
176✔
219
            visit(overloaded{[&](const Reg right) {
110✔
220
                                 res.opcode |= INST_SRC_REG;
44✔
221
                                 res.src = right.v;
44✔
222
                             },
22✔
223
                             [&](const Imm right) { res.imm = gsl::narrow<int32_t>(right.v); }},
44✔
224
                  b.cond->right);
88✔
225
            return {res};
176✔
226
        } else {
227
            const int32_t imm = label_to_offset32(b.target);
4✔
228
            if (imm != 0) {
4✔
229
                return {ebpf_inst{.opcode = INST_OP_JA32, .imm = imm}};
×
230
            } else {
231
                return {ebpf_inst{.opcode = INST_OP_JA16, .offset = label_to_offset16(b.target)}};
10✔
232
            }
233
        }
234
    }
235

236
    vector<ebpf_inst> operator()(Mem const& b) const {
34✔
237
        const Deref access = b.access;
34✔
238
        ebpf_inst res{
34✔
239
            .opcode = gsl::narrow<uint8_t>(INST_MODE_MEM | width_to_opcode(access.width)),
34✔
240
            .dst = 0,
241
            .src = 0,
242
            .offset = gsl::narrow<int16_t>(access.offset),
51✔
243
        };
34✔
244
        if (b.is_load) {
34✔
245
            if (!std::holds_alternative<Reg>(b.value)) {
14✔
246
                throw std::runtime_error(std::string("LD IMM: ") + to_string(b));
5✔
247
            }
248
            res.opcode |= INST_CLS_LD | 0x1;
12✔
249
            res.dst = gsl::narrow<uint8_t>(std::get<Reg>(b.value).v);
12✔
250
            res.src = access.basereg.v;
12✔
251
        } else {
252
            res.opcode |= INST_CLS_ST;
20✔
253
            res.dst = access.basereg.v;
20✔
254
            if (const auto preg = std::get_if<Reg>(&b.value)) {
20✔
255
                res.opcode |= 0x1;
10✔
256
                res.src = preg->v;
10✔
257
            } else {
258
                res.opcode |= 0x0;
10✔
259
                res.imm = gsl::narrow<int32_t>(std::get<Imm>(b.value).v);
10✔
260
            }
261
        }
262
        return {res};
64✔
263
    }
264

265
    vector<ebpf_inst> operator()(Packet const& b) const {
24✔
266
        ebpf_inst res{
24✔
267
            .opcode = gsl::narrow<uint8_t>(INST_CLS_LD | width_to_opcode(b.width)),
24✔
268
            .dst = 0,
269
            .src = 0,
270
            .offset = 0,
271
            .imm = gsl::narrow<int32_t>(b.offset),
36✔
272
        };
24✔
273
        if (b.regoffset) {
24✔
274
            res.opcode |= INST_MODE_IND;
12✔
275
            res.src = b.regoffset->v;
12✔
276
        } else {
277
            res.opcode |= INST_MODE_ABS;
12✔
278
        }
279
        return {res};
48✔
280
    }
281

282
    vector<ebpf_inst> operator()(Atomic const& b) const {
48✔
283
        auto imm = gsl::narrow<int32_t>(b.op);
48✔
284
        if (b.fetch) {
48✔
285
            imm |= INST_FETCH;
24✔
286
        }
287
        return {
24✔
288
            ebpf_inst{.opcode = gsl::narrow<uint8_t>(INST_CLS_STX | INST_MODE_ATOMIC | width_to_opcode(b.access.width)),
48✔
289
                      .dst = b.access.basereg.v,
48✔
290
                      .src = b.valreg.v,
48✔
291
                      .offset = gsl::narrow<int16_t>(b.access.offset),
72✔
292
                      .imm = imm}};
96✔
293
    }
294

295
    vector<ebpf_inst> operator()(IncrementLoopCounter const&) const { return {}; }
×
296
};
297

298
vector<ebpf_inst> marshal(const Instruction& ins, const pc_t pc) {
428✔
299
    return std::visit(MarshalVisitor{crab::label_to_offset16(pc), crab::label_to_offset32(pc)}, ins);
642✔
300
}
301

302
int asm_syntax::size(const Instruction& inst) {
1,340✔
303
    if (const auto pins = std::get_if<Bin>(&inst)) {
1,340✔
304
        if (pins->lddw) {
556✔
305
            return 2;
2✔
306
        }
307
    }
308
    if (std::holds_alternative<LoadMapFd>(inst)) {
1,336✔
309
        return 2;
24✔
310
    }
311
    if (std::holds_alternative<LoadMapAddress>(inst)) {
1,288✔
NEW
312
        return 2;
×
313
    }
314
    return 1;
644✔
315
}
316

317
static auto get_labels(const InstructionSeq& insts) {
×
318
    pc_t pc = 0;
×
319
    std::map<label_t, pc_t> pc_of_label;
×
320
    for (const auto& [label, inst, _] : insts) {
×
321
        pc_of_label[label] = pc;
×
322
        pc += size(inst);
×
323
    }
324
    return pc_of_label;
×
325
}
×
326

327
vector<ebpf_inst> marshal(const InstructionSeq& insts) {
×
328
    vector<ebpf_inst> res;
×
329
    const auto pc_of_label = get_labels(insts);
×
330
    pc_t pc = 0;
×
331
    for (auto [label, ins, _] : insts) {
×
332
        (void)label; // unused
333
        if (const auto pins = std::get_if<Jmp>(&ins)) {
×
334
            pins->target = label_t{gsl::narrow<int>(pc_of_label.at(pins->target))};
×
335
        }
336
        for (const auto e : marshal(ins, pc)) {
×
337
            pc++;
×
338
            res.push_back(e);
×
339
        }
×
340
    }
×
341
    return res;
×
342
}
×
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