1#![allow(non_upper_case_globals)]
7
8use crate::mm::memory::MemoryObject;
9use crate::security;
10use crate::task::{CurrentTask, CurrentTaskAndLocked, Kernel, register_delayed_release};
11use ebpf::MapSchema;
12use ebpf_api::{Map, MapError, PinnedMap};
13use starnix_lifecycle::{ObjectReleaser, ReleaserAction};
14use starnix_sync::{
15 EbpfMapStateLevel, EbpfStateLock, LockBefore, Locked, MutexGuard, OrderedMutex,
16};
17use starnix_types::ownership::{Releasable, ReleaseGuard};
18use starnix_uapi::auth::{CAP_BPF, CAP_NET_ADMIN, CAP_PERFMON, CAP_SYS_ADMIN};
19use starnix_uapi::errors::Errno;
20use starnix_uapi::{
21 bpf_map_type_BPF_MAP_TYPE_ARRAY_OF_MAPS, bpf_map_type_BPF_MAP_TYPE_BLOOM_FILTER,
22 bpf_map_type_BPF_MAP_TYPE_CGROUP_STORAGE, bpf_map_type_BPF_MAP_TYPE_CGRP_STORAGE,
23 bpf_map_type_BPF_MAP_TYPE_CPUMAP, bpf_map_type_BPF_MAP_TYPE_DEVMAP,
24 bpf_map_type_BPF_MAP_TYPE_DEVMAP_HASH, bpf_map_type_BPF_MAP_TYPE_HASH_OF_MAPS,
25 bpf_map_type_BPF_MAP_TYPE_INODE_STORAGE, bpf_map_type_BPF_MAP_TYPE_LPM_TRIE,
26 bpf_map_type_BPF_MAP_TYPE_LRU_HASH, bpf_map_type_BPF_MAP_TYPE_LRU_PERCPU_HASH,
27 bpf_map_type_BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE, bpf_map_type_BPF_MAP_TYPE_QUEUE,
28 bpf_map_type_BPF_MAP_TYPE_SK_STORAGE, bpf_map_type_BPF_MAP_TYPE_SOCKHASH,
29 bpf_map_type_BPF_MAP_TYPE_SOCKMAP, bpf_map_type_BPF_MAP_TYPE_STACK,
30 bpf_map_type_BPF_MAP_TYPE_STACK_TRACE, bpf_map_type_BPF_MAP_TYPE_STRUCT_OPS,
31 bpf_map_type_BPF_MAP_TYPE_TASK_STORAGE, bpf_map_type_BPF_MAP_TYPE_XSKMAP, errno, error,
32};
33use std::ops::Deref;
34use std::sync::atomic::{AtomicU32, Ordering};
35use std::sync::{Arc, Weak};
36
37pub type BpfMapId = u32;
38
39static MAP_IDS: AtomicU32 = AtomicU32::new(1);
41fn new_map_id() -> BpfMapId {
42 MAP_IDS.fetch_add(1, Ordering::Relaxed)
43}
44
45pub(crate) fn map_error_to_errno(e: MapError) -> Errno {
46 match e {
47 MapError::InvalidParam => errno!(EINVAL),
48 MapError::InvalidKey => errno!(ENOENT),
49 MapError::EntryExists => errno!(EEXIST),
50 MapError::NoMemory => errno!(ENOMEM),
51 MapError::SizeLimit => errno!(E2BIG),
52 MapError::MapTypeNotSupported | MapError::NotSupported => errno!(ENOSYS),
53 MapError::InvalidVmo | MapError::Internal => errno!(EIO),
54 }
55}
56
57fn check_map_create_access(current_task: &CurrentTask, schema: &MapSchema) -> Result<(), Errno> {
58 if security::is_task_capable_noaudit(current_task, CAP_SYS_ADMIN) {
59 return Ok(());
60 }
61 let cap_bpf_always_required = matches!(
62 schema.map_type,
63 bpf_map_type_BPF_MAP_TYPE_LPM_TRIE
64 | bpf_map_type_BPF_MAP_TYPE_LRU_HASH
65 | bpf_map_type_BPF_MAP_TYPE_LRU_PERCPU_HASH
66 | bpf_map_type_BPF_MAP_TYPE_QUEUE
67 | bpf_map_type_BPF_MAP_TYPE_STACK
68 | bpf_map_type_BPF_MAP_TYPE_ARRAY_OF_MAPS
69 | bpf_map_type_BPF_MAP_TYPE_HASH_OF_MAPS
70 | bpf_map_type_BPF_MAP_TYPE_BLOOM_FILTER
71 | bpf_map_type_BPF_MAP_TYPE_SK_STORAGE
72 | bpf_map_type_BPF_MAP_TYPE_INODE_STORAGE
73 | bpf_map_type_BPF_MAP_TYPE_TASK_STORAGE
74 | bpf_map_type_BPF_MAP_TYPE_CGROUP_STORAGE
75 | bpf_map_type_BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE
76 | bpf_map_type_BPF_MAP_TYPE_CGRP_STORAGE
77 );
78
79 if cap_bpf_always_required || !current_task.kernel().allow_unprivileged_bpf() {
80 security::check_task_capable(current_task, CAP_BPF)?;
81 }
82
83 match schema.map_type {
84 bpf_map_type_BPF_MAP_TYPE_DEVMAP
85 | bpf_map_type_BPF_MAP_TYPE_DEVMAP_HASH
86 | bpf_map_type_BPF_MAP_TYPE_CPUMAP
87 | bpf_map_type_BPF_MAP_TYPE_SOCKMAP
88 | bpf_map_type_BPF_MAP_TYPE_SOCKHASH
89 | bpf_map_type_BPF_MAP_TYPE_XSKMAP => {
90 security::check_task_capable(current_task, CAP_NET_ADMIN)?;
91 }
92 bpf_map_type_BPF_MAP_TYPE_STACK_TRACE => {
93 security::check_task_capable(current_task, CAP_PERFMON)?;
94 }
95 bpf_map_type_BPF_MAP_TYPE_STRUCT_OPS => {
96 return error!(EPERM);
97 }
98 _ => {}
99 }
100 Ok(())
101}
102
103#[derive(Debug, Default)]
104struct BpfMapState {
105 memory_object: Option<Arc<MemoryObject>>,
106 is_frozen: bool,
107}
108
109#[derive(Debug)]
111pub struct BpfMap {
112 id: BpfMapId,
113 map: PinnedMap,
114
115 state: OrderedMutex<BpfMapState, EbpfMapStateLevel>,
117
118 pub security_state: security::BpfMapState,
120
121 kernel: Weak<Kernel>,
123}
124
125impl Deref for BpfMap {
126 type Target = PinnedMap;
127 fn deref(&self) -> &PinnedMap {
128 &self.map
129 }
130}
131
132impl BpfMap {
133 pub fn new<L>(
134 locked: &mut Locked<L>,
135 current_task: &CurrentTask,
136 schema: MapSchema,
137 name: &str,
138 security_state: security::BpfMapState,
139 ) -> Result<BpfMapHandle, Errno>
140 where
141 L: LockBefore<EbpfStateLock>,
142 {
143 check_map_create_access(current_task, &schema)?;
144
145 let map = Map::new(schema, name).map_err(map_error_to_errno)?;
146 let map = BpfMapHandle::new(
147 Self {
148 id: new_map_id(),
149 map,
150 state: Default::default(),
151 security_state,
152 kernel: Arc::downgrade(current_task.kernel()),
153 }
154 .into(),
155 );
156 current_task.kernel().ebpf_state.register_map(locked, &map);
157 Ok(map)
158 }
159
160 pub fn id(&self) -> BpfMapId {
161 self.id
162 }
163
164 pub(crate) fn frozen<'a, L>(
165 &'a self,
166 locked: &'a mut Locked<L>,
167 ) -> (impl Deref<Target = bool> + 'a, &'a mut Locked<EbpfMapStateLevel>)
168 where
169 L: LockBefore<EbpfMapStateLevel>,
170 {
171 let (guard, locked) = self.state.lock_and(locked);
172 (MutexGuard::map(guard, |s| &mut s.is_frozen), locked)
173 }
174
175 pub(crate) fn freeze<L>(&self, locked: &mut Locked<L>) -> Result<(), Errno>
176 where
177 L: LockBefore<EbpfMapStateLevel>,
178 {
179 let mut state = self.state.lock(locked);
180 if state.is_frozen {
181 return Ok(());
182 }
183 if let Some(memory) = state.memory_object.take() {
184 if let Err(memory) = Arc::try_unwrap(memory) {
186 state.memory_object = Some(memory);
188 return error!(EBUSY);
189 }
190 }
191 state.is_frozen = true;
192 return Ok(());
193 }
194
195 pub(crate) fn get_inner(&self) -> PinnedMap {
196 self.map.clone()
197 }
198
199 pub(crate) fn get_memory<L, F>(
200 &self,
201 locked: &mut Locked<L>,
202 factory: F,
203 ) -> Result<Arc<MemoryObject>, Errno>
204 where
205 L: LockBefore<EbpfMapStateLevel>,
206 F: FnOnce() -> Result<Arc<MemoryObject>, Errno>,
207 {
208 let mut state = self.state.lock(locked);
209 if state.is_frozen {
210 return error!(EPERM);
211 }
212 if let Some(memory) = state.memory_object.as_ref() {
213 return Ok(memory.clone());
214 }
215 let memory = factory()?;
216 state.memory_object = Some(memory.clone());
217 Ok(memory)
218 }
219}
220
221impl Releasable for BpfMap {
222 type Context<'a> = CurrentTaskAndLocked<'a>;
223
224 fn release<'a>(self, (locked, _current_task): CurrentTaskAndLocked<'a>) {
225 if let Some(kernel) = self.kernel.upgrade() {
226 kernel.ebpf_state.unregister_map(locked, self.id);
227 }
228 }
229}
230
231pub enum BpfMapReleaserAction {}
232impl ReleaserAction<BpfMap> for BpfMapReleaserAction {
233 fn release(map: ReleaseGuard<BpfMap>) {
234 register_delayed_release(map);
235 }
236}
237pub type BpfMapReleaser = ObjectReleaser<BpfMap, BpfMapReleaserAction>;
238pub type BpfMapHandle = Arc<BpfMapReleaser>;
239pub type WeakBpfMapHandle = Weak<BpfMapReleaser>;