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

Alan-Jowett / ebpf-verifier / 15194704016

22 May 2025 08:53AM UTC coverage: 88.11% (-0.07%) from 88.177%
15194704016

push

github

elazarg
uniform class names and explicit constructors for adapt_sgraph.hpp

Signed-off-by: Elazar Gershuni <elazarg@gmail.com>

27 of 30 new or added lines in 1 file covered. (90.0%)

481 existing lines in 33 files now uncovered.

8552 of 9706 relevant lines covered (88.11%)

9089054.61 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
namespace prevail {
14

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

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

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

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

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

101
  public:
102
    std::function<auto(Label)->int16_t> label_to_offset16;
103
    std::function<auto(Label)->int32_t> label_to_offset32;
104

UNCOV
105
    vector<EbpfInst> operator()(Undefined const& a) const {
×
UNCOV
106
        assert(false);
×
107
        return {};
108
    }
109

110
    vector<EbpfInst> operator()(LoadMapFd const& b) const { return makeLddw(b.dst, INST_LD_MODE_MAP_FD, b.mapfd, 0); }
2✔
111

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

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

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

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

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

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

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

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

UNCOV
211
    vector<EbpfInst> operator()(Assume const&) const { throw std::invalid_argument("Cannot marshal assumptions"); }
×
212

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

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

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

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

UNCOV
297
    vector<EbpfInst> operator()(IncrementLoopCounter const&) const { return {}; }
×
298
};
299

300
vector<EbpfInst> marshal(const Instruction& ins, const Pc pc) {
428✔
301
    return std::visit(MarshalVisitor{label_to_offset16(pc), label_to_offset32(pc)}, ins);
642✔
302
}
303

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

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

329
vector<EbpfInst> marshal(const InstructionSeq& insts) {
×
330
    vector<EbpfInst> res;
×
331
    const auto pc_of_label = get_labels(insts);
×
UNCOV
332
    Pc pc = 0;
×
333
    for (auto [label, ins, _] : insts) {
×
334
        (void)label; // unused
UNCOV
335
        if (const auto pins = std::get_if<Jmp>(&ins)) {
×
336
            pins->target = Label{gsl::narrow<int>(pc_of_label.at(pins->target))};
×
337
        }
338
        for (const auto e : marshal(ins, pc)) {
×
339
            pc++;
×
340
            res.push_back(e);
×
341
        }
×
342
    }
×
UNCOV
343
    return res;
×
UNCOV
344
}
×
345
} // namespace prevail
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc