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

vbpf / ebpf-verifier / 14231336081

02 Apr 2025 11:08PM UTC coverage: 87.272% (-0.9%) from 88.177%
14231336081

push

github

web-flow
Propogate ebpf_verifier_options_t to thread_local_options (#856)

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

5 of 5 new or added lines in 2 files covered. (100.0%)

58 existing lines in 19 files now uncovered.

8324 of 9538 relevant lines covered (87.27%)

4881701.3 hits per line

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

80.66
/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) {
44✔
14
    using Op = Condition::Op;
44✔
15
    switch (op) {
44✔
16
    case Op::EQ: return 0x1;
17
    case Op::GT: return 0x2;
18
    case Op::GE: return 0x3;
19
    case Op::SET: return 0x4;
20
    case Op::NSET: assert(false);
21
    case Op::NE: return 0x5;
22
    case Op::SGT: return 0x6;
23
    case Op::SGE: return 0x7;
24
    case Op::LT: return 0xa;
25
    case Op::LE: return 0xb;
26
    case Op::SLT: return 0xc;
27
    case Op::SLE: return 0xd;
28
    }
29
    assert(false);
30
    return {};
31
}
32

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

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

70
static uint8_t imm_endian(const Un::Op op) {
9✔
71
    using Op = Un::Op;
9✔
72
    switch (op) {
9✔
73
    case Op::NEG: assert(false); return 0;
74
    case Op::BE16:
3✔
75
    case Op::LE16:
3✔
76
    case Op::SWAP16: return 16;
3✔
77
    case Op::BE32:
3✔
78
    case Op::LE32:
3✔
79
    case Op::SWAP32: return 32;
3✔
80
    case Op::BE64:
3✔
81
    case Op::LE64:
3✔
82
    case Op::SWAP64: return 64;
3✔
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) {
5✔
91
        return {ebpf_inst{.opcode = gsl::narrow<uint8_t>(INST_CLS_LD | width_to_opcode(8)),
5✔
92
                          .dst = dst.v,
5✔
93
                          .src = type,
5✔
94
                          .offset = 0,
95
                          .imm = imm},
96
                ebpf_inst{.opcode = 0, .dst = 0, .src = 0, .offset = 0, .imm = next_imm}};
5✔
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);
×
UNCOV
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); }
1✔
109

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

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

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

136
    vector<ebpf_inst> operator()(Un const& b) const {
11✔
137
        switch (b.op) {
11✔
138
        case Un::Op::NEG:
2✔
139
            return {ebpf_inst{
2✔
140
                .opcode = gsl::narrow<uint8_t>((b.is64 ? INST_CLS_ALU64 : INST_CLS_ALU) | INST_ALU_OP_NEG),
3✔
141
                .dst = b.dst.v,
2✔
142
                .src = 0,
143
                .offset = 0,
144
                .imm = 0,
145
            }};
2✔
146
        case Un::Op::LE16:
3✔
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,
3✔
152
                .src = 0,
153
                .offset = 0,
154
                .imm = imm_endian(b.op),
3✔
155
            }};
3✔
156
        case Un::Op::BE16:
3✔
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,
3✔
162
                .src = 0,
163
                .offset = 0,
164
                .imm = imm_endian(b.op),
3✔
165
            }};
3✔
166
        case Un::Op::SWAP16:
3✔
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,
3✔
172
                .src = 0,
173
                .offset = 0,
174
                .imm = imm_endian(b.op),
3✔
175
            }};
3✔
176
        }
177
        assert(false);
×
UNCOV
178
        return {};
×
179
    }
180

181
    vector<ebpf_inst> operator()(Call const& b) const {
2✔
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}};
2✔
187
    }
188

189
    vector<ebpf_inst> operator()(CallLocal const& b) const {
×
UNCOV
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 {
3✔
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,
3✔
201
                          .src = INST_CALL_STATIC_HELPER,
202
                          .offset = 0}};
3✔
203
    }
204

205
    vector<ebpf_inst> operator()(Exit const& b) const {
1✔
206
        return {ebpf_inst{.opcode = INST_OP_EXIT, .dst = 0, .src = 0, .offset = 0, .imm = 0}};
1✔
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 {
46✔
212
        if (b.cond) {
46✔
213
            ebpf_inst res{
44✔
214
                .opcode = gsl::narrow<uint8_t>(INST_CLS_JMP | (op(b.cond->op) << 4)),
88✔
215
                .dst = b.cond->left.v,
44✔
216
                .src = 0,
217
                .offset = label_to_offset16(b.target),
44✔
218
            };
88✔
219
            visit(overloaded{[&](const Reg right) {
110✔
220
                                 res.opcode |= INST_SRC_REG;
22✔
221
                                 res.src = right.v;
22✔
222
                             },
223
                             [&](const Imm right) { res.imm = gsl::narrow<int32_t>(right.v); }},
22✔
224
                  b.cond->right);
44✔
225
            return {res};
44✔
226
        } else {
227
            const int32_t imm = label_to_offset32(b.target);
2✔
228
            if (imm != 0) {
2✔
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)}};
4✔
232
            }
233
        }
234
    }
235

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

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

282
    vector<ebpf_inst> operator()(Atomic const& b) const {
24✔
283
        auto imm = gsl::narrow<int32_t>(b.op);
24✔
284
        if (b.fetch) {
24✔
285
            imm |= INST_FETCH;
12✔
286
        }
287
        return {
24✔
288
            ebpf_inst{.opcode = gsl::narrow<uint8_t>(INST_CLS_STX | INST_MODE_ATOMIC | width_to_opcode(b.access.width)),
24✔
289
                      .dst = b.access.basereg.v,
24✔
290
                      .src = b.valreg.v,
24✔
291
                      .offset = gsl::narrow<int16_t>(b.access.offset),
24✔
292
                      .imm = imm}};
24✔
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) {
214✔
299
    return std::visit(MarshalVisitor{crab::label_to_offset16(pc), crab::label_to_offset32(pc)}, ins);
427✔
300
}
301

302
int asm_syntax::size(const Instruction& inst) {
670✔
303
    if (const auto pins = std::get_if<Bin>(&inst)) {
670✔
304
        if (pins->lddw) {
278✔
305
            return 2;
306
        }
307
    }
308
    if (std::holds_alternative<LoadMapFd>(inst)) {
668✔
309
        return 2;
310
    }
311
    if (std::holds_alternative<LoadMapAddress>(inst)) {
644✔
312
        return 2;
×
313
    }
314
    return 1;
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) {
×
UNCOV
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

© 2025 Coveralls, Inc