1use {
3 crate::{
4 ebpf,
5 elf::ElfError,
6 vm::{Config, ContextObject, EbpfVm},
7 },
8 std::collections::{btree_map::Entry, BTreeMap},
9};
10
11#[derive(Debug, PartialEq, Eq, Clone)]
13pub enum SBPFVersion {
14 V1,
16 V2,
18 V3,
20}
21
22impl SBPFVersion {
23 pub fn enable_le(&self) -> bool {
25 self == &SBPFVersion::V1
26 }
27
28 pub fn enable_neg(&self) -> bool {
30 self == &SBPFVersion::V1
31 }
32
33 pub fn swap_sub_reg_imm_operands(&self) -> bool {
35 self != &SBPFVersion::V1
36 }
37
38 pub fn enable_lddw(&self) -> bool {
40 self == &SBPFVersion::V1
41 }
42
43 pub fn enable_pqr(&self) -> bool {
45 self != &SBPFVersion::V1
46 }
47
48 pub fn callx_uses_src_reg(&self) -> bool {
50 self != &SBPFVersion::V1
51 }
52
53 pub fn reject_rodata_stack_overlap(&self) -> bool {
56 self != &SBPFVersion::V1
57 }
58
59 pub fn enable_elf_vaddr(&self) -> bool {
62 self != &SBPFVersion::V1
63 }
64
65 pub fn dynamic_stack_frames(&self) -> bool {
67 self != &SBPFVersion::V1
68 }
69
70 pub fn static_syscalls(&self) -> bool {
72 self != &SBPFVersion::V1
73 }
74}
75
76#[derive(Debug, PartialEq, Eq)]
78pub struct FunctionRegistry<T> {
79 pub(crate) map: BTreeMap<u32, (Vec<u8>, T)>,
80}
81
82impl<T> Default for FunctionRegistry<T> {
83 fn default() -> Self {
84 Self {
85 map: BTreeMap::new(),
86 }
87 }
88}
89
90impl<T: Copy + PartialEq> FunctionRegistry<T> {
91 pub fn register_function(
93 &mut self,
94 key: u32,
95 name: impl Into<Vec<u8>>,
96 value: T,
97 ) -> Result<(), ElfError> {
98 match self.map.entry(key) {
99 Entry::Vacant(entry) => {
100 entry.insert((name.into(), value));
101 }
102 Entry::Occupied(entry) => {
103 if entry.get().1 != value {
104 return Err(ElfError::SymbolHashCollision(key));
105 }
106 }
107 }
108 Ok(())
109 }
110
111 pub fn register_function_hashed(
113 &mut self,
114 name: impl Into<Vec<u8>>,
115 value: T,
116 ) -> Result<u32, ElfError> {
117 let name = name.into();
118 let key = ebpf::hash_symbol_name(name.as_slice());
119 self.register_function(key, name, value)?;
120 Ok(key)
121 }
122
123 pub(crate) fn register_function_hashed_legacy<C: ContextObject>(
125 &mut self,
126 loader: &BuiltinProgram<C>,
127 hash_symbol_name: bool,
128 name: impl Into<Vec<u8>>,
129 value: T,
130 ) -> Result<u32, ElfError>
131 where
132 usize: From<T>,
133 {
134 let name = name.into();
135 let config = loader.get_config();
136 let key = if hash_symbol_name {
137 let hash = if name == b"entrypoint" {
138 ebpf::hash_symbol_name(b"entrypoint")
139 } else {
140 ebpf::hash_symbol_name(&usize::from(value).to_le_bytes())
141 };
142 if config.external_internal_function_hash_collision
143 && loader.get_function_registry().lookup_by_key(hash).is_some()
144 {
145 return Err(ElfError::SymbolHashCollision(hash));
146 }
147 hash
148 } else {
149 usize::from(value) as u32
150 };
151 self.register_function(
152 key,
153 if config.enable_symbol_and_section_labels || name == b"entrypoint" {
154 name
155 } else {
156 Vec::default()
157 },
158 value,
159 )?;
160 Ok(key)
161 }
162
163 pub fn unregister_function(&mut self, key: u32) {
165 self.map.remove(&key);
166 }
167
168 pub fn keys(&self) -> impl Iterator<Item = u32> + '_ {
170 self.map.keys().copied()
171 }
172
173 pub fn iter(&self) -> impl Iterator<Item = (u32, (&[u8], T))> + '_ {
175 self.map
176 .iter()
177 .map(|(key, (name, value))| (*key, (name.as_slice(), *value)))
178 }
179
180 pub fn lookup_by_key(&self, key: u32) -> Option<(&[u8], T)> {
182 self.map
184 .get(&key)
185 .map(|(function_name, value)| (function_name.as_slice(), *value))
186 }
187
188 pub fn lookup_by_name(&self, name: &[u8]) -> Option<(&[u8], T)> {
190 self.map
191 .values()
192 .find(|(function_name, _value)| function_name == name)
193 .map(|(function_name, value)| (function_name.as_slice(), *value))
194 }
195
196 pub fn mem_size(&self) -> usize {
198 std::mem::size_of::<Self>().saturating_add(self.map.iter().fold(
199 0,
200 |state: usize, (_, (name, value))| {
201 state.saturating_add(
202 std::mem::size_of_val(value).saturating_add(
203 std::mem::size_of_val(name).saturating_add(name.capacity()),
204 ),
205 )
206 },
207 ))
208 }
209}
210
211pub type BuiltinFunction<C> = fn(*mut EbpfVm<C>, u64, u64, u64, u64, u64);
213
214#[derive(Eq)]
216pub struct BuiltinProgram<C: ContextObject> {
217 config: Option<Box<Config>>,
219 functions: FunctionRegistry<BuiltinFunction<C>>,
221}
222
223impl<C: ContextObject> PartialEq for BuiltinProgram<C> {
224 fn eq(&self, other: &Self) -> bool {
225 self.config.eq(&other.config) && self.functions.eq(&other.functions)
226 }
227}
228
229impl<C: ContextObject> BuiltinProgram<C> {
230 pub fn new_loader(config: Config, functions: FunctionRegistry<BuiltinFunction<C>>) -> Self {
232 Self {
233 config: Some(Box::new(config)),
234 functions,
235 }
236 }
237
238 pub fn new_builtin(functions: FunctionRegistry<BuiltinFunction<C>>) -> Self {
240 Self {
241 config: None,
242 functions,
243 }
244 }
245
246 pub fn new_mock() -> Self {
248 Self {
249 config: Some(Box::default()),
250 functions: FunctionRegistry::default(),
251 }
252 }
253
254 pub fn get_config(&self) -> &Config {
256 self.config.as_ref().unwrap()
257 }
258
259 pub fn get_function_registry(&self) -> &FunctionRegistry<BuiltinFunction<C>> {
261 &self.functions
262 }
263
264 pub fn mem_size(&self) -> usize {
266 std::mem::size_of::<Self>()
267 .saturating_add(if self.config.is_some() {
268 std::mem::size_of::<Config>()
269 } else {
270 0
271 })
272 .saturating_add(self.functions.mem_size())
273 }
274}
275
276impl<C: ContextObject> std::fmt::Debug for BuiltinProgram<C> {
277 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
278 writeln!(f, "{:?}", unsafe {
279 std::mem::transmute::<&FunctionRegistry<BuiltinFunction<C>>, &FunctionRegistry<usize>>(
281 &self.functions,
282 )
283 })?;
284 Ok(())
285 }
286}
287
288#[macro_export]
290macro_rules! declare_builtin_function {
291 ($(#[$attr:meta])* $name:ident $(<$($generic_ident:tt : $generic_type:tt),+>)?, fn rust(
292 $vm:ident : &mut $ContextObject:ty,
293 $arg_a:ident : u64,
294 $arg_b:ident : u64,
295 $arg_c:ident : u64,
296 $arg_d:ident : u64,
297 $arg_e:ident : u64,
298 $memory_mapping:ident : &mut $MemoryMapping:ty,
299 ) -> $Result:ty { $($rust:tt)* }) => {
300 $(#[$attr])*
301 pub struct $name {}
302 impl $name {
303 pub fn rust $(<$($generic_ident : $generic_type),+>)? (
305 $vm: &mut $ContextObject,
306 $arg_a: u64,
307 $arg_b: u64,
308 $arg_c: u64,
309 $arg_d: u64,
310 $arg_e: u64,
311 $memory_mapping: &mut $MemoryMapping,
312 ) -> $Result {
313 $($rust)*
314 }
315 #[allow(clippy::too_many_arguments)]
317 pub fn vm $(<$($generic_ident : $generic_type),+>)? (
318 $vm: *mut $crate::vm::EbpfVm<$ContextObject>,
319 $arg_a: u64,
320 $arg_b: u64,
321 $arg_c: u64,
322 $arg_d: u64,
323 $arg_e: u64,
324 ) {
325 use $crate::vm::ContextObject;
326 let vm = unsafe {
327 &mut *($vm.cast::<u64>().offset(-($crate::vm::get_runtime_environment_key() as isize)).cast::<$crate::vm::EbpfVm<$ContextObject>>())
328 };
329 let config = vm.loader.get_config();
330 if config.enable_instruction_meter {
331 vm.context_object_pointer.consume(vm.previous_instruction_meter - vm.due_insn_count);
332 }
333 let converted_result: $crate::error::ProgramResult = Self::rust $(::<$($generic_ident),+>)?(
334 vm.context_object_pointer, $arg_a, $arg_b, $arg_c, $arg_d, $arg_e, &mut vm.memory_mapping,
335 ).map_err(|err| $crate::error::EbpfError::SyscallError(err)).into();
336 vm.program_result = converted_result;
337 if config.enable_instruction_meter {
338 vm.previous_instruction_meter = vm.context_object_pointer.get_remaining();
339 }
340 }
341 }
342 };
343}
344
345#[cfg(test)]
346mod tests {
347 use super::*;
348 use crate::{syscalls, vm::TestContextObject};
349
350 #[test]
351 fn test_builtin_program_eq() {
352 let mut function_registry_a =
353 FunctionRegistry::<BuiltinFunction<TestContextObject>>::default();
354 function_registry_a
355 .register_function_hashed(*b"log", syscalls::SyscallString::vm)
356 .unwrap();
357 function_registry_a
358 .register_function_hashed(*b"log_64", syscalls::SyscallU64::vm)
359 .unwrap();
360 let mut function_registry_b =
361 FunctionRegistry::<BuiltinFunction<TestContextObject>>::default();
362 function_registry_b
363 .register_function_hashed(*b"log_64", syscalls::SyscallU64::vm)
364 .unwrap();
365 function_registry_b
366 .register_function_hashed(*b"log", syscalls::SyscallString::vm)
367 .unwrap();
368 let mut function_registry_c =
369 FunctionRegistry::<BuiltinFunction<TestContextObject>>::default();
370 function_registry_c
371 .register_function_hashed(*b"log_64", syscalls::SyscallU64::vm)
372 .unwrap();
373 let builtin_program_a = BuiltinProgram::new_loader(Config::default(), function_registry_a);
374 let builtin_program_b = BuiltinProgram::new_loader(Config::default(), function_registry_b);
375 assert_eq!(builtin_program_a, builtin_program_b);
376 let builtin_program_c = BuiltinProgram::new_loader(Config::default(), function_registry_c);
377 assert_ne!(builtin_program_a, builtin_program_c);
378 }
379}