sandlock_core/netlink/state.rs
1use std::collections::HashSet;
2use std::sync::Mutex;
3
4/// Per-sandbox registry of virtualized netlink cookie fds.
5///
6/// Keyed by `(pid, fd)` — the exact fd number allocated in the child
7/// when our `socket(AF_NETLINK, ..., NETLINK_ROUTE)` handler returned
8/// `InjectFdSendTracked`. Using the fd number directly (instead of
9/// comparing `/proc/<pid>/fd/<fd>` inodes against a set of injected
10/// inodes) avoids TOCTOU: once we record `(pid, fd)`, no other thread
11/// can redirect that fd slot without our `close` handler observing it
12/// and removing the entry first.
13#[derive(Default)]
14pub struct NetlinkState {
15 cookies: Mutex<HashSet<(i32, i32)>>,
16}
17
18impl NetlinkState {
19 pub fn new() -> Self {
20 Self { cookies: Mutex::new(HashSet::new()) }
21 }
22
23 /// Register a new cookie fd injected into the child.
24 pub fn register(&self, pid: i32, fd: i32) {
25 self.cookies.lock().unwrap().insert((pid, fd));
26 }
27
28 /// Remove a cookie entry. Called from the close handler when the
29 /// child closes a tracked fd.
30 pub fn unregister(&self, pid: i32, fd: i32) {
31 self.cookies.lock().unwrap().remove(&(pid, fd));
32 }
33
34 /// Is this (pid, fd) one of our injected netlink cookies?
35 pub fn is_cookie(&self, pid: i32, fd: i32) -> bool {
36 self.cookies.lock().unwrap().contains(&(pid, fd))
37 }
38}