1#![allow(non_upper_case_globals)]
7
8use crate::bpf::check_cap_or_sys_admin;
9use crate::bpf::context::EbpfRunContextImpl;
10use crate::bpf::fs::{BpfHandle, get_bpf_object};
11use crate::bpf::program::ProgramHandle;
12use crate::mm::PAGE_SIZE;
13use crate::task::CurrentTask;
14use crate::vfs::FdNumber;
15use crate::vfs::socket::{
16 SockOptValue, Socket, SocketBpfState, SocketDomain, SocketProtocol, SocketType,
17 ZxioBackedSocket,
18};
19use ebpf::{BpfValue, EbpfProgram, EbpfProgramContext, EbpfPtr, ProgramArgument, Type};
20use ebpf_api::{
21 AttachType, BPF_SOCK_ADDR_TYPE, BPF_SOCK_TYPE, BpfSockContext, CgroupSockAddrProgramContext,
22 CgroupSockOptProgramContext, CgroupSockProgramContext, CurrentTaskContext, Map, MapValueRef,
23 MapsContext, PinnedMap, ProgramType, ReturnValueContext, SocketRef,
24};
25use fidl_fuchsia_net_filter as fnet_filter;
26use fuchsia_component::client::connect_to_protocol_sync;
27use fuchsia_rcu::RcuArc;
28use linux_uapi::{bpf_sockopt, uaddr};
29use starnix_logging::{log_error, log_warn, track_stub};
30use starnix_sync::{EbpfStateLock, RcuLock};
31use starnix_syscalls::{SUCCESS, SyscallResult};
32use starnix_uapi::auth::{CAP_NET_ADMIN, Capabilities};
33use starnix_uapi::errors::{Errno, ErrnoCode, is_error_return_value};
34use starnix_uapi::{
35 CGROUP2_SUPER_MAGIC, bpf_attr__bindgen_ty_6, bpf_sock, bpf_sock_addr, errno, error, gid_t,
36 pid_t, uid_t,
37};
38use std::ops::{Deref, DerefMut};
39use std::sync::{Arc, OnceLock};
40use zerocopy::FromBytes;
41
42pub type BpfAttachAttr = bpf_attr__bindgen_ty_6;
43
44fn check_root_cgroup_fd(current_task: &CurrentTask, cgroup_fd: FdNumber) -> Result<(), Errno> {
45 let file = current_task.files().get(cgroup_fd)?;
46
47 let is_cgroup = file.node().fs().statfs(current_task)?.f_type == CGROUP2_SUPER_MAGIC as i64;
49 if !is_cgroup {
50 log_warn!("bpf_prog_attach(BPF_PROG_ATTACH) is called with an invalid cgroup2 FD.");
51 return error!(EINVAL);
52 }
53
54 let is_root = file
58 .node()
59 .fs()
60 .maybe_root()
61 .map(|root| Arc::ptr_eq(&root.node, file.node()))
62 .unwrap_or(false);
63 if !is_root {
64 log_warn!("bpf_prog_attach(BPF_PROG_ATTACH) is supported only for root cgroup.");
65 return error!(EINVAL);
66 }
67
68 Ok(())
69}
70
71pub fn bpf_prog_attach(
72 current_task: &CurrentTask,
73 attr: BpfAttachAttr,
74) -> Result<SyscallResult, Errno> {
75 let bpf_fd = FdNumber::from_raw(attr.attach_bpf_fd as i32);
77 let object = get_bpf_object(current_task, bpf_fd)?;
78 if matches!(object, BpfHandle::ProgramStub(_)) {
79 log_warn!("Stub program. Faking successful attach");
80 return Ok(SUCCESS);
81 }
82 let program = object.as_program()?.clone();
83
84 let required_caps = get_capability_for_program(program.info.program_type)?;
85 check_cap_or_sys_admin(current_task, required_caps)?;
86
87 let attach_type = AttachType::from(attr.attach_type);
88 let program_type = program.info.program_type;
89 if attach_type.get_program_type() != program_type {
90 log_warn!(
91 "bpf_prog_attach(BPF_PROG_ATTACH): program not compatible with attach_type \
92 attach_type: {attach_type:?}, program_type: {program_type:?}"
93 );
94 return error!(EINVAL);
95 }
96
97 if !attach_type.is_compatible_with_expected_attach_type(program.info.expected_attach_type) {
98 log_warn!(
99 "bpf_prog_attach(BPF_PROG_ATTACH): expected_attach_type didn't match attach_type \
100 expected_attach_type: {:?}, attach_type: {:?}",
101 program.info.expected_attach_type,
102 attach_type
103 );
104 return error!(EINVAL);
105 }
106
107 let target_fd = unsafe { attr.__bindgen_anon_1.target_fd };
109 let target_fd = FdNumber::from_raw(target_fd as i32);
110
111 current_task.kernel().ebpf_state.attachments.attach_prog(
112 current_task,
113 attach_type,
114 target_fd,
115 program,
116 )
117}
118
119pub fn bpf_prog_detach(
120 current_task: &CurrentTask,
121 attr: BpfAttachAttr,
122) -> Result<SyscallResult, Errno> {
123 let attach_type = AttachType::from(attr.attach_type);
124
125 let target_fd = unsafe { attr.__bindgen_anon_1.target_fd };
127 let target_fd = FdNumber::from_raw(target_fd as i32);
128
129 current_task.kernel().ebpf_state.attachments.detach_prog(current_task, attach_type, target_fd)
130}
131
132#[repr(C)]
134pub struct BpfSockAddr<'a> {
135 sock_addr: bpf_sock_addr,
136
137 bpf_sock: &'a BpfSock<'a>,
138}
139
140impl<'a> Deref for BpfSockAddr<'a> {
141 type Target = bpf_sock_addr;
142 fn deref(&self) -> &Self::Target {
143 &self.sock_addr
144 }
145}
146
147impl<'a> DerefMut for BpfSockAddr<'a> {
148 fn deref_mut(&mut self) -> &mut Self::Target {
149 &mut self.sock_addr
150 }
151}
152
153impl<'a> ProgramArgument for &'_ mut BpfSockAddr<'a> {
154 fn get_type() -> &'static Type {
155 &*BPF_SOCK_ADDR_TYPE
156 }
157}
158
159impl<'a, 'b> SocketRef for &'a mut BpfSockAddr<'a> {
160 fn get_socket_cookie(&self) -> Option<u64> {
161 self.bpf_sock.get_socket_cookie()
162 }
163
164 fn get_socket_uid(&self) -> Option<uid_t> {
165 self.bpf_sock.get_socket_uid()
166 }
167}
168
169struct SockAddrProgram(EbpfProgram<SockAddrProgram>);
171
172impl EbpfProgramContext for SockAddrProgram {
173 type RunContext<'a> = EbpfRunContextImpl<'a>;
174 type Packet<'a> = ();
175 type Arg1<'a> = &'a mut BpfSockAddr<'a>;
176 type Arg2<'a> = ();
177 type Arg3<'a> = ();
178 type Arg4<'a> = ();
179 type Arg5<'a> = ();
180
181 type Map = PinnedMap;
182}
183
184ebpf_api::ebpf_program_context_type!(SockAddrProgram, CgroupSockAddrProgramContext);
185
186#[derive(Debug, PartialEq, Eq)]
187pub enum SockAddrProgramResult {
188 Allow,
189 Block,
190}
191
192impl SockAddrProgram {
193 fn run<'a>(
194 &self,
195 current_task: &'a CurrentTask,
196 addr: &'a mut BpfSockAddr<'a>,
197 can_block: bool,
198 ) -> SockAddrProgramResult {
199 let mut run_context = EbpfRunContextImpl::new(current_task);
200 match self.0.run_with_1_argument(&mut run_context, addr) {
201 0 if can_block => SockAddrProgramResult::Block,
203 1 => SockAddrProgramResult::Allow,
204 result => {
205 log_error!("eBPF program returned invalid result: {}", result);
208 SockAddrProgramResult::Allow
209 }
210 }
211 }
212}
213
214type AttachedSockAddrProgramCell = RcuLock<RcuArc<SockAddrProgram>, EbpfStateLock>;
215
216#[repr(C)]
218pub struct BpfSock<'a> {
219 value: bpf_sock,
221
222 socket: Option<&'a ZxioBackedSocket>,
223}
224
225impl<'a> BpfSock<'a> {
226 fn from_socket(socket: &'a Socket) -> Self {
227 Self {
228 value: bpf_sock {
229 family: socket.domain.as_raw().into(),
230 type_: socket.socket_type.as_raw(),
231 protocol: socket.protocol.as_raw(),
232 state: socket.bpf_state().into(),
233 ..Default::default()
234 },
235 socket: socket.downcast_socket(),
236 }
237 }
238}
239
240impl<'a> Deref for BpfSock<'a> {
241 type Target = bpf_sock;
242 fn deref(&self) -> &Self::Target {
243 &self.value
244 }
245}
246
247impl<'a> DerefMut for BpfSock<'a> {
248 fn deref_mut(&mut self) -> &mut Self::Target {
249 &mut self.value
250 }
251}
252
253impl<'a> ProgramArgument for &'_ BpfSock<'a> {
254 fn get_type() -> &'static Type {
255 &*BPF_SOCK_TYPE
256 }
257}
258
259impl<'a> SocketRef for &'_ BpfSock<'a> {
260 fn get_socket_cookie(&self) -> Option<u64> {
261 self.socket.and_then(|socket| {
262 socket
263 .get_socket_cookie()
264 .inspect_err(|errno| log_error!("Failed to get socket cookie: {:?}", errno))
265 .ok()
266 })
267 }
268
269 fn get_socket_uid(&self) -> Option<uid_t> {
270 self.socket.map(|socket| socket.uid())
271 }
272}
273
274struct SockProgram(EbpfProgram<SockProgram>);
276
277impl EbpfProgramContext for SockProgram {
278 type RunContext<'a> = EbpfRunContextImpl<'a>;
279 type Packet<'a> = ();
280 type Arg1<'a> = &'a BpfSock<'a>;
281 type Arg2<'a> = ();
282 type Arg3<'a> = ();
283 type Arg4<'a> = ();
284 type Arg5<'a> = ();
285
286 type Map = PinnedMap;
287}
288
289ebpf_api::ebpf_program_context_type!(SockProgram, CgroupSockProgramContext);
290
291#[derive(Debug, PartialEq, Eq)]
292pub enum SockProgramResult {
293 Allow,
294 Block,
295}
296
297impl SockProgram {
298 fn run<'a>(&self, current_task: &'a CurrentTask, sock: &'a BpfSock<'a>) -> SockProgramResult {
299 let mut run_context = EbpfRunContextImpl::new(current_task);
300 if self.0.run_with_1_argument(&mut run_context, sock) == 0 {
301 SockProgramResult::Block
302 } else {
303 SockProgramResult::Allow
304 }
305 }
306}
307
308type AttachedSockProgramCell = RcuLock<RcuArc<SockProgram>, EbpfStateLock>;
309
310mod internal {
311 use super::BpfSock;
312 use ebpf::{BpfValue, EbpfPtr, ProgramArgument, Type};
313 use ebpf_api::BPF_SOCKOPT_TYPE;
314 use starnix_uapi::{bpf_sockopt, uaddr};
315 use std::ops::Deref;
316 use zerocopy::{FromBytes, IntoBytes};
317
318 #[repr(C)]
320 #[derive(IntoBytes, FromBytes)]
321 pub struct BpfSockOpt(bpf_sockopt);
322
323 impl ProgramArgument for &'_ mut BpfSockOpt {
324 fn get_type() -> &'static Type {
325 &*BPF_SOCKOPT_TYPE
326 }
327 }
328
329 pub struct BpfSockOptWithValue {
331 sockopt: BpfSockOpt,
332
333 value_buf: Vec<u8>,
339 }
340
341 impl BpfSockOptWithValue {
342 pub fn new(
343 level: u32,
344 optname: u32,
345 value_buf: Vec<u8>,
346 optlen: u32,
347 retval: i32,
348 sock: *const BpfSock<'_>,
349 ) -> Self {
350 let mut sockopt = Self {
351 sockopt: BpfSockOpt(bpf_sockopt {
352 level: level as i32,
353 optname: optname as i32,
354 optlen: optlen as i32,
355 retval: retval as i32,
356 ..Default::default()
357 }),
358 value_buf,
359 };
360
361 unsafe {
363 sockopt.sockopt.0.__bindgen_anon_2.optval =
364 uaddr { addr: sockopt.value_buf.as_mut_ptr() as u64 };
365 sockopt.sockopt.0.__bindgen_anon_3.optval_end = uaddr {
366 addr: sockopt.value_buf.as_mut_ptr().add(sockopt.value_buf.len()) as u64,
367 };
368 }
369
370 sockopt.sockopt.0.__bindgen_anon_1.sk =
371 (uaddr { addr: BpfValue::from(sock).into() }).into();
372
373 sockopt
374 }
375
376 pub fn as_ptr<'a>(&'a mut self) -> EbpfPtr<'a, BpfSockOpt> {
377 EbpfPtr::from(&mut self.sockopt)
378 }
379
380 pub fn take_value(self) -> Vec<u8> {
383 self.value_buf
384 }
385 }
386
387 impl Deref for BpfSockOptWithValue {
388 type Target = bpf_sockopt;
389 fn deref(&self) -> &Self::Target {
390 &self.sockopt.0
391 }
392 }
393}
394
395use internal::{BpfSockOpt, BpfSockOptWithValue};
396
397struct SockOptProgram(EbpfProgram<SockOptProgram>);
399
400pub struct SockOptEbpfRunContextImpl<'a> {
402 ebpf_run_context: EbpfRunContextImpl<'a>,
403
404 sockopt: EbpfPtr<'a, BpfSockOpt>,
407}
408
409const BPF_SOCKOPT_RETVAL_OFFSET: usize = std::mem::offset_of!(bpf_sockopt, retval);
410
411impl<'a> SockOptEbpfRunContextImpl<'a> {
412 pub fn new(current_task: &'a CurrentTask, sockopt: EbpfPtr<'a, BpfSockOpt>) -> Self {
413 Self { ebpf_run_context: EbpfRunContextImpl::new(current_task), sockopt }
414 }
415}
416
417impl<'a> MapsContext<'a> for SockOptEbpfRunContextImpl<'a> {
418 fn on_map_access(&mut self, map: &Map) {
419 self.ebpf_run_context.on_map_access(map);
420 }
421 fn add_value_ref(&mut self, map_ref: MapValueRef<'a>) {
422 self.ebpf_run_context.add_value_ref(map_ref);
423 }
424}
425
426impl<'a> CurrentTaskContext for SockOptEbpfRunContextImpl<'a> {
427 fn get_uid_gid(&self) -> (uid_t, gid_t) {
428 self.ebpf_run_context.get_uid_gid()
429 }
430 fn get_tid_tgid(&self) -> (pid_t, pid_t) {
431 self.ebpf_run_context.get_tid_tgid()
432 }
433}
434
435impl<'a> ReturnValueContext for SockOptEbpfRunContextImpl<'a> {
436 fn set_retval(&mut self, value: i32) -> i32 {
437 let sockopt = self.sockopt.get_field::<i32, BPF_SOCKOPT_RETVAL_OFFSET>();
438 sockopt.store_relaxed(value);
439 0
440 }
441 fn get_retval(&self) -> i32 {
442 let sockopt = self.sockopt.get_field::<i32, BPF_SOCKOPT_RETVAL_OFFSET>();
443 sockopt.load_relaxed()
444 }
445}
446
447impl<'a> BpfSockContext for SockOptEbpfRunContextImpl<'a> {
448 type BpfSockRef = &'a BpfSock<'a>;
449}
450
451impl EbpfProgramContext for SockOptProgram {
452 type RunContext<'a> = SockOptEbpfRunContextImpl<'a>;
453 type Packet<'a> = ();
454 type Arg1<'a> = EbpfPtr<'a, BpfSockOpt>;
455 type Arg2<'a> = ();
456 type Arg3<'a> = ();
457 type Arg4<'a> = ();
458 type Arg5<'a> = ();
459
460 type Map = PinnedMap;
461}
462
463ebpf_api::ebpf_program_context_type!(SockOptProgram, CgroupSockOptProgramContext);
464
465#[derive(Debug)]
466pub enum SetSockOptProgramResult {
467 Fail(Errno),
469
470 Allow(SockOptValue),
472
473 Bypass,
476}
477
478impl SockOptProgram {
479 fn run<'a>(&self, current_task: &'a CurrentTask, sockopt: &'a mut BpfSockOptWithValue) -> u64 {
480 let sockopt_ptr = sockopt.as_ptr();
481 let mut run_context = SockOptEbpfRunContextImpl::new(current_task, sockopt_ptr);
482 self.0.run_with_1_argument(&mut run_context, sockopt_ptr)
483 }
484}
485
486type AttachedSockOptProgramCell = RcuLock<RcuArc<SockOptProgram>, EbpfStateLock>;
487
488#[derive(Default)]
489pub struct CgroupEbpfProgramSet {
490 inet4_bind: AttachedSockAddrProgramCell,
491 inet6_bind: AttachedSockAddrProgramCell,
492 inet4_connect: AttachedSockAddrProgramCell,
493 inet6_connect: AttachedSockAddrProgramCell,
494 udp4_sendmsg: AttachedSockAddrProgramCell,
495 udp6_sendmsg: AttachedSockAddrProgramCell,
496 udp4_recvmsg: AttachedSockAddrProgramCell,
497 udp6_recvmsg: AttachedSockAddrProgramCell,
498 sock_create: AttachedSockProgramCell,
499 sock_release: AttachedSockProgramCell,
500 set_sockopt: AttachedSockOptProgramCell,
501 get_sockopt: AttachedSockOptProgramCell,
502}
503
504#[derive(Eq, PartialEq, Debug, Copy, Clone)]
505pub enum SockAddrOp {
506 Bind,
507 Connect,
508 UdpSendMsg,
509 UdpRecvMsg,
510}
511
512#[derive(Eq, PartialEq, Debug, Copy, Clone)]
513pub enum SockOp {
514 Create,
515 Release,
516}
517
518impl CgroupEbpfProgramSet {
519 fn get_sock_addr_program(
520 &self,
521 attach_type: AttachType,
522 ) -> Result<&AttachedSockAddrProgramCell, Errno> {
523 assert!(attach_type.is_cgroup());
524
525 match attach_type {
526 AttachType::CgroupInet4Bind => Ok(&self.inet4_bind),
527 AttachType::CgroupInet6Bind => Ok(&self.inet6_bind),
528 AttachType::CgroupInet4Connect => Ok(&self.inet4_connect),
529 AttachType::CgroupInet6Connect => Ok(&self.inet6_connect),
530 AttachType::CgroupUdp4Sendmsg => Ok(&self.udp4_sendmsg),
531 AttachType::CgroupUdp6Sendmsg => Ok(&self.udp6_sendmsg),
532 AttachType::CgroupUdp4Recvmsg => Ok(&self.udp4_recvmsg),
533 AttachType::CgroupUdp6Recvmsg => Ok(&self.udp6_recvmsg),
534 _ => error!(ENOTSUP),
535 }
536 }
537
538 fn get_sock_program(&self, attach_type: AttachType) -> Result<&AttachedSockProgramCell, Errno> {
539 assert!(attach_type.is_cgroup());
540
541 match attach_type {
542 AttachType::CgroupInetSockCreate => Ok(&self.sock_create),
543 AttachType::CgroupInetSockRelease => Ok(&self.sock_release),
544 _ => error!(ENOTSUP),
545 }
546 }
547
548 fn get_sock_opt_program(
549 &self,
550 attach_type: AttachType,
551 ) -> Result<&AttachedSockOptProgramCell, Errno> {
552 assert!(attach_type.is_cgroup());
553
554 match attach_type {
555 AttachType::CgroupSetsockopt => Ok(&self.set_sockopt),
556 AttachType::CgroupGetsockopt => Ok(&self.get_sockopt),
557 _ => error!(ENOTSUP),
558 }
559 }
560
561 pub fn run_sock_addr_prog(
564 &self,
565 current_task: &CurrentTask,
566 op: SockAddrOp,
567 domain: SocketDomain,
568 socket_type: SocketType,
569 protocol: SocketProtocol,
570 socket_address: &[u8],
571 socket: &Socket,
572 ) -> Result<SockAddrProgramResult, Errno> {
573 let prog_cell = match (domain, op) {
574 (SocketDomain::Inet, SockAddrOp::Bind) => &self.inet4_bind,
575 (SocketDomain::Inet6, SockAddrOp::Bind) => &self.inet6_bind,
576 (SocketDomain::Inet, SockAddrOp::Connect) => &self.inet4_connect,
577 (SocketDomain::Inet6, SockAddrOp::Connect) => &self.inet6_connect,
578 (SocketDomain::Inet, SockAddrOp::UdpSendMsg) => &self.udp4_sendmsg,
579 (SocketDomain::Inet6, SockAddrOp::UdpSendMsg) => &self.udp6_sendmsg,
580 (SocketDomain::Inet, SockAddrOp::UdpRecvMsg) => &self.udp4_recvmsg,
581 (SocketDomain::Inet6, SockAddrOp::UdpRecvMsg) => &self.udp6_recvmsg,
582 _ => return Ok(SockAddrProgramResult::Allow),
583 };
584
585 let Some(prog) = prog_cell.upgrade() else {
586 return Ok(SockAddrProgramResult::Allow);
587 };
588
589 let bpf_sock = BpfSock::from_socket(socket);
590
591 let mut bpf_sockaddr = BpfSockAddr { sock_addr: Default::default(), bpf_sock: &bpf_sock };
592 bpf_sockaddr.family = domain.as_raw().into();
593 bpf_sockaddr.type_ = socket_type.as_raw();
594 bpf_sockaddr.protocol = protocol.as_raw();
595
596 let (sa_family, _) = u16::read_from_prefix(socket_address).map_err(|_| errno!(EINVAL))?;
597
598 if domain.as_raw() != sa_family {
599 return error!(EAFNOSUPPORT);
600 }
601 bpf_sockaddr.user_family = sa_family.into();
602
603 match sa_family.into() {
604 linux_uapi::AF_INET => {
605 let (sockaddr, _) = linux_uapi::sockaddr_in::ref_from_prefix(socket_address)
606 .map_err(|_| errno!(EINVAL))?;
607 bpf_sockaddr.user_port = sockaddr.sin_port.into();
608 bpf_sockaddr.user_ip4 = sockaddr.sin_addr.s_addr;
609 }
610 linux_uapi::AF_INET6 => {
611 let sockaddr = linux_uapi::sockaddr_in6::ref_from_prefix(socket_address)
612 .map_err(|_| errno!(EINVAL))?
613 .0;
614 bpf_sockaddr.user_port = sockaddr.sin6_port.into();
615 bpf_sockaddr.user_ip6 = unsafe { sockaddr.sin6_addr.in6_u.u6_addr32 };
617 }
618 _ => return error!(EAFNOSUPPORT),
619 }
620
621 bpf_sockaddr.__bindgen_anon_1.sk =
622 (uaddr { addr: BpfValue::from(&bpf_sock).into() }).into();
623
624 let can_block = op != SockAddrOp::UdpRecvMsg;
626 Ok(prog.run(current_task, &mut bpf_sockaddr, can_block))
627 }
628
629 pub fn run_sock_prog(
630 &self,
631 current_task: &CurrentTask,
632 op: SockOp,
633 domain: SocketDomain,
634 socket_type: SocketType,
635 protocol: SocketProtocol,
636 socket: &ZxioBackedSocket,
637 ) -> SockProgramResult {
638 let prog_cell = match op {
639 SockOp::Create => &self.sock_create,
640 SockOp::Release => &self.sock_release,
641 };
642 let Some(prog) = prog_cell.upgrade() else {
643 return SockProgramResult::Allow;
644 };
645
646 let bpf_sock = BpfSock {
647 value: bpf_sock {
648 family: domain.as_raw().into(),
649 type_: socket_type.as_raw(),
650 protocol: protocol.as_raw(),
651 state: SocketBpfState::Close.into(),
652 ..Default::default()
653 },
654 socket: Some(socket),
655 };
656
657 prog.run(current_task, &bpf_sock)
658 }
659
660 pub fn run_getsockopt_prog(
661 &self,
662 current_task: &CurrentTask,
663 level: u32,
664 optname: u32,
665 value_buf: Vec<u8>,
666 optlen: usize,
667 error: Option<Errno>,
668 socket: &Socket,
669 ) -> Result<(Vec<u8>, usize), Errno> {
670 let Some(prog) = self.get_sockopt.upgrade() else {
671 return error.map(|e| Err(e)).unwrap_or_else(|| Ok((value_buf, optlen)));
672 };
673
674 let bpf_sock = BpfSock::from_socket(socket);
675
676 let retval = error.as_ref().map(|e| -(e.code.error_code() as i32)).unwrap_or(0);
677 let mut bpf_sockopt = BpfSockOptWithValue::new(
678 level,
679 optname,
680 value_buf.clone(),
681 optlen as u32,
682 retval,
683 &bpf_sock,
684 );
685
686 let result = prog.run(current_task, &mut bpf_sockopt);
688
689 let retval = bpf_sockopt.retval;
690
691 let retval = match result {
692 0 if is_error_return_value(retval) => retval,
693 0 => -(linux_uapi::EPERM as i32),
694 1 => retval,
695 _ => {
696 log_error!("eBPF getsockopt program returned invalid result: {}", result);
699 retval
700 }
701 };
702
703 if retval < 0 {
704 return Err(Errno::new(ErrnoCode::from_error_code(-retval as i16)));
705 }
706
707 let new_optlen = bpf_sockopt.optlen;
708
709 match usize::try_from(new_optlen) {
710 Err(_) => error!(EFAULT),
712 Ok(new_optlen) if new_optlen > value_buf.len() => error!(EFAULT),
713
714 Ok(0) => Ok((value_buf, optlen)),
716
717 Ok(new_optlen) => Ok((bpf_sockopt.take_value(), new_optlen)),
718 }
719 }
720
721 pub fn run_setsockopt_prog(
722 &self,
723 current_task: &CurrentTask,
724 level: u32,
725 optname: u32,
726 value: SockOptValue,
727 socket: &Socket,
728 ) -> SetSockOptProgramResult {
729 let Some(prog) = self.set_sockopt.upgrade() else {
730 return SetSockOptProgramResult::Allow(value);
731 };
732
733 let page_size = *PAGE_SIZE as usize;
734
735 let buffer = match value.read_bytes(current_task, page_size) {
738 Ok(buffer) => buffer,
739 Err(err) => return SetSockOptProgramResult::Fail(err),
740 };
741
742 let bpf_sock = BpfSock::from_socket(socket);
743
744 let buffer_len = buffer.len();
745 let optlen = value.len();
746 let mut bpf_sockopt =
747 BpfSockOptWithValue::new(level, optname, buffer, optlen as u32, 0, &bpf_sock);
748 let result = prog.run(current_task, &mut bpf_sockopt);
749
750 let retval = bpf_sockopt.retval;
751
752 let retval = match result {
753 0 if is_error_return_value(retval) => retval,
754 0 => -(linux_uapi::EPERM as i32),
755 1 => retval,
756 _ => {
757 log_error!("eBPF getsockopt program returned invalid result: {}", result);
760 retval
761 }
762 };
763
764 if retval < 0 {
765 return SetSockOptProgramResult::Fail(Errno::new(ErrnoCode::from_error_code(
766 -retval as i16,
767 )));
768 }
769
770 match bpf_sockopt.optlen {
771 -1 => SetSockOptProgramResult::Bypass,
774
775 new_optlen if optlen > page_size && (new_optlen as usize) == optlen => {
779 SetSockOptProgramResult::Allow(value)
780 }
781
782 optlen if optlen < 0 || (optlen as usize) > buffer_len => {
785 SetSockOptProgramResult::Fail(errno!(EFAULT))
786 }
787
788 0 => SetSockOptProgramResult::Allow(value),
790
791 optlen => {
794 let mut value = bpf_sockopt.take_value();
795 value.resize(optlen as usize, 0);
796 SetSockOptProgramResult::Allow(value.into())
797 }
798 }
799 }
800}
801
802fn attach_type_to_netstack_hook(attach_type: AttachType) -> Option<fnet_filter::SocketHook> {
803 let hook = match attach_type {
804 AttachType::CgroupInetEgress => fnet_filter::SocketHook::Egress,
805 AttachType::CgroupInetIngress => fnet_filter::SocketHook::Ingress,
806 _ => return None,
807 };
808 Some(hook)
809}
810
811#[derive(Copy, Clone, Debug, PartialEq, Eq)]
813enum AttachLocation {
814 Kernel,
816
817 Netstack,
819}
820
821impl TryFrom<AttachType> for AttachLocation {
822 type Error = Errno;
823
824 fn try_from(attach_type: AttachType) -> Result<Self, Self::Error> {
825 match attach_type {
826 AttachType::CgroupInet4Bind
827 | AttachType::CgroupInet6Bind
828 | AttachType::CgroupInet4Connect
829 | AttachType::CgroupInet6Connect
830 | AttachType::CgroupUdp4Sendmsg
831 | AttachType::CgroupUdp6Sendmsg
832 | AttachType::CgroupUdp4Recvmsg
833 | AttachType::CgroupUdp6Recvmsg
834 | AttachType::CgroupInetSockCreate
835 | AttachType::CgroupInetSockRelease
836 | AttachType::CgroupGetsockopt
837 | AttachType::CgroupSetsockopt => Ok(AttachLocation::Kernel),
838
839 AttachType::CgroupInetEgress | AttachType::CgroupInetIngress => {
840 Ok(AttachLocation::Netstack)
841 }
842
843 AttachType::CgroupDevice
844 | AttachType::CgroupInet4Getpeername
845 | AttachType::CgroupInet4Getsockname
846 | AttachType::CgroupInet4PostBind
847 | AttachType::CgroupInet6Getpeername
848 | AttachType::CgroupInet6Getsockname
849 | AttachType::CgroupInet6PostBind
850 | AttachType::CgroupSysctl
851 | AttachType::CgroupUnixConnect
852 | AttachType::CgroupUnixGetpeername
853 | AttachType::CgroupUnixGetsockname
854 | AttachType::CgroupUnixRecvmsg
855 | AttachType::CgroupUnixSendmsg
856 | AttachType::CgroupSockOps
857 | AttachType::SkSkbStreamParser
858 | AttachType::SkSkbStreamVerdict
859 | AttachType::SkMsgVerdict
860 | AttachType::LircMode2
861 | AttachType::FlowDissector
862 | AttachType::TraceRawTp
863 | AttachType::TraceFentry
864 | AttachType::TraceFexit
865 | AttachType::ModifyReturn
866 | AttachType::LsmMac
867 | AttachType::TraceIter
868 | AttachType::XdpDevmap
869 | AttachType::XdpCpumap
870 | AttachType::SkLookup
871 | AttachType::Xdp
872 | AttachType::SkSkbVerdict
873 | AttachType::SkReuseportSelect
874 | AttachType::SkReuseportSelectOrMigrate
875 | AttachType::PerfEvent
876 | AttachType::TraceKprobeMulti
877 | AttachType::LsmCgroup
878 | AttachType::StructOps
879 | AttachType::Netfilter
880 | AttachType::TcxIngress
881 | AttachType::TcxEgress
882 | AttachType::TraceUprobeMulti
883 | AttachType::NetkitPrimary
884 | AttachType::NetkitPeer
885 | AttachType::TraceKprobeSession => {
886 track_stub!(TODO("https://fxbug.dev/322873416"), "BPF_PROG_ATTACH", attach_type);
887 error!(ENOTSUP)
888 }
889
890 AttachType::Unspecified | AttachType::Invalid(_) => {
891 error!(EINVAL)
892 }
893 }
894 }
895}
896
897fn get_capability_for_program(program_type: ProgramType) -> Result<Capabilities, Errno> {
898 match program_type {
899 ProgramType::CgroupSkb
900 | ProgramType::CgroupSock
901 | ProgramType::CgroupSockAddr
902 | ProgramType::CgroupSockopt
903 | ProgramType::CgroupSysctl => Ok(CAP_NET_ADMIN),
904
905 ProgramType::CgroupDevice
908 | ProgramType::Ext
909 | ProgramType::FlowDissector
910 | ProgramType::Kprobe
911 | ProgramType::LircMode2
912 | ProgramType::Lsm
913 | ProgramType::LwtIn
914 | ProgramType::LwtOut
915 | ProgramType::LwtSeg6Local
916 | ProgramType::LwtXmit
917 | ProgramType::Netfilter
918 | ProgramType::PerfEvent
919 | ProgramType::RawTracepoint
920 | ProgramType::RawTracepointWritable
921 | ProgramType::SchedAct
922 | ProgramType::SchedCls
923 | ProgramType::SkLookup
924 | ProgramType::SkMsg
925 | ProgramType::SkReuseport
926 | ProgramType::SkSkb
927 | ProgramType::SocketFilter
928 | ProgramType::SockOps
929 | ProgramType::StructOps
930 | ProgramType::Syscall
931 | ProgramType::Tracepoint
932 | ProgramType::Tracing
933 | ProgramType::Unspec
934 | ProgramType::Xdp => error!(ENOTSUP),
935 }
936}
937
938#[derive(Default)]
939pub struct EbpfAttachments {
940 root_cgroup: CgroupEbpfProgramSet,
941 socket_control: OnceLock<fnet_filter::SocketControlSynchronousProxy>,
942}
943
944impl EbpfAttachments {
945 pub fn root_cgroup(&self) -> &CgroupEbpfProgramSet {
946 &self.root_cgroup
947 }
948
949 fn socket_control(&self) -> &fnet_filter::SocketControlSynchronousProxy {
950 self.socket_control.get_or_init(|| {
951 connect_to_protocol_sync::<fnet_filter::SocketControlMarker>()
952 .expect("Failed to connect to fuchsia.net.filter.SocketControl.")
953 })
954 }
955
956 fn attach_prog(
957 &self,
958 current_task: &CurrentTask,
959 attach_type: AttachType,
960 target_fd: FdNumber,
961 program: ProgramHandle,
962 ) -> Result<SyscallResult, Errno> {
963 let location: AttachLocation = attach_type.try_into()?;
964 let program_type = attach_type.get_program_type();
965 match (location, program_type) {
966 (AttachLocation::Kernel, ProgramType::CgroupSockAddr) => {
967 check_root_cgroup_fd(current_task, target_fd)?;
968
969 let linked_program = SockAddrProgram(program.link(attach_type.get_program_type())?);
970 self.root_cgroup
971 .get_sock_addr_program(attach_type)?
972 .lock()
973 .update(Some(Arc::new(linked_program)));
974
975 Ok(SUCCESS)
976 }
977
978 (AttachLocation::Kernel, ProgramType::CgroupSock) => {
979 check_root_cgroup_fd(current_task, target_fd)?;
980
981 let linked_program = SockProgram(program.link(attach_type.get_program_type())?);
982 self.root_cgroup
983 .get_sock_program(attach_type)?
984 .lock()
985 .update(Some(Arc::new(linked_program)));
986
987 Ok(SUCCESS)
988 }
989
990 (AttachLocation::Kernel, ProgramType::CgroupSockopt) => {
991 check_root_cgroup_fd(current_task, target_fd)?;
992
993 let linked_program = SockOptProgram(program.link(attach_type.get_program_type())?);
994 self.root_cgroup
995 .get_sock_opt_program(attach_type)?
996 .lock()
997 .update(Some(Arc::new(linked_program)));
998
999 Ok(SUCCESS)
1000 }
1001
1002 (AttachLocation::Kernel, _) => {
1003 unreachable!();
1004 }
1005
1006 (AttachLocation::Netstack, _) => {
1007 check_root_cgroup_fd(current_task, target_fd)?;
1008 self.attach_prog_in_netstack(attach_type, program)
1009 }
1010 }
1011 }
1012
1013 fn detach_prog(
1014 &self,
1015 current_task: &CurrentTask,
1016 attach_type: AttachType,
1017 target_fd: FdNumber,
1018 ) -> Result<SyscallResult, Errno> {
1019 let location = attach_type.try_into()?;
1020 let program_type = attach_type.get_program_type();
1021 match (location, program_type) {
1022 (AttachLocation::Kernel, ProgramType::CgroupSockAddr) => {
1023 check_root_cgroup_fd(current_task, target_fd)?;
1024
1025 let prog_cell = self.root_cgroup.get_sock_addr_program(attach_type)?.lock();
1026 if prog_cell.is_none() {
1027 return error!(ENOENT);
1028 }
1029
1030 prog_cell.update(None);
1031
1032 Ok(SUCCESS)
1033 }
1034
1035 (AttachLocation::Kernel, ProgramType::CgroupSock) => {
1036 check_root_cgroup_fd(current_task, target_fd)?;
1037
1038 let prog_cell = self.root_cgroup.get_sock_program(attach_type)?.lock();
1039 if prog_cell.is_none() {
1040 return error!(ENOENT);
1041 }
1042
1043 prog_cell.update(None);
1044
1045 Ok(SUCCESS)
1046 }
1047
1048 (AttachLocation::Kernel, ProgramType::CgroupSockopt) => {
1049 check_root_cgroup_fd(current_task, target_fd)?;
1050
1051 let prog_cell = self.root_cgroup.get_sock_opt_program(attach_type)?.lock();
1052 if prog_cell.is_none() {
1053 return error!(ENOENT);
1054 }
1055
1056 prog_cell.update(None);
1057
1058 Ok(SUCCESS)
1059 }
1060
1061 (AttachLocation::Kernel, _) => {
1062 unreachable!();
1063 }
1064
1065 (AttachLocation::Netstack, _) => {
1066 check_root_cgroup_fd(current_task, target_fd)?;
1067 self.detach_prog_in_netstack(attach_type)
1068 }
1069 }
1070 }
1071
1072 fn attach_prog_in_netstack(
1073 &self,
1074 attach_type: AttachType,
1075 program: ProgramHandle,
1076 ) -> Result<SyscallResult, Errno> {
1077 let hook = attach_type_to_netstack_hook(attach_type).ok_or_else(|| errno!(ENOTSUP))?;
1078 let opts = fnet_filter::AttachEbpfProgramOptions {
1079 hook: Some(hook),
1080 program: Some((&**program).try_into()?),
1081 ..Default::default()
1082 };
1083 self.socket_control()
1084 .attach_ebpf_program(opts, zx::MonotonicInstant::INFINITE)
1085 .map_err(|e| {
1086 log_error!(
1087 "failed to send fuchsia.net.filter/SocketControl.AttachEbpfProgram: {}",
1088 e
1089 );
1090 errno!(EIO)
1091 })?
1092 .map_err(|e| {
1093 use fnet_filter::SocketControlAttachEbpfProgramError as Error;
1094 match e {
1095 Error::NotSupported => errno!(ENOTSUP),
1096 Error::LinkFailed => errno!(EINVAL),
1097 Error::MapFailed => errno!(EIO),
1098 Error::DuplicateAttachment => errno!(EEXIST),
1099 }
1100 })?;
1101
1102 Ok(SUCCESS)
1103 }
1104
1105 fn detach_prog_in_netstack(&self, attach_type: AttachType) -> Result<SyscallResult, Errno> {
1106 let hook = attach_type_to_netstack_hook(attach_type).ok_or_else(|| errno!(ENOTSUP))?;
1107 self.socket_control()
1108 .detach_ebpf_program(hook, zx::MonotonicInstant::INFINITE)
1109 .map_err(|e| {
1110 log_error!(
1111 "failed to send fuchsia.net.filter/SocketControl.DetachEbpfProgram: {}",
1112 e
1113 );
1114 errno!(EIO)
1115 })?
1116 .map_err(|e| {
1117 use fnet_filter::SocketControlDetachEbpfProgramError as Error;
1118 match e {
1119 Error::NotFound => errno!(ENOENT),
1120 }
1121 })?;
1122 Ok(SUCCESS)
1123 }
1124}
1125
1126#[cfg(test)]
1127mod tests {
1128 use super::*;
1129 use crate::bpf::program::{Program, ProgramInfo};
1130 use crate::task::dynamic_thread_spawner::SpawnRequestBuilder;
1131 use crate::testing::{PanickingFile, PanickingFsNode, spawn_kernel_and_run};
1132 use crate::vfs::buffers::VecOutputBuffer;
1133 use crate::vfs::{
1134 CacheMode, FdFlags, FileObject, FileSystem, FileSystemOps, FileSystemOptions, FsStr,
1135 };
1136 use ebpf::{
1137 EbpfInstruction, MapFlags, MapSchema, NullVerifierLogger, link_program, verify_program,
1138 };
1139 use linux_uapi::{
1140 AF_INET, BPF_ALU64, BPF_EXIT, BPF_JMP, BPF_K, BPF_MOV, bpf_map_type_BPF_MAP_TYPE_ARRAY,
1141 bpf_map_type_BPF_MAP_TYPE_RINGBUF, sockaddr_in,
1142 };
1143 use starnix_types::vfs::default_statfs;
1144 use starnix_uapi::open_flags::OpenFlags;
1145 use starnix_uapi::statfs;
1146 use std::sync::atomic::{AtomicBool, Ordering};
1147 use syncio::Zxio;
1148 use zerocopy::IntoBytes as _;
1149
1150 const STRESS_WRITER_ITERATIONS: i32 = 50;
1151 const STRESS_READER_THREADS: usize = 2;
1152
1153 struct TestCgroup2Fs;
1154 impl FileSystemOps for TestCgroup2Fs {
1155 fn statfs(&self, _fs: &FileSystem, _current_task: &CurrentTask) -> Result<statfs, Errno> {
1156 Ok(default_statfs(CGROUP2_SUPER_MAGIC))
1157 }
1158 fn name(&self) -> &'static FsStr {
1159 "cgroup2".into()
1160 }
1161 }
1162
1163 fn create_root_cgroup_fd(current_task: &CurrentTask) -> FdNumber {
1164 let fs = FileSystem::new(
1165 current_task.kernel(),
1166 CacheMode::Uncached,
1167 TestCgroup2Fs,
1168 FileSystemOptions::default(),
1169 )
1170 .expect("create cgroup2 filesystem");
1171 let root_ino = fs.allocate_ino();
1172 fs.create_root(root_ino, PanickingFsNode);
1173 let root_node = fs.root().node.clone();
1174 let file = FileObject::new_anonymous(
1175 current_task,
1176 Box::new(PanickingFile),
1177 root_node,
1178 OpenFlags::RDONLY,
1179 );
1180 current_task.add_file(file, FdFlags::empty()).expect("add root cgroup fd")
1181 }
1182
1183 fn make_cgroup_program(
1184 current_task: &CurrentTask,
1185 program_type: ProgramType,
1186 expected_attach_type: AttachType,
1187 return_value: i32,
1188 ) -> ProgramHandle {
1189 let info =
1190 ProgramInfo { program_type, expected_attach_type, log_level: Default::default() };
1191 let code = vec![
1192 EbpfInstruction::new((BPF_ALU64 | BPF_MOV | BPF_K) as u8, 0, 0, 0, return_value),
1193 EbpfInstruction::new((BPF_JMP | BPF_EXIT) as u8, 0, 0, 0, 0),
1194 ];
1195 let mut logger = VecOutputBuffer::new(0);
1196 Program::new(current_task, info, &mut logger, code).expect("create eBPF program")
1197 }
1198
1199 #[::fuchsia::test]
1200 async fn test_cgroup_ebpf_attach_detach_and_vmar_teardown() {
1201 spawn_kernel_and_run(async |current_task| {
1202 let ebpf_state = ¤t_task.kernel().ebpf_state;
1203 let root_cgroup = ebpf_state.attachments.root_cgroup();
1204
1205 assert!(root_cgroup.inet4_bind.is_none());
1207 assert!(root_cgroup.inet6_bind.is_none());
1208 assert!(root_cgroup.inet4_connect.is_none());
1209 assert!(root_cgroup.inet6_connect.is_none());
1210 assert!(root_cgroup.udp4_sendmsg.is_none());
1211 assert!(root_cgroup.udp6_sendmsg.is_none());
1212 assert!(root_cgroup.udp4_recvmsg.is_none());
1213 assert!(root_cgroup.udp6_recvmsg.is_none());
1214 assert!(root_cgroup.sock_create.is_none());
1215 assert!(root_cgroup.sock_release.is_none());
1216 assert!(root_cgroup.set_sockopt.is_none());
1217 assert!(root_cgroup.get_sockopt.is_none());
1218
1219 let cgroup_fd = create_root_cgroup_fd(current_task);
1220 let zxio = Zxio::create(zx::Event::create().into()).expect("create zxio");
1221 let zxio_socket = ZxioBackedSocket::new_with_zxio(current_task, zxio);
1222 let unix_socket = Socket::new(
1223 current_task,
1224 SocketDomain::Unix,
1225 SocketType::Datagram,
1226 SocketProtocol::default(),
1227 true,
1228 )
1229 .expect("create socket");
1230 let inet4_sockaddr = sockaddr_in { sin_family: AF_INET as u16, ..Default::default() };
1231
1232 assert_eq!(
1234 root_cgroup.run_sock_prog(
1235 current_task,
1236 SockOp::Create,
1237 SocketDomain::Inet,
1238 SocketType::Stream,
1239 SocketProtocol::TCP,
1240 &zxio_socket,
1241 ),
1242 SockProgramResult::Allow
1243 );
1244 assert_eq!(
1245 root_cgroup.run_sock_addr_prog(
1246 current_task,
1247 SockAddrOp::Bind,
1248 SocketDomain::Inet,
1249 SocketType::Stream,
1250 SocketProtocol::TCP,
1251 inet4_sockaddr.as_bytes(),
1252 &unix_socket,
1253 ),
1254 Ok(SockAddrProgramResult::Allow)
1255 );
1256 assert_eq!(
1257 root_cgroup.run_getsockopt_prog(
1258 current_task,
1259 0,
1260 0,
1261 vec![1, 2, 3, 4],
1262 4,
1263 None,
1264 &unix_socket,
1265 ),
1266 Ok((vec![1, 2, 3, 4], 4))
1267 );
1268 assert!(matches!(
1269 root_cgroup.run_setsockopt_prog(
1270 current_task,
1271 0,
1272 0,
1273 vec![1, 2, 3, 4].into(),
1274 &unix_socket,
1275 ),
1276 SetSockOptProgramResult::Allow(_)
1277 ));
1278
1279 assert_eq!(
1281 ebpf_state.attachments.detach_prog(
1282 current_task,
1283 AttachType::CgroupInetSockCreate,
1284 cgroup_fd,
1285 ),
1286 error!(ENOENT)
1287 );
1288
1289 let ringbuf_map = Map::new(
1292 MapSchema {
1293 map_type: bpf_map_type_BPF_MAP_TYPE_RINGBUF,
1294 key_size: 0,
1295 value_size: 0,
1296 max_entries: *PAGE_SIZE as u32,
1297 flags: MapFlags::empty(),
1298 },
1299 "test_ringbuf_vmar",
1300 )
1301 .expect("create ringbuf map with AllocatedVmar");
1302 let array_map = Map::new(
1303 MapSchema {
1304 map_type: bpf_map_type_BPF_MAP_TYPE_ARRAY,
1305 key_size: 4,
1306 value_size: 8,
1307 max_entries: 1,
1308 flags: MapFlags::empty(),
1309 },
1310 "test_array_map",
1311 )
1312 .expect("create array map");
1313 let code = vec![
1314 EbpfInstruction::new((BPF_ALU64 | BPF_MOV | BPF_K) as u8, 0, 0, 0, 0),
1315 EbpfInstruction::new((BPF_JMP | BPF_EXIT) as u8, 0, 0, 0, 0),
1316 ];
1317 let calling_context = ProgramType::CgroupSock
1318 .create_calling_context(
1319 AttachType::CgroupInetSockCreate,
1320 vec![ringbuf_map.schema, array_map.schema],
1321 )
1322 .expect("calling context");
1323 let verified = verify_program(code, calling_context, &mut NullVerifierLogger)
1324 .expect("verify program");
1325 let linked =
1326 link_program::<SockProgram>(&verified, vec![ringbuf_map, array_map]).expect("link");
1327 root_cgroup
1328 .get_sock_program(AttachType::CgroupInetSockCreate)
1329 .expect("get sock_create slot")
1330 .lock()
1331 .update(Some(Arc::new(SockProgram(linked))));
1332
1333 assert_eq!(
1334 root_cgroup.run_sock_prog(
1335 current_task,
1336 SockOp::Create,
1337 SocketDomain::Inet,
1338 SocketType::Stream,
1339 SocketProtocol::TCP,
1340 &zxio_socket,
1341 ),
1342 SockProgramResult::Block
1343 );
1344
1345 let allow_prog = make_cgroup_program(
1348 current_task,
1349 ProgramType::CgroupSock,
1350 AttachType::CgroupInetSockCreate,
1351 1,
1352 );
1353 ebpf_state
1354 .attachments
1355 .attach_prog(current_task, AttachType::CgroupInetSockCreate, cgroup_fd, allow_prog)
1356 .expect("attach allow program");
1357 assert_eq!(
1358 root_cgroup.run_sock_prog(
1359 current_task,
1360 SockOp::Create,
1361 SocketDomain::Inet,
1362 SocketType::Stream,
1363 SocketProtocol::TCP,
1364 &zxio_socket,
1365 ),
1366 SockProgramResult::Allow
1367 );
1368
1369 ebpf_state
1370 .attachments
1371 .detach_prog(current_task, AttachType::CgroupInetSockCreate, cgroup_fd)
1372 .expect("detach program");
1373 assert!(root_cgroup.sock_create.is_none());
1374 current_task.trigger_delayed_releaser();
1375 fuchsia_rcu::rcu_run_callbacks();
1376 })
1377 .await;
1378 }
1379
1380 #[::fuchsia::test]
1381 async fn test_concurrent_cgroup_ebpf_attach_detach_and_execution() {
1382 spawn_kernel_and_run(async |current_task| {
1383 let kernel = current_task.kernel();
1384 let cgroup_fd = create_root_cgroup_fd(current_task);
1385 let unix_socket = Socket::new(
1386 current_task,
1387 SocketDomain::Unix,
1388 SocketType::Datagram,
1389 SocketProtocol::default(),
1390 true,
1391 )
1392 .expect("create socket");
1393 let done = Arc::new(AtomicBool::new(false));
1394
1395 let mut reader_results = Vec::new();
1396 for _ in 0..STRESS_READER_THREADS {
1397 let socket_clone = unix_socket.clone();
1398 let done_clone = done.clone();
1399 let (res_fn, req) = SpawnRequestBuilder::new()
1400 .with_sync_closure(move |task: &CurrentTask| {
1401 let zxio =
1402 Zxio::create(zx::Event::create().into()).expect("create reader zxio");
1403 let zxio_socket = ZxioBackedSocket::new_with_zxio(task, zxio);
1404 let inet4_sockaddr =
1405 sockaddr_in { sin_family: AF_INET as u16, ..Default::default() };
1406 let cgroup = task.kernel().ebpf_state.attachments.root_cgroup();
1407 while !done_clone.load(Ordering::Relaxed) {
1408 let sock_res = cgroup.run_sock_prog(
1409 task,
1410 SockOp::Create,
1411 SocketDomain::Inet,
1412 SocketType::Stream,
1413 SocketProtocol::TCP,
1414 &zxio_socket,
1415 );
1416 assert!(matches!(
1417 sock_res,
1418 SockProgramResult::Allow | SockProgramResult::Block
1419 ));
1420
1421 let addr_res = cgroup
1422 .run_sock_addr_prog(
1423 task,
1424 SockAddrOp::Bind,
1425 SocketDomain::Inet,
1426 SocketType::Stream,
1427 SocketProtocol::TCP,
1428 inet4_sockaddr.as_bytes(),
1429 &socket_clone,
1430 )
1431 .expect("run_sock_addr_prog");
1432 assert!(matches!(
1433 addr_res,
1434 SockAddrProgramResult::Allow | SockAddrProgramResult::Block
1435 ));
1436 }
1437 })
1438 .build_with_sync_result();
1439 kernel.kthreads.spawner().spawn_from_request(req);
1440 reader_results.push(res_fn);
1441 }
1442
1443 for iteration in 0..STRESS_WRITER_ITERATIONS {
1444 let sock_prog = make_cgroup_program(
1445 current_task,
1446 ProgramType::CgroupSock,
1447 AttachType::CgroupInetSockCreate,
1448 iteration % 2,
1449 );
1450 kernel
1451 .ebpf_state
1452 .attachments
1453 .attach_prog(
1454 current_task,
1455 AttachType::CgroupInetSockCreate,
1456 cgroup_fd,
1457 sock_prog,
1458 )
1459 .expect("attach sock_create");
1460
1461 let addr_prog = make_cgroup_program(
1462 current_task,
1463 ProgramType::CgroupSockAddr,
1464 AttachType::CgroupInet4Bind,
1465 iteration % 2,
1466 );
1467 kernel
1468 .ebpf_state
1469 .attachments
1470 .attach_prog(current_task, AttachType::CgroupInet4Bind, cgroup_fd, addr_prog)
1471 .expect("attach inet4_bind");
1472
1473 if iteration % 3 == 0 {
1474 kernel
1475 .ebpf_state
1476 .attachments
1477 .detach_prog(current_task, AttachType::CgroupInetSockCreate, cgroup_fd)
1478 .expect("detach sock_create");
1479 kernel
1480 .ebpf_state
1481 .attachments
1482 .detach_prog(current_task, AttachType::CgroupInet4Bind, cgroup_fd)
1483 .expect("detach inet4_bind");
1484 }
1485 current_task.trigger_delayed_releaser();
1486 fuchsia_rcu::rcu_run_callbacks();
1487 }
1488
1489 done.store(true, Ordering::Relaxed);
1490 for res_fn in reader_results {
1491 res_fn().expect("reader thread completed");
1492 }
1493 let _ = kernel.ebpf_state.attachments.detach_prog(
1494 current_task,
1495 AttachType::CgroupInetSockCreate,
1496 cgroup_fd,
1497 );
1498 let _ = kernel.ebpf_state.attachments.detach_prog(
1499 current_task,
1500 AttachType::CgroupInet4Bind,
1501 cgroup_fd,
1502 );
1503 current_task.trigger_delayed_releaser();
1504 fuchsia_rcu::rcu_run_callbacks();
1505 })
1506 .await;
1507 }
1508}