Skip to main content

starnix_core/bpf/
map.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// TODO(https://github.com/rust-lang/rust/issues/39371): remove
6#![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
39/// Counter for map identifiers.
40static 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/// A BPF map and Starnix-specific metadata.
110#[derive(Debug)]
111pub struct BpfMap {
112    id: BpfMapId,
113    map: PinnedMap,
114
115    /// The internal state of the map object.
116    state: OrderedMutex<BpfMapState, EbpfMapStateLevel>,
117
118    /// The security state associated with this bpf Map.
119    pub security_state: security::BpfMapState,
120
121    /// Reference to the `Kernel`. Used to unregister `self` on drop.
122    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            // The memory has been computed, check whether it is still in use.
185            if let Err(memory) = Arc::try_unwrap(memory) {
186                // There is other user of the memory. freeze must fail.
187                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>;