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> { 26 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. 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 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