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

qmonnet / rbpf / 14420063260

12 Apr 2025 01:27PM UTC coverage: 95.277% (+0.02%) from 95.255%
14420063260

Pull #122

github

web-flow
Merge 0c892bdeb into 9642917c9
Pull Request #122: feat: Add bpf to bpf call support

192 of 204 new or added lines in 7 files covered. (94.12%)

3 existing lines in 3 files now uncovered.

4317 of 4531 relevant lines covered (95.28%)

256.63 hits per line

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

96.63
/src/verifier.rs
1
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
2
// Derived from uBPF <https://github.com/iovisor/ubpf>
3
// Copyright 2015 Big Switch Networks, Inc
4
//      (uBPF: safety checks, originally in C)
5
// Copyright 2016 6WIND S.A. <quentin.monnet@6wind.com>
6
//      (Translation to Rust)
7

8
// This “verifier” performs simple checks when the eBPF program is loaded into the VM (before it is
9
// interpreted or JIT-compiled). It has nothing to do with the much more elaborated verifier inside
10
// Linux kernel. There is no verification regarding the program flow control (should be a Direct
11
// Acyclic Graph) or the consistency for registers usage (the verifier of the kernel assigns types
12
// to the registers and is much stricter).
13
//
14
// On the other hand, rbpf is not expected to run in kernel space.
15
//
16
// Improving the verifier would be nice, but this is not trivial (and Linux kernel is under GPL
17
// license, so we cannot copy it).
18
//
19
// Contrary to the verifier of the Linux kernel, this one does not modify the bytecode at all.
20

21

22
use crate::ebpf;
23
use crate::lib::*;
24

25
fn reject<S: AsRef<str>>(msg: S) -> Result<(), Error> {
13✔
26
    let full_msg = format!("[Verifier] Error: {}", msg.as_ref());
13✔
27
    Err(Error::new(ErrorKind::Other, full_msg))
13✔
28
}
13✔
29

30
fn check_prog_len(prog: &[u8]) -> Result<(), Error> {
441✔
31
    if prog.len() % ebpf::INSN_SIZE != 0 {
441✔
32
        reject(format!("eBPF program length must be a multiple of {:?} octets",
×
33
                       ebpf::INSN_SIZE))?;
×
34
    }
441✔
35
    if prog.len() > ebpf::PROG_MAX_SIZE {
441✔
36
        reject(format!("eBPF program length limited to {:?}, here {:?}",
1✔
37
                       ebpf::PROG_MAX_INSNS, prog.len() / ebpf::INSN_SIZE))?;
1✔
38
    }
440✔
39

40
    if prog.is_empty() {
440✔
41
        reject("no program set, call set_program() to load one")?;
×
42
    }
440✔
43
    let last_opc = ebpf::get_insn(prog, (prog.len() / ebpf::INSN_SIZE) - 1).opc;
440✔
44
    if last_opc & ebpf::BPF_CLS_MASK != ebpf::BPF_JMP {
440✔
45
        reject("program does not end with “EXIT” instruction")?;
1✔
46
    }
439✔
47

48
    Ok(())
439✔
49
}
441✔
50

51
fn check_imm_endian(insn: &ebpf::Insn, insn_ptr: usize) -> Result<(), Error> {
125✔
52
    match insn.imm {
125✔
53
        16 | 32 | 64 => Ok(()),
124✔
54
        _ => reject(format!("unsupported argument for LE/BE (insn #{insn_ptr:?})"))
1✔
55
    }
56
}
125✔
57

58
fn check_load_dw(prog: &[u8], insn_ptr: usize) -> Result<(), Error> {
30✔
59
    // We know we can reach next insn since we enforce an EXIT insn at the end of program, while
30✔
60
    // this function should be called only for LD_DW insn, that cannot be last in program.
30✔
61
    let next_insn = ebpf::get_insn(prog, insn_ptr + 1);
30✔
62
    if next_insn.opc != 0 {
30✔
63
        reject(format!("incomplete LD_DW instruction (insn #{insn_ptr:?})"))?;
1✔
64
    }
29✔
65

66
    Ok(())
29✔
67
}
30✔
68

69
fn check_jmp_offset(prog: &[u8], insn_ptr: usize) -> Result<(), Error> {
476✔
70
    let insn = ebpf::get_insn(prog, insn_ptr);
476✔
71
    if insn.off == -1 {
476✔
72
        reject(format!("infinite loop (insn #{insn_ptr:?})"))?;
1✔
73
    }
475✔
74

75
    let dst_insn_ptr = insn_ptr as isize + 1 + insn.off as isize;
475✔
76
    if dst_insn_ptr < 0 || dst_insn_ptr as usize >= (prog.len() / ebpf::INSN_SIZE) {
475✔
77
        reject(format!("jump out of code to #{dst_insn_ptr:?} (insn #{insn_ptr:?})"))?;
1✔
78
    }
474✔
79

80
    let dst_insn = ebpf::get_insn(prog, dst_insn_ptr as usize);
474✔
81
    if dst_insn.opc == 0 {
474✔
82
        reject(format!("jump to middle of LD_DW at #{dst_insn_ptr:?} (insn #{insn_ptr:?})"))?;
1✔
83
    }
473✔
84

85
    Ok(())
473✔
86
}
476✔
87

88
fn check_registers(insn: &ebpf::Insn, store: bool, insn_ptr: usize) -> Result<(), Error> {
3,818✔
89
    if insn.src > 10 {
3,818✔
90
        reject(format!("invalid source register (insn #{insn_ptr:?})"))?;
1✔
91
    }
3,817✔
92

93
    match (insn.dst, store) {
3,817✔
94
        (0 ..= 9, _) | (10, true) => Ok(()),
3,817✔
95
        (10, false) => reject(format!("cannot write into register r10 (insn #{insn_ptr:?})")),
1✔
96
        (_, _)      => reject(format!("invalid destination register (insn #{insn_ptr:?})"))
1✔
97
    }
98
}
3,818✔
99

100
pub fn check(prog: &[u8]) -> Result<(), Error> {
441✔
101
    check_prog_len(prog)?;
441✔
102

103
    let mut insn_ptr:usize = 0;
439✔
104
    while insn_ptr * ebpf::INSN_SIZE < prog.len() {
4,254✔
105
        let insn = ebpf::get_insn(prog, insn_ptr);
3,826✔
106
        let mut store = false;
3,826✔
107

3,826✔
108
        match insn.opc {
3,826✔
109

110
            // BPF_LD class
111
            ebpf::LD_ABS_B   => {},
2✔
112
            ebpf::LD_ABS_H   => {},
2✔
113
            ebpf::LD_ABS_W   => {},
2✔
114
            ebpf::LD_ABS_DW  => {},
6✔
115
            ebpf::LD_IND_B   => {},
2✔
116
            ebpf::LD_IND_H   => {},
2✔
117
            ebpf::LD_IND_W   => {},
2✔
118
            ebpf::LD_IND_DW  => {},
2✔
119

120
            ebpf::LD_DW_IMM  => {
121
                store = true;
30✔
122
                check_load_dw(prog, insn_ptr)?;
30✔
123
                insn_ptr += 1;
29✔
124
            },
125

126
            // BPF_LDX class
127
            ebpf::LD_B_REG   => {},
167✔
128
            ebpf::LD_H_REG   => {},
122✔
129
            ebpf::LD_W_REG   => {},
50✔
130
            ebpf::LD_DW_REG  => {},
39✔
131

132
            // BPF_ST class
133
            ebpf::ST_B_IMM   => store = true,
16✔
134
            ebpf::ST_H_IMM   => store = true,
6✔
135
            ebpf::ST_W_IMM   => store = true,
3✔
136
            ebpf::ST_DW_IMM  => store = true,
11✔
137

138
            // BPF_STX class
139
            ebpf::ST_B_REG   => store = true,
64✔
140
            ebpf::ST_H_REG   => store = true,
3✔
141
            ebpf::ST_W_REG   => store = true,
7✔
142
            ebpf::ST_DW_REG  => store = true,
3✔
143
            ebpf::ST_W_XADD  => { unimplemented!(); },
×
144
            ebpf::ST_DW_XADD => { unimplemented!(); },
×
145

146
            // BPF_ALU class
147
            ebpf::ADD32_IMM  => {},
6✔
148
            ebpf::ADD32_REG  => {},
6✔
149
            ebpf::SUB32_IMM  => {},
3✔
150
            ebpf::SUB32_REG  => {},
3✔
151
            ebpf::MUL32_IMM  => {},
6✔
152
            ebpf::MUL32_REG  => {},
9✔
153
            ebpf::DIV32_IMM  => {},
9✔
154
            ebpf::DIV32_REG  => {},
14✔
155
            ebpf::OR32_IMM   => {},
3✔
156
            ebpf::OR32_REG   => {},
3✔
157
            ebpf::AND32_IMM  => {},
3✔
158
            ebpf::AND32_REG  => {},
3✔
159
            ebpf::LSH32_IMM  => {},
13✔
160
            ebpf::LSH32_REG  => {},
5✔
161
            ebpf::RSH32_IMM  => {},
6✔
162
            ebpf::RSH32_REG  => {},
3✔
163
            ebpf::NEG32      => {},
3✔
164
            ebpf::MOD32_IMM  => {},
9✔
165
            ebpf::MOD32_REG  => {},
7✔
166
            ebpf::XOR32_IMM  => {},
3✔
167
            ebpf::XOR32_REG  => {},
3✔
168
            ebpf::MOV32_IMM  => {},
623✔
169
            ebpf::MOV32_REG  => {},
3✔
170
            ebpf::ARSH32_IMM => {},
4✔
171
            ebpf::ARSH32_REG => {},
7✔
172
            ebpf::LE         => { check_imm_endian(&insn, insn_ptr)?; },
9✔
173
            ebpf::BE         => { check_imm_endian(&insn, insn_ptr)?; },
116✔
174

175
            // BPF_ALU64 class
176
            ebpf::ADD64_IMM  => {},
59✔
177
            ebpf::ADD64_REG  => {},
42✔
178
            ebpf::SUB64_IMM  => {},
6✔
179
            ebpf::SUB64_REG  => {},
12✔
180
            ebpf::MUL64_IMM  => {},
9✔
181
            ebpf::MUL64_REG  => {},
9✔
182
            ebpf::DIV64_IMM  => {},
9✔
183
            ebpf::DIV64_REG  => {},
13✔
184
            ebpf::OR64_IMM   => {},
12✔
185
            ebpf::OR64_REG   => {},
201✔
186
            ebpf::AND64_IMM  => {},
38✔
187
            ebpf::AND64_REG  => {},
5✔
188
            ebpf::LSH64_IMM  => {},
212✔
189
            ebpf::LSH64_REG  => {},
7✔
190
            ebpf::RSH64_IMM  => {},
19✔
191
            ebpf::RSH64_REG  => {},
6✔
192
            ebpf::NEG64      => {},
3✔
193
            ebpf::MOD64_IMM  => {},
6✔
194
            ebpf::MOD64_REG  => {},
6✔
195
            ebpf::XOR64_IMM  => {},
6✔
196
            ebpf::XOR64_REG  => {},
6✔
197
            ebpf::MOV64_IMM  => {},
564✔
198
            ebpf::MOV64_REG  => {},
119✔
199
            ebpf::ARSH64_IMM => {},
16✔
200
            ebpf::ARSH64_REG => {},
4✔
201

202
            // BPF_JMP class
203
            ebpf::JA         => { check_jmp_offset(prog, insn_ptr)?; },
23✔
204
            ebpf::JEQ_IMM    => { check_jmp_offset(prog, insn_ptr)?; },
45✔
205
            ebpf::JEQ_REG    => { check_jmp_offset(prog, insn_ptr)?; },
10✔
206
            ebpf::JGT_IMM    => { check_jmp_offset(prog, insn_ptr)?; },
12✔
207
            ebpf::JGT_REG    => { check_jmp_offset(prog, insn_ptr)?; },
17✔
208
            ebpf::JGE_IMM    => { check_jmp_offset(prog, insn_ptr)?; },
6✔
209
            ebpf::JGE_REG    => { check_jmp_offset(prog, insn_ptr)?; },
3✔
210
            ebpf::JLT_IMM    => { check_jmp_offset(prog, insn_ptr)?; },
9✔
211
            ebpf::JLT_REG    => { check_jmp_offset(prog, insn_ptr)?; },
9✔
212
            ebpf::JLE_IMM    => { check_jmp_offset(prog, insn_ptr)?; },
9✔
213
            ebpf::JLE_REG    => { check_jmp_offset(prog, insn_ptr)?; },
9✔
214
            ebpf::JSET_IMM   => { check_jmp_offset(prog, insn_ptr)?; },
6✔
215
            ebpf::JSET_REG   => { check_jmp_offset(prog, insn_ptr)?; },
6✔
216
            ebpf::JNE_IMM    => { check_jmp_offset(prog, insn_ptr)?; },
66✔
217
            ebpf::JNE_REG    => { check_jmp_offset(prog, insn_ptr)?; },
6✔
218
            ebpf::JSGT_IMM   => { check_jmp_offset(prog, insn_ptr)?; },
6✔
219
            ebpf::JSGT_REG   => { check_jmp_offset(prog, insn_ptr)?; },
12✔
220
            ebpf::JSGE_IMM   => { check_jmp_offset(prog, insn_ptr)?; },
9✔
221
            ebpf::JSGE_REG   => { check_jmp_offset(prog, insn_ptr)?; },
9✔
222
            ebpf::JSLT_IMM   => { check_jmp_offset(prog, insn_ptr)?; },
9✔
223
            ebpf::JSLT_REG   => { check_jmp_offset(prog, insn_ptr)?; },
9✔
224
            ebpf::JSLE_IMM   => { check_jmp_offset(prog, insn_ptr)?; },
9✔
225
            ebpf::JSLE_REG   => { check_jmp_offset(prog, insn_ptr)?; },
9✔
226

227
            // BPF_JMP32 class
228
            ebpf::JEQ_IMM32  => { check_jmp_offset(prog, insn_ptr)?; },
6✔
229
            ebpf::JEQ_REG32  => { check_jmp_offset(prog, insn_ptr)?; },
6✔
230
            ebpf::JGT_IMM32  => { check_jmp_offset(prog, insn_ptr)?; },
9✔
231
            ebpf::JGT_REG32  => { check_jmp_offset(prog, insn_ptr)?; },
9✔
232
            ebpf::JGE_IMM32  => { check_jmp_offset(prog, insn_ptr)?; },
6✔
233
            ebpf::JGE_REG32  => { check_jmp_offset(prog, insn_ptr)?; },
6✔
234
            ebpf::JLT_IMM32  => { check_jmp_offset(prog, insn_ptr)?; },
9✔
235
            ebpf::JLT_REG32  => { check_jmp_offset(prog, insn_ptr)?; },
9✔
236
            ebpf::JLE_IMM32  => { check_jmp_offset(prog, insn_ptr)?; },
9✔
237
            ebpf::JLE_REG32  => { check_jmp_offset(prog, insn_ptr)?; },
9✔
238
            ebpf::JSET_IMM32 => { check_jmp_offset(prog, insn_ptr)?; },
6✔
239
            ebpf::JSET_REG32 => { check_jmp_offset(prog, insn_ptr)?; },
6✔
240
            ebpf::JNE_IMM32  => { check_jmp_offset(prog, insn_ptr)?; },
6✔
241
            ebpf::JNE_REG32  => { check_jmp_offset(prog, insn_ptr)?; },
6✔
242
            ebpf::JSGT_IMM32 => { check_jmp_offset(prog, insn_ptr)?; },
6✔
243
            ebpf::JSGT_REG32 => { check_jmp_offset(prog, insn_ptr)?; },
6✔
244
            ebpf::JSGE_IMM32 => { check_jmp_offset(prog, insn_ptr)?; },
9✔
245
            ebpf::JSGE_REG32 => { check_jmp_offset(prog, insn_ptr)?; },
9✔
246
            ebpf::JSLT_IMM32 => { check_jmp_offset(prog, insn_ptr)?; },
9✔
247
            ebpf::JSLT_REG32 => { check_jmp_offset(prog, insn_ptr)?; },
9✔
248
            ebpf::JSLE_IMM32 => { check_jmp_offset(prog, insn_ptr)?; },
9✔
249
            ebpf::JSLE_REG32 => { check_jmp_offset(prog, insn_ptr)?; },
9✔
250

251
            ebpf::CALL       => {
252
                let src = insn.src;
27✔
253
                match src {
27✔
254
                    0 => {
255
                        if insn.imm < 0 { reject(format!("invalid call to function #{:?} (insn #{insn_ptr:?})", insn.imm))?; }
19✔
256
                    }
257
                    1 => {
258
                        let dst_insn_ptr = insn_ptr as isize + 1 + insn.imm as isize;
7✔
259
                        if dst_insn_ptr < 0 || dst_insn_ptr as usize >= (prog.len() / ebpf::INSN_SIZE) {
7✔
260
                            reject(format!("call out of code to #{dst_insn_ptr:?} (insn #{insn_ptr:?})"))?;
1✔
261
                        }
6✔
262
                    }
263
                    _ => { reject(format!("unsupported call type #{:?} (insn #{insn_ptr:?})", src))?; }
1✔
264
                }
265
            },
UNCOV
266
            ebpf::TAIL_CALL  => { unimplemented!() },
×
267
            ebpf::EXIT       => {},
510✔
268

269
            _                => {
270
                reject(format!("unknown eBPF opcode {:#2x} (insn #{insn_ptr:?})", insn.opc))?;
1✔
271
            },
272
        }
273

274
        check_registers(&insn, store, insn_ptr)?;
3,818✔
275

276
        insn_ptr += 1;
3,815✔
277
    }
278

279
    // insn_ptr should now be equal to number of instructions.
280
    if insn_ptr != prog.len() / ebpf::INSN_SIZE {
428✔
281
        reject(format!("jumped out of code to #{insn_ptr:?}"))?;
×
282
    }
428✔
283

284
    Ok(())
428✔
285
}
441✔
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