xref: /DragonOS/kernel/src/bpf/prog/verifier.rs (revision fae6e9ade46a52976ad5d099643d51cc20876448)
1 use super::super::Result;
2 use crate::bpf::map::BpfMap;
3 use crate::bpf::prog::util::VerifierLogLevel;
4 use crate::bpf::prog::BpfProg;
5 use crate::filesystem::vfs::file::FileDescriptorVec;
6 use crate::include::bindings::linux_bpf::*;
7 use crate::libs::casting::DowncastArc;
8 use crate::libs::rwlock::RwLock;
9 use alloc::{sync::Arc, vec::Vec};
10 use log::{error, info};
11 use rbpf::ebpf;
12 use rbpf::ebpf::to_insn_vec;
13 use system_error::SystemError;
14 
15 /// The BPF program verifier.
16 ///
17 /// See https://docs.kernel.org/bpf/verifier.html
18 #[derive(Debug)]
19 pub struct BpfProgVerifier<'a> {
20     prog: BpfProg,
21     _log_level: VerifierLogLevel,
22     _log_buf: &'a mut [u8],
23 }
24 
25 impl<'a> BpfProgVerifier<'a> {
new(prog: BpfProg, log_level: VerifierLogLevel, log_buf: &'a mut [u8]) -> Self26     pub fn new(prog: BpfProg, log_level: VerifierLogLevel, log_buf: &'a mut [u8]) -> Self {
27         Self {
28             prog,
29             _log_level: log_level,
30             _log_buf: log_buf,
31         }
32     }
33     /// Relocate the program.
34     ///
35     /// This function will relocate the program, and update the program's instructions.
relocation(&mut self, fd_table: &Arc<RwLock<FileDescriptorVec>>) -> Result<()>36     fn relocation(&mut self, fd_table: &Arc<RwLock<FileDescriptorVec>>) -> Result<()> {
37         let instructions = self.prog.insns_mut();
38         let mut fmt_insn = to_insn_vec(instructions);
39         let mut index = 0;
40         let mut raw_file_ptr = vec![];
41         loop {
42             if index >= fmt_insn.len() {
43                 break;
44             }
45             let mut insn = fmt_insn[index].clone();
46             if insn.opc == ebpf::LD_DW_IMM {
47                 // relocate the instruction
48                 let mut next_insn = fmt_insn[index + 1].clone();
49                 // the imm is the map_fd because user lib has already done the relocation
50                 let map_fd = insn.imm as usize;
51                 let src_reg = insn.src;
52                 // See https://www.kernel.org/doc/html/latest/bpf/standardization/instruction-set.html#id23
53                 let ptr = match src_reg as u32 {
54                     BPF_PSEUDO_MAP_VALUE => {
55                         // dst = map_val(map_by_fd(imm)) + next_imm
56                         // map_val(map) gets the address of the first value in a given map
57                         let file = fd_table
58                             .read()
59                             .get_file_by_fd(map_fd as i32)
60                             .ok_or(SystemError::EBADF)?;
61                         let bpf_map = file
62                             .inode()
63                             .downcast_arc::<BpfMap>()
64                             .ok_or(SystemError::EINVAL)?;
65                         let first_value_ptr =
66                             bpf_map.inner_map().lock().first_value_ptr()? as usize;
67                         let offset = next_insn.imm as usize;
68                         info!(
69                             "Relocate for BPF_PSEUDO_MAP_VALUE, instruction index: {}, map_fd: {}",
70                             index, map_fd
71                         );
72                         Some(first_value_ptr + offset)
73                     }
74                     BPF_PSEUDO_MAP_FD => {
75                         // dst = map_by_fd(imm)
76                         // map_by_fd(imm) means to convert a 32-bit file descriptor into an address of a map
77                         let bpf_map = fd_table
78                             .read()
79                             .get_file_by_fd(map_fd as i32)
80                             .ok_or(SystemError::EBADF)?
81                             .inode()
82                             .downcast_arc::<BpfMap>()
83                             .ok_or(SystemError::EINVAL)?;
84                         // todo!(warning: We need release after prog unload)
85                         let map_ptr = Arc::into_raw(bpf_map) as usize;
86                         info!(
87                             "Relocate for BPF_PSEUDO_MAP_FD, instruction index: {}, map_fd: {}, ptr: {:#x}",
88                             index, map_fd, map_ptr
89                         );
90                         raw_file_ptr.push(map_ptr);
91                         Some(map_ptr)
92                     }
93                     ty => {
94                         error!(
95                             "relocation for ty: {} not implemented, instruction index: {}",
96                             ty, index
97                         );
98                         None
99                     }
100                 };
101                 if let Some(ptr) = ptr {
102                     // The current ins store the map_data_ptr low 32 bits,
103                     // the next ins store the map_data_ptr high 32 bits
104                     insn.imm = ptr as i32;
105                     next_insn.imm = (ptr >> 32) as i32;
106                     fmt_insn[index] = insn;
107                     fmt_insn[index + 1] = next_insn;
108                     index += 2;
109                 } else {
110                     index += 1;
111                 }
112             } else {
113                 index += 1;
114             }
115         }
116         let fmt_insn = fmt_insn
117             .iter()
118             .flat_map(|ins| ins.to_vec())
119             .collect::<Vec<u8>>();
120         instructions.copy_from_slice(&fmt_insn);
121         for ptr in raw_file_ptr {
122             self.prog.insert_map(ptr);
123         }
124         Ok(())
125     }
126 
verify(mut self, fd_table: &Arc<RwLock<FileDescriptorVec>>) -> Result<BpfProg>127     pub fn verify(mut self, fd_table: &Arc<RwLock<FileDescriptorVec>>) -> Result<BpfProg> {
128         self.relocation(fd_table)?;
129         Ok(self.prog)
130     }
131 }
132