Skip to main content

ebpf_api/
helpers.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
5use crate::MapKey;
6use crate::maps::{Map, MapValueRef, RingBuffer, RingBufferWakeupPolicy};
7use ebpf::{
8    BpfValue, EbpfBufferPtr, EbpfHelperImpl, EbpfProgramContext, FromBpfValue, HelperSet, MapFlags,
9};
10use inspect_stubs::track_stub;
11use linux_uapi::{
12    BPF_SK_STORAGE_GET_F_CREATE, bpf_func_id_BPF_FUNC_get_current_pid_tgid,
13    bpf_func_id_BPF_FUNC_get_current_uid_gid, bpf_func_id_BPF_FUNC_get_netns_cookie,
14    bpf_func_id_BPF_FUNC_get_retval, bpf_func_id_BPF_FUNC_get_smp_processor_id,
15    bpf_func_id_BPF_FUNC_get_socket_cookie, bpf_func_id_BPF_FUNC_get_socket_uid,
16    bpf_func_id_BPF_FUNC_ktime_get_boot_ns, bpf_func_id_BPF_FUNC_ktime_get_coarse_ns,
17    bpf_func_id_BPF_FUNC_ktime_get_ns, bpf_func_id_BPF_FUNC_map_delete_elem,
18    bpf_func_id_BPF_FUNC_map_lookup_elem, bpf_func_id_BPF_FUNC_map_update_elem,
19    bpf_func_id_BPF_FUNC_probe_read_str, bpf_func_id_BPF_FUNC_probe_read_user,
20    bpf_func_id_BPF_FUNC_probe_read_user_str, bpf_func_id_BPF_FUNC_ringbuf_discard,
21    bpf_func_id_BPF_FUNC_ringbuf_reserve, bpf_func_id_BPF_FUNC_ringbuf_submit,
22    bpf_func_id_BPF_FUNC_set_retval, bpf_func_id_BPF_FUNC_sk_fullsock,
23    bpf_func_id_BPF_FUNC_sk_lookup_tcp, bpf_func_id_BPF_FUNC_sk_lookup_udp,
24    bpf_func_id_BPF_FUNC_sk_release, bpf_func_id_BPF_FUNC_sk_storage_get,
25    bpf_func_id_BPF_FUNC_skb_load_bytes, bpf_func_id_BPF_FUNC_skb_load_bytes_relative,
26    bpf_func_id_BPF_FUNC_trace_printk, bpf_map_type_BPF_MAP_TYPE_RINGBUF,
27    bpf_map_type_BPF_MAP_TYPE_SK_STORAGE, gid_t, pid_t, uid_t,
28};
29use smallvec::SmallVec;
30use zerocopy::IntoBytes as _;
31
32pub trait MapsContext<'a> {
33    fn on_map_access(&mut self, map: &Map);
34    fn add_value_ref(&mut self, map_ref: MapValueRef<'a>);
35}
36
37pub trait MapsProgramContext: EbpfProgramContext {
38    fn on_map_access(context: &mut Self::RunContext<'_>, map: &Map);
39    fn add_value_ref<'a>(context: &mut Self::RunContext<'a>, map_ref: MapValueRef<'a>);
40}
41
42impl<C: EbpfProgramContext> MapsProgramContext for C
43where
44    for<'a> C::RunContext<'a>: MapsContext<'a>,
45{
46    fn on_map_access(context: &mut Self::RunContext<'_>, map: &Map) {
47        context.on_map_access(map);
48    }
49
50    fn add_value_ref<'a>(context: &mut Self::RunContext<'a>, map_ref: MapValueRef<'a>) {
51        context.add_value_ref(map_ref);
52    }
53}
54
55fn bpf_map_lookup_elem<'a, C: MapsProgramContext>(
56    context: &mut C::RunContext<'a>,
57    map: BpfValue,
58    key: BpfValue,
59    _: BpfValue,
60    _: BpfValue,
61    _: BpfValue,
62) -> BpfValue {
63    // SAFETY: The `map` must be a reference to a `Map` object kept alive by the program itself.
64    let map: &Map = unsafe { &*map.as_ptr::<Map>() };
65
66    // SAFETY: safety is ensured by the verifier.
67    let key = unsafe { EbpfBufferPtr::new(key.as_ptr::<u8>(), map.schema.key_size as usize) };
68    let key: MapKey = key.load();
69
70    C::on_map_access(context, map);
71
72    let Some(value_ref) = map.lookup(&key) else {
73        return BpfValue::default();
74    };
75
76    let result: BpfValue = value_ref.ptr().raw_ptr().into();
77
78    // If this is a map with ref-counted elements then save the reference for
79    // the lifetime of the program.
80    if value_ref.is_ref_counted() {
81        C::add_value_ref(context, value_ref);
82    }
83
84    result
85}
86
87fn bpf_map_update_elem<C: MapsProgramContext>(
88    context: &mut C::RunContext<'_>,
89    map: BpfValue,
90    key: BpfValue,
91    value: BpfValue,
92    flags: BpfValue,
93    _: BpfValue,
94) -> BpfValue {
95    // SAFETY: The `map` must be a reference to a `Map` object kept alive by the program itself.
96    let map: &Map = unsafe { &*map.as_ptr::<Map>() };
97
98    assert!(!map.schema.flags.contains(MapFlags::ProgReadOnly));
99
100    // TODO(https://fxbug.dev/496639039): This should be checked by the verifier.
101    if map.schema.map_type == bpf_map_type_BPF_MAP_TYPE_SK_STORAGE {
102        return BpfValue::default();
103    }
104
105    // SAFETY: safety is ensured by the verifier.
106    let key = unsafe { EbpfBufferPtr::new(key.as_ptr::<u8>(), map.schema.key_size as usize) };
107    let key: MapKey = key.load();
108
109    // SAFETY: safety is ensured by the verifier.
110    let value = unsafe { EbpfBufferPtr::new(value.as_ptr::<u8>(), map.schema.value_size as usize) };
111    let flags = flags.as_u64();
112
113    C::on_map_access(context, map);
114
115    map.update(&key, value, flags).map(|_| 0).unwrap_or(u64::MAX).into()
116}
117
118fn bpf_map_delete_elem<C: MapsProgramContext>(
119    context: &mut C::RunContext<'_>,
120    map: BpfValue,
121    key: BpfValue,
122    _: BpfValue,
123    _: BpfValue,
124    _: BpfValue,
125) -> BpfValue {
126    // SAFETY: The `map` must be a reference to a `Map` object kept alive by the program itself.
127    let map: &Map = unsafe { &*map.as_ptr::<Map>() };
128
129    assert!(!map.schema.flags.contains(MapFlags::ProgReadOnly));
130
131    // TODO(https://fxbug.dev/496639039): This should be checked by the verifier.
132    if map.schema.map_type == bpf_map_type_BPF_MAP_TYPE_SK_STORAGE {
133        return BpfValue::default();
134    }
135
136    // SAFETY: safety is ensured by the verifier.
137    let key = unsafe { EbpfBufferPtr::new(key.as_ptr::<u8>(), map.schema.key_size as usize) };
138    let key: MapKey = key.load();
139
140    C::on_map_access(context, map);
141
142    map.delete(&key).map(|_| 0).unwrap_or(u64::MAX).into()
143}
144
145fn bpf_trace_printk<C: EbpfProgramContext>(
146    _context: &mut C::RunContext<'_>,
147    _fmt: BpfValue,
148    _fmt_size: BpfValue,
149    _: BpfValue,
150    _: BpfValue,
151    _: BpfValue,
152) -> BpfValue {
153    track_stub!(TODO("https://fxbug.dev/534355500"), "bpf_trace_printk");
154    0.into()
155}
156
157fn bpf_ktime_get_ns<C: EbpfProgramContext>(
158    _context: &mut C::RunContext<'_>,
159    _: BpfValue,
160    _: BpfValue,
161    _: BpfValue,
162    _: BpfValue,
163    _: BpfValue,
164) -> BpfValue {
165    zx::MonotonicInstant::get().into_nanos().into()
166}
167
168fn bpf_ringbuf_reserve<C: EbpfProgramContext>(
169    _context: &mut C::RunContext<'_>,
170    map: BpfValue,
171    size: BpfValue,
172    flags: BpfValue,
173    _: BpfValue,
174    _: BpfValue,
175) -> BpfValue {
176    // SAFETY: The safety of the operation is ensured by the bpf verifier. The `map` must be a
177    // reference to a `Map` object kept alive by the program itself.
178    let map: &Map = unsafe { &*map.as_ptr::<Map>() };
179
180    // Map type is checked by the verifier.
181    assert!(map.schema.map_type == bpf_map_type_BPF_MAP_TYPE_RINGBUF);
182
183    let Ok(size) = u32::try_from(size) else {
184        return BpfValue::default();
185    };
186    let flags = u64::from(flags);
187    map.ringbuf_reserve(size, flags).map(BpfValue::from).unwrap_or_else(|_| BpfValue::default())
188}
189
190fn bpf_ringbuf_submit<C: EbpfProgramContext>(
191    _context: &mut C::RunContext<'_>,
192    data: BpfValue,
193    flags: BpfValue,
194    _: BpfValue,
195    _: BpfValue,
196    _: BpfValue,
197) -> BpfValue {
198    let flags = RingBufferWakeupPolicy::from(flags);
199
200    // SAFETY: The safety of the operation is ensured by the bpf verifier. The data has to come from
201    // the result of a reserve call.
202    unsafe {
203        RingBuffer::submit(u64::from(data), flags);
204    }
205    0.into()
206}
207
208fn bpf_ringbuf_discard<C: EbpfProgramContext>(
209    _context: &mut C::RunContext<'_>,
210    data: BpfValue,
211    flags: BpfValue,
212    _: BpfValue,
213    _: BpfValue,
214    _: BpfValue,
215) -> BpfValue {
216    let flags = RingBufferWakeupPolicy::from(flags);
217
218    // SAFETY: The safety of the operation is ensured by the bpf verifier. The data has to come from
219    // the result of a reserve call.
220    unsafe {
221        RingBuffer::discard(u64::from(data), flags);
222    }
223    0.into()
224}
225
226fn bpf_ktime_get_boot_ns<C: EbpfProgramContext>(
227    _context: &mut C::RunContext<'_>,
228    _: BpfValue,
229    _: BpfValue,
230    _: BpfValue,
231    _: BpfValue,
232    _: BpfValue,
233) -> BpfValue {
234    track_stub!(TODO("https://fxbug.dev/534355721"), "bpf_ktime_get_boot_ns");
235    0.into()
236}
237
238fn bpf_probe_read_user<C: EbpfProgramContext>(
239    _context: &mut C::RunContext<'_>,
240    dst: BpfValue,
241    size: BpfValue,
242    _src: BpfValue,
243    _: BpfValue,
244    _: BpfValue,
245) -> BpfValue {
246    track_stub!(TODO("https://fxbug.dev/534354547"), "bpf_probe_read_user");
247    // The real helper copies `size` bytes from user memory into `dst`. Until
248    // that is implemented, zero `dst`: the verifier models it as written
249    // (`output: true`), so leaving it untouched would let the program read back
250    // uninitialized executor memory.
251    let size = size.as_usize();
252    if size > 0 {
253        // SAFETY: the verifier guarantees `dst` points to `size` writable bytes
254        // (MemoryParameter { output: true, size: Reference { index: 1 } }).
255        unsafe { std::ptr::write_bytes(dst.as_ptr::<u8>(), 0, size) };
256    }
257    0.into()
258}
259
260fn bpf_probe_read_user_str<C: EbpfProgramContext>(
261    _context: &mut C::RunContext<'_>,
262    dst: BpfValue,
263    size: BpfValue,
264    _src: BpfValue,
265    _: BpfValue,
266    _: BpfValue,
267) -> BpfValue {
268    track_stub!(TODO("https://fxbug.dev/534355539"), "bpf_probe_read_user_str");
269    // The real helper copies a NUL-terminated string from user memory into
270    // `dst`. Until that is implemented, zero `dst`: the verifier models it as
271    // written (`output: true`), so leaving it untouched would let the program
272    // read back uninitialized executor memory.
273    let size = size.as_usize();
274    if size > 0 {
275        // SAFETY: the verifier guarantees `dst` points to `size` writable bytes
276        // (MemoryParameter { output: true, size: Reference { index: 1 } }).
277        unsafe { std::ptr::write_bytes(dst.as_ptr::<u8>(), 0, size) };
278    }
279    0.into()
280}
281
282fn bpf_ktime_get_coarse_ns<C: EbpfProgramContext>(
283    _context: &mut C::RunContext<'_>,
284    _: BpfValue,
285    _: BpfValue,
286    _: BpfValue,
287    _: BpfValue,
288    _: BpfValue,
289) -> BpfValue {
290    track_stub!(TODO("https://fxbug.dev/534355976"), "bpf_ktime_get_coarse_ns");
291    0.into()
292}
293
294fn bpf_probe_read_str<C: EbpfProgramContext>(
295    _context: &mut C::RunContext<'_>,
296    _: BpfValue,
297    _: BpfValue,
298    _: BpfValue,
299    _: BpfValue,
300    _: BpfValue,
301) -> BpfValue {
302    track_stub!(TODO("https://fxbug.dev/534355560"), "bpf_probe_read_str");
303    0.into()
304}
305
306fn bpf_get_smp_processor_id<C: EbpfProgramContext>(
307    _context: &mut C::RunContext<'_>,
308    _: BpfValue,
309    _: BpfValue,
310    _: BpfValue,
311    _: BpfValue,
312    _: BpfValue,
313) -> BpfValue {
314    track_stub!(TODO("https://fxbug.dev/534354909"), "bpf_get_smp_processor_id");
315    0.into()
316}
317
318pub trait CurrentTaskContext {
319    fn get_uid_gid(&self) -> (uid_t, gid_t);
320    fn get_tid_tgid(&self) -> (pid_t, pid_t);
321}
322
323pub trait CurrentTaskProgramContext: EbpfProgramContext {
324    fn get_uid_gid<'a>(context: &mut Self::RunContext<'a>) -> (uid_t, gid_t);
325    fn get_tid_tgid<'a>(context: &mut Self::RunContext<'a>) -> (pid_t, pid_t);
326}
327
328impl<C: EbpfProgramContext> CurrentTaskProgramContext for C
329where
330    for<'a> C::RunContext<'a>: CurrentTaskContext,
331{
332    fn get_uid_gid<'a>(context: &mut Self::RunContext<'a>) -> (uid_t, gid_t) {
333        context.get_uid_gid()
334    }
335    fn get_tid_tgid<'a>(context: &mut Self::RunContext<'a>) -> (pid_t, pid_t) {
336        context.get_tid_tgid()
337    }
338}
339
340fn bpf_get_current_uid_gid<C: CurrentTaskProgramContext>(
341    context: &mut C::RunContext<'_>,
342    _: BpfValue,
343    _: BpfValue,
344    _: BpfValue,
345    _: BpfValue,
346    _: BpfValue,
347) -> BpfValue {
348    let (uid, gid) = C::get_uid_gid(context);
349    (uid as u64 | (gid as u64) << 32).into()
350}
351
352fn bpf_get_current_pid_tgid<C: CurrentTaskProgramContext>(
353    context: &mut C::RunContext<'_>,
354    _: BpfValue,
355    _: BpfValue,
356    _: BpfValue,
357    _: BpfValue,
358    _: BpfValue,
359) -> BpfValue {
360    let (pid, tgid) = C::get_tid_tgid(context);
361    (pid as u64 | (tgid as u64) << 32).into()
362}
363
364// Trait for `EbpfProgramContext` where the first argument is a `SocketRef`,
365// i.e. it references a socket.
366pub trait Arg1IsSocketProgramContext: EbpfProgramContext {
367    type Arg1AsSocket<'a>: FromBpfValue<Self::RunContext<'a>> + SocketRef;
368}
369
370impl<C> Arg1IsSocketProgramContext for C
371where
372    C: EbpfProgramContext,
373    for<'a> Self::Arg1<'a>: FromBpfValue<Self::RunContext<'a>> + SocketRef,
374{
375    type Arg1AsSocket<'a> = Self::Arg1<'a>;
376}
377
378// Marker trait for `EbpfProgramContext` that supports `bpf_get_socket_uid`.
379pub trait SocketCookieProgramContext: Arg1IsSocketProgramContext {}
380impl<C> SocketCookieProgramContext for C where C: Arg1IsSocketProgramContext {}
381
382fn bpf_get_socket_cookie<'a, C: SocketCookieProgramContext>(
383    context: &mut C::RunContext<'a>,
384    arg1: BpfValue,
385    _: BpfValue,
386    _: BpfValue,
387    _: BpfValue,
388    _: BpfValue,
389) -> BpfValue {
390    // SAFETY: Verifier checks that the argument points at the value that
391    // that's passed as the first argument.
392    let arg1_as_socket = unsafe { C::Arg1AsSocket::from_bpf_value(context, arg1) };
393    arg1_as_socket.get_socket_cookie().unwrap_or(0).into()
394}
395
396pub trait SocketRef {
397    fn get_socket_cookie(&self) -> Option<u64>;
398    fn get_socket_uid(&self) -> Option<uid_t>;
399}
400
401// A trait for eBPF run context with `bpf_sock` pointers.
402pub trait BpfSockContext: Sized {
403    type BpfSockRef: SocketRef + FromBpfValue<Self>;
404}
405
406pub trait SkStorageProgramContext: EbpfProgramContext {
407    type BpfSockRef<'a>: SocketRef + FromBpfValue<Self::RunContext<'a>>;
408}
409
410impl<C> SkStorageProgramContext for C
411where
412    C: EbpfProgramContext,
413    for<'a> C::RunContext<'a>: BpfSockContext,
414{
415    type BpfSockRef<'a> = <C::RunContext<'a> as BpfSockContext>::BpfSockRef;
416}
417
418#[derive(Copy, Clone, Debug, PartialEq, Eq)]
419pub enum LoadBytesBase {
420    MacHeader,
421    NetworkHeader,
422}
423
424// Marker trait for `EbpfProgramContext` that supports `bpf_get_socket_uid`.
425pub trait SocketUidProgramContext: Arg1IsSocketProgramContext {}
426impl<C> SocketUidProgramContext for C where C: Arg1IsSocketProgramContext {}
427
428fn bpf_get_socket_uid<'a, C: SocketUidProgramContext>(
429    context: &mut C::RunContext<'a>,
430    sk_buf: BpfValue,
431    _: BpfValue,
432    _: BpfValue,
433    _: BpfValue,
434    _: BpfValue,
435) -> BpfValue {
436    const OVERFLOW_UID: uid_t = 65534;
437    // SAFETY: Verifier checks that the first argument points at a `__sk_buff`.
438    let sk_buf = unsafe { C::Arg1AsSocket::from_bpf_value(context, sk_buf) };
439    sk_buf.get_socket_uid().unwrap_or(OVERFLOW_UID).into()
440}
441
442// Trait for packets that support `bpf_load_bytes_relative`.
443pub trait PacketWithLoadBytes {
444    fn load_bytes_relative(
445        &self,
446        base: LoadBytesBase,
447        offset: usize,
448        buf: EbpfBufferPtr<'_>,
449    ) -> i64;
450}
451
452// Trait for `EbpfProgramContext` that supports `bpf_load_bytes_relative`.
453pub trait SkbLoadBytesProgramContext: EbpfProgramContext {
454    fn skb_load_bytes_relative<'a>(
455        context: &mut Self::RunContext<'a>,
456        sk_buf: BpfValue,
457        base: LoadBytesBase,
458        offset: usize,
459        buf: EbpfBufferPtr<'_>,
460    ) -> i64;
461}
462
463impl<C: EbpfProgramContext> SkbLoadBytesProgramContext for C
464where
465    for<'b> C::Arg1<'b>: FromBpfValue<C::RunContext<'b>>,
466    for<'b> C::Arg1<'b>: PacketWithLoadBytes,
467{
468    fn skb_load_bytes_relative<'a>(
469        context: &mut Self::RunContext<'a>,
470        sk_buf: BpfValue,
471        base: LoadBytesBase,
472        offset: usize,
473        buf: EbpfBufferPtr<'_>,
474    ) -> i64 {
475        // SAFETY: Verifier checks that the argument points at the same value
476        // that was passed to the program as the first argument.
477        let sk_buf = unsafe { C::Arg1::from_bpf_value(context, sk_buf) };
478        sk_buf.load_bytes_relative(base, offset, buf)
479    }
480}
481
482fn bpf_skb_load_bytes<'a, C: SkbLoadBytesProgramContext>(
483    context: &mut C::RunContext<'a>,
484    sk_buf: BpfValue,
485    offset: BpfValue,
486    to: BpfValue,
487    len: BpfValue,
488    _: BpfValue,
489) -> BpfValue {
490    let base = LoadBytesBase::NetworkHeader;
491
492    let Ok(offset) = offset.as_u64().try_into() else {
493        return u64::MAX.into();
494    };
495
496    // SAFETY: The verifier ensures that `to` points to a valid buffer of at
497    // least `len` bytes that the eBPF program has permission to access.
498    let buf = unsafe { EbpfBufferPtr::new(to.as_ptr::<u8>(), len.as_u64() as usize) };
499
500    C::skb_load_bytes_relative(context, sk_buf, base, offset, buf).into()
501}
502
503fn bpf_skb_load_bytes_relative<'a, C: SkbLoadBytesProgramContext>(
504    context: &mut C::RunContext<'a>,
505    sk_buf: BpfValue,
506    offset: BpfValue,
507    to: BpfValue,
508    len: BpfValue,
509    start_header: BpfValue,
510) -> BpfValue {
511    let base = match start_header.as_u64() {
512        0 => LoadBytesBase::MacHeader,
513        1 => LoadBytesBase::NetworkHeader,
514        _ => return u64::MAX.into(),
515    };
516
517    let Ok(offset) = offset.as_u64().try_into() else {
518        return u64::MAX.into();
519    };
520
521    // SAFETY: The verifier ensures that `to` points to a valid buffer of at
522    // least `len` bytes that the eBPF program has permission to access.
523    let buf = unsafe { EbpfBufferPtr::new(to.as_ptr::<u8>(), len.as_u64() as usize) };
524
525    C::skb_load_bytes_relative(context, sk_buf, base, offset, buf).into()
526}
527
528fn bpf_sk_storage_get<'a, C: SkStorageProgramContext + MapsProgramContext>(
529    context: &mut C::RunContext<'a>,
530    map: BpfValue,
531    sk: BpfValue,
532    value: BpfValue,
533    flags: BpfValue,
534    _: BpfValue,
535) -> BpfValue {
536    if sk.is_zero() {
537        return BpfValue::default();
538    }
539
540    // SAFETY: Verifier ensures that `sk` is either null or a pointer to
541    // `bpf_sock`. The null case is checked above.
542    let bpf_sock = unsafe { C::BpfSockRef::from_bpf_value(context, sk) };
543
544    // Use socket cookie to identify the socket in the map.
545    let Some(socket_id) = bpf_sock.get_socket_cookie() else {
546        return BpfValue::default();
547    };
548
549    let key = socket_id.as_bytes();
550
551    // SAFETY: The `map` must be a reference to a `Map` object kept alive by the program itself.
552    let map: &Map = unsafe { &*map.as_ptr::<Map>() };
553
554    // Checked by the verifier.
555    assert!(map.schema.map_type == bpf_map_type_BPF_MAP_TYPE_SK_STORAGE);
556
557    C::on_map_access(context, map);
558
559    if let Some(value_ref) = map.lookup(key) {
560        let result: BpfValue = value_ref.ptr().raw_ptr().into();
561        C::add_value_ref(context, value_ref);
562        return result;
563    }
564
565    if flags.as_u32() & BPF_SK_STORAGE_GET_F_CREATE != 0 {
566        let mut vec;
567        let init_val = if value.as_u64() == 0 {
568            vec = SmallVec::<[u8; 128]>::new();
569            vec.resize(map.schema.value_size as usize, 0);
570            (&mut vec[..]).into()
571        } else {
572            // SAFETY: The verifier ensures that `value` points to a valid buffer.
573            unsafe { EbpfBufferPtr::new(value.as_ptr::<u8>(), map.schema.value_size as usize) }
574        };
575
576        let r = map.update(key, init_val, 0);
577        if r.is_ok() {
578            if let Some(value_ref) = map.lookup(key) {
579                let result: BpfValue = value_ref.ptr().raw_ptr().into();
580                C::add_value_ref(context, value_ref);
581                return result;
582            }
583        }
584    }
585
586    BpfValue::default()
587}
588
589fn bpf_sk_fullsock<C: EbpfProgramContext>(
590    _context: &mut C::RunContext<'_>,
591    sk: BpfValue,
592    _: BpfValue,
593    _: BpfValue,
594    _: BpfValue,
595    _: BpfValue,
596) -> BpfValue {
597    sk
598}
599
600pub trait ReturnValueContext {
601    fn set_retval(&mut self, value: i32) -> i32;
602    fn get_retval(&self) -> i32;
603}
604
605pub trait ReturnValueProgramContext: EbpfProgramContext {
606    fn set_retval<'a>(context: &mut Self::RunContext<'a>, value: i32) -> i32;
607    fn get_retval<'a>(context: &mut Self::RunContext<'a>) -> i32;
608}
609
610impl<C: EbpfProgramContext> ReturnValueProgramContext for C
611where
612    for<'a> C::RunContext<'a>: ReturnValueContext,
613{
614    fn set_retval<'a>(context: &mut Self::RunContext<'a>, value: i32) -> i32 {
615        context.set_retval(value)
616    }
617    fn get_retval<'a>(context: &mut Self::RunContext<'a>) -> i32 {
618        context.get_retval()
619    }
620}
621
622fn bpf_set_retval<C: ReturnValueProgramContext>(
623    context: &mut C::RunContext<'_>,
624    value: BpfValue,
625    _: BpfValue,
626    _: BpfValue,
627    _: BpfValue,
628    _: BpfValue,
629) -> BpfValue {
630    C::set_retval(context, value.as_i32()).into()
631}
632
633fn bpf_get_retval<C: ReturnValueProgramContext>(
634    context: &mut C::RunContext<'_>,
635    _: BpfValue,
636    _: BpfValue,
637    _: BpfValue,
638    _: BpfValue,
639    _: BpfValue,
640) -> BpfValue {
641    C::get_retval(context).into()
642}
643
644fn bpf_sk_lookup_tcp<C: EbpfProgramContext>(
645    _context: &mut C::RunContext<'_>,
646    _: BpfValue,
647    _: BpfValue,
648    _: BpfValue,
649    _: BpfValue,
650    _: BpfValue,
651) -> BpfValue {
652    track_stub!(TODO("https://fxbug.dev/534355706"), "bpf_sk_lookup_tcp");
653    0.into()
654}
655
656fn bpf_sk_lookup_udp<C: EbpfProgramContext>(
657    _context: &mut C::RunContext<'_>,
658    _: BpfValue,
659    _: BpfValue,
660    _: BpfValue,
661    _: BpfValue,
662    _: BpfValue,
663) -> BpfValue {
664    track_stub!(TODO("https://fxbug.dev/534355185"), "bpf_sk_lookup_udp");
665    0.into()
666}
667
668fn bpf_sk_release<C: EbpfProgramContext>(
669    _context: &mut C::RunContext<'_>,
670    _: BpfValue,
671    _: BpfValue,
672    _: BpfValue,
673    _: BpfValue,
674    _: BpfValue,
675) -> BpfValue {
676    track_stub!(TODO("https://fxbug.dev/534355132"), "bpf_sk_release");
677    0.into()
678}
679
680fn bpf_get_netns_cookie<C: EbpfProgramContext>(
681    _context: &mut C::RunContext<'_>,
682    _: BpfValue,
683    _: BpfValue,
684    _: BpfValue,
685    _: BpfValue,
686    _: BpfValue,
687) -> BpfValue {
688    track_stub!(TODO("https://fxbug.dev/534354509"), "bpf_get_netns_cookie");
689    const DEFAULT_NETWORK_NAMESPACE_COOKIE: u64 = 1;
690    DEFAULT_NETWORK_NAMESPACE_COOKIE.into()
691}
692
693fn get_common_helpers<C: MapsProgramContext>() -> impl Iterator<Item = (u32, EbpfHelperImpl<C>)> {
694    [
695        (bpf_func_id_BPF_FUNC_ktime_get_boot_ns, EbpfHelperImpl(bpf_ktime_get_boot_ns::<C>)),
696        (bpf_func_id_BPF_FUNC_ktime_get_coarse_ns, EbpfHelperImpl(bpf_ktime_get_coarse_ns::<C>)),
697        (bpf_func_id_BPF_FUNC_ktime_get_ns, EbpfHelperImpl(bpf_ktime_get_ns::<C>)),
698        (bpf_func_id_BPF_FUNC_map_delete_elem, EbpfHelperImpl(bpf_map_delete_elem::<C>)),
699        (bpf_func_id_BPF_FUNC_map_lookup_elem, EbpfHelperImpl(bpf_map_lookup_elem::<C>)),
700        (bpf_func_id_BPF_FUNC_map_update_elem, EbpfHelperImpl(bpf_map_update_elem::<C>)),
701        (bpf_func_id_BPF_FUNC_probe_read_str, EbpfHelperImpl(bpf_probe_read_str::<C>)),
702        (bpf_func_id_BPF_FUNC_probe_read_user, EbpfHelperImpl(bpf_probe_read_user::<C>)),
703        (bpf_func_id_BPF_FUNC_probe_read_user_str, EbpfHelperImpl(bpf_probe_read_user_str::<C>)),
704        (bpf_func_id_BPF_FUNC_ringbuf_discard, EbpfHelperImpl(bpf_ringbuf_discard::<C>)),
705        (bpf_func_id_BPF_FUNC_ringbuf_reserve, EbpfHelperImpl(bpf_ringbuf_reserve::<C>)),
706        (bpf_func_id_BPF_FUNC_ringbuf_submit, EbpfHelperImpl(bpf_ringbuf_submit::<C>)),
707        (bpf_func_id_BPF_FUNC_trace_printk, EbpfHelperImpl(bpf_trace_printk::<C>)),
708        (bpf_func_id_BPF_FUNC_get_smp_processor_id, EbpfHelperImpl(bpf_get_smp_processor_id::<C>)),
709    ]
710    .into_iter()
711}
712
713/// Returns helper implementations that depend on `CurrentTask`.
714fn get_current_task_helpers<C: CurrentTaskProgramContext>()
715-> impl Iterator<Item = (u32, EbpfHelperImpl<C>)> {
716    [
717        (bpf_func_id_BPF_FUNC_get_current_uid_gid, EbpfHelperImpl(bpf_get_current_uid_gid::<C>)),
718        (bpf_func_id_BPF_FUNC_get_current_pid_tgid, EbpfHelperImpl(bpf_get_current_pid_tgid::<C>)),
719    ]
720    .into_iter()
721}
722
723// Trait for `EbpfProgramContext` implementations that are used for
724// `BPF_PROG_TYPE_CGROUP_SOCK` programs.
725pub trait CgroupSockProgramContext:
726    MapsProgramContext
727    + SocketCookieProgramContext
728    + CurrentTaskProgramContext
729    + SkStorageProgramContext
730{
731    fn get_helpers() -> HelperSet<Self> {
732        [
733            (bpf_func_id_BPF_FUNC_get_netns_cookie, EbpfHelperImpl(bpf_get_netns_cookie::<Self>)),
734            (bpf_func_id_BPF_FUNC_get_socket_cookie, EbpfHelperImpl(bpf_get_socket_cookie::<Self>)),
735            (bpf_func_id_BPF_FUNC_sk_storage_get, EbpfHelperImpl(bpf_sk_storage_get::<Self>)),
736            (bpf_func_id_BPF_FUNC_sk_lookup_tcp, EbpfHelperImpl(bpf_sk_lookup_tcp::<Self>)),
737            (bpf_func_id_BPF_FUNC_sk_lookup_udp, EbpfHelperImpl(bpf_sk_lookup_udp::<Self>)),
738            (bpf_func_id_BPF_FUNC_sk_release, EbpfHelperImpl(bpf_sk_release::<Self>)),
739        ]
740        .into_iter()
741        .chain(get_common_helpers())
742        .chain(get_current_task_helpers())
743        .collect()
744    }
745}
746
747// Trait for `EbpfProgramContext` implementations that are used for
748// `BPF_PROG_TYPE_CGROUP_SOCKADDR` programs.
749pub trait CgroupSockAddrProgramContext:
750    MapsProgramContext
751    + SocketCookieProgramContext
752    + CurrentTaskProgramContext
753    + SkStorageProgramContext
754{
755    fn get_helpers() -> HelperSet<Self> {
756        [
757            (bpf_func_id_BPF_FUNC_get_netns_cookie, EbpfHelperImpl(bpf_get_netns_cookie::<Self>)),
758            (bpf_func_id_BPF_FUNC_get_socket_cookie, EbpfHelperImpl(bpf_get_socket_cookie::<Self>)),
759            (bpf_func_id_BPF_FUNC_sk_storage_get, EbpfHelperImpl(bpf_sk_storage_get::<Self>)),
760            (bpf_func_id_BPF_FUNC_sk_lookup_tcp, EbpfHelperImpl(bpf_sk_lookup_tcp::<Self>)),
761            (bpf_func_id_BPF_FUNC_sk_lookup_udp, EbpfHelperImpl(bpf_sk_lookup_udp::<Self>)),
762            (bpf_func_id_BPF_FUNC_sk_release, EbpfHelperImpl(bpf_sk_release::<Self>)),
763        ]
764        .into_iter()
765        .chain(get_common_helpers())
766        .chain(get_current_task_helpers())
767        .collect()
768    }
769}
770
771// Trait for `EbpfProgramContext` implementations that are used for
772// `BPF_PROG_TYPE_CGROUP_SOCKOPT` programs.
773pub trait CgroupSockOptProgramContext:
774    MapsProgramContext + CurrentTaskProgramContext + ReturnValueProgramContext + SkStorageProgramContext
775{
776    fn get_helpers() -> HelperSet<Self> {
777        [
778            (bpf_func_id_BPF_FUNC_get_netns_cookie, EbpfHelperImpl(bpf_get_netns_cookie::<Self>)),
779            (bpf_func_id_BPF_FUNC_set_retval, EbpfHelperImpl(bpf_set_retval::<Self>)),
780            (bpf_func_id_BPF_FUNC_get_retval, EbpfHelperImpl(bpf_get_retval::<Self>)),
781            (bpf_func_id_BPF_FUNC_sk_storage_get, EbpfHelperImpl(bpf_sk_storage_get::<Self>)),
782            (bpf_func_id_BPF_FUNC_sk_lookup_tcp, EbpfHelperImpl(bpf_sk_lookup_tcp::<Self>)),
783            (bpf_func_id_BPF_FUNC_sk_lookup_udp, EbpfHelperImpl(bpf_sk_lookup_udp::<Self>)),
784            (bpf_func_id_BPF_FUNC_sk_release, EbpfHelperImpl(bpf_sk_release::<Self>)),
785        ]
786        .into_iter()
787        .chain(get_common_helpers())
788        .chain(get_current_task_helpers())
789        .collect()
790    }
791}
792
793// Trait for `EbpfProgramContext` implementations that are used for
794// `BPF_PROG_TYPE_SOCKET_FILTER` programs.
795pub trait SocketFilterProgramContext:
796    MapsProgramContext
797    + SocketUidProgramContext
798    + SocketCookieProgramContext
799    + SkbLoadBytesProgramContext
800{
801    fn get_helpers() -> HelperSet<Self> {
802        vec![
803            (bpf_func_id_BPF_FUNC_get_netns_cookie, EbpfHelperImpl(bpf_get_netns_cookie::<Self>)),
804            (bpf_func_id_BPF_FUNC_get_socket_uid, EbpfHelperImpl(bpf_get_socket_uid::<Self>)),
805            (bpf_func_id_BPF_FUNC_get_socket_cookie, EbpfHelperImpl(bpf_get_socket_cookie::<Self>)),
806            (bpf_func_id_BPF_FUNC_skb_load_bytes, EbpfHelperImpl(bpf_skb_load_bytes::<Self>)),
807            (
808                bpf_func_id_BPF_FUNC_skb_load_bytes_relative,
809                EbpfHelperImpl(bpf_skb_load_bytes_relative::<Self>),
810            ),
811        ]
812        .into_iter()
813        .chain(get_common_helpers())
814        .collect()
815    }
816}
817
818// Trait for `EbpfProgramContext` implementations that are used for
819// `BPF_PROG_TYPE_CGROUP_SKB` programs.
820pub trait CgroupSkbProgramContext:
821    MapsProgramContext
822    + SocketUidProgramContext
823    + SocketCookieProgramContext
824    + SkbLoadBytesProgramContext
825    + SkStorageProgramContext
826{
827    fn get_helpers() -> HelperSet<Self> {
828        vec![
829            (bpf_func_id_BPF_FUNC_get_netns_cookie, EbpfHelperImpl(bpf_get_netns_cookie::<Self>)),
830            (bpf_func_id_BPF_FUNC_get_socket_uid, EbpfHelperImpl(bpf_get_socket_uid::<Self>)),
831            (bpf_func_id_BPF_FUNC_get_socket_cookie, EbpfHelperImpl(bpf_get_socket_cookie::<Self>)),
832            (bpf_func_id_BPF_FUNC_skb_load_bytes, EbpfHelperImpl(bpf_skb_load_bytes::<Self>)),
833            (
834                bpf_func_id_BPF_FUNC_skb_load_bytes_relative,
835                EbpfHelperImpl(bpf_skb_load_bytes_relative::<Self>),
836            ),
837            (bpf_func_id_BPF_FUNC_sk_storage_get, EbpfHelperImpl(bpf_sk_storage_get::<Self>)),
838            (bpf_func_id_BPF_FUNC_sk_lookup_tcp, EbpfHelperImpl(bpf_sk_lookup_tcp::<Self>)),
839            (bpf_func_id_BPF_FUNC_sk_lookup_udp, EbpfHelperImpl(bpf_sk_lookup_udp::<Self>)),
840            (bpf_func_id_BPF_FUNC_sk_release, EbpfHelperImpl(bpf_sk_release::<Self>)),
841            (bpf_func_id_BPF_FUNC_sk_fullsock, EbpfHelperImpl(bpf_sk_fullsock::<Self>)),
842        ]
843        .into_iter()
844        .chain(get_common_helpers())
845        .collect()
846    }
847}
848
849/// Macro used to declare program type for a `EbpfProgramContext` implementation.
850/// Implements `StaticHelperSet` trait for the context type.
851///
852/// # Example
853///
854/// The following example declares that `MyEbpfProgramContext` is used to run
855/// socket filter programs:
856///
857/// ```
858/// ebpf_program_context_type!(MyEbpfProgramContext, SocketFilterProgramContext);
859/// ```
860#[macro_export]
861macro_rules! ebpf_program_context_type {
862    ($context:ty, $subtrait:ty) => {
863        impl $subtrait for $context {}
864        ebpf::static_helper_set!($context, <$context as $subtrait>::get_helpers());
865    };
866}
867
868#[cfg(test)]
869mod tests {
870    use super::*;
871    use crate::maps::{Map, PinnedMap};
872    use ebpf::{BpfValue, EbpfProgramContext, FromBpfValue, MapFlags, MapSchema};
873    use linux_uapi::{BPF_SK_STORAGE_GET_F_CREATE, bpf_map_type_BPF_MAP_TYPE_SK_STORAGE};
874
875    struct MockSocket {
876        cookie: u64,
877    }
878    impl SocketRef for MockSocket {
879        fn get_socket_cookie(&self) -> Option<u64> {
880            Some(self.cookie)
881        }
882        fn get_socket_uid(&self) -> Option<uid_t> {
883            Some(0)
884        }
885    }
886    impl<'a> FromBpfValue<TestRunContext<'a>> for MockSocket {
887        unsafe fn from_bpf_value(_context: &mut TestRunContext<'a>, value: BpfValue) -> Self {
888            Self { cookie: value.as_u64() }
889        }
890    }
891
892    struct TestRunContext<'a> {
893        map_refs: Vec<MapValueRef<'a>>,
894    }
895    impl<'a> BpfSockContext for TestRunContext<'a> {
896        type BpfSockRef = MockSocket;
897    }
898    impl<'a> MapsContext<'a> for TestRunContext<'a> {
899        fn on_map_access(&mut self, _map: &Map) {}
900        fn add_value_ref(&mut self, map_ref: MapValueRef<'a>) {
901            self.map_refs.push(map_ref);
902        }
903    }
904
905    struct TestContext;
906    impl EbpfProgramContext for TestContext {
907        type RunContext<'a> = TestRunContext<'a>;
908        type Packet<'a> = ();
909        type Arg1<'a> = ();
910        type Arg2<'a> = ();
911        type Arg3<'a> = ();
912        type Arg4<'a> = ();
913        type Arg5<'a> = ();
914        type Map = PinnedMap;
915    }
916
917    #[fuchsia::test]
918    fn test_sk_storage_get_uaf() {
919        let schema = MapSchema {
920            map_type: bpf_map_type_BPF_MAP_TYPE_SK_STORAGE,
921            key_size: 4,
922            value_size: 8,
923            max_entries: 0,
924            flags: MapFlags::NoPrealloc,
925        };
926        let map = Map::new(schema, "test").unwrap();
927        let map_value = BpfValue::from(&*map as *const Map);
928
929        let mut context = TestRunContext { map_refs: vec![] };
930
931        // 1. Create entry for socket 42
932        let sk_value1 = BpfValue::from(42u64);
933        let init_value1 = [0x11u8; 8];
934        let init_value_ptr1 = BpfValue::from(init_value1.as_ptr());
935        let flags = BpfValue::from(BPF_SK_STORAGE_GET_F_CREATE as u64);
936
937        let ptr1 = bpf_sk_storage_get::<TestContext>(
938            &mut context,
939            map_value,
940            sk_value1,
941            init_value_ptr1,
942            flags,
943            BpfValue::default(),
944        );
945        assert!(!ptr1.is_zero());
946
947        // Verify initial value
948        // SAFETY: ptr1 is a valid pointer to the map value.
949        unsafe {
950            assert_eq!(*(ptr1.as_ptr::<u64>()), 0x1111111111111111);
951        }
952
953        // 2. Delete entry for socket 42 from map
954        let key_bytes = 42u64.to_ne_bytes();
955        map.delete(&key_bytes).unwrap();
956
957        // 3. Create entry for socket 43
958        // If UAF exists, this should reuse the same memory block because it was freed.
959        let sk_value2 = BpfValue::from(43u64);
960        let init_value2 = [0x22u8; 8];
961        let init_value_ptr2 = BpfValue::from(init_value2.as_ptr());
962
963        let ptr2 = bpf_sk_storage_get::<TestContext>(
964            &mut context,
965            map_value,
966            sk_value2,
967            init_value_ptr2,
968            flags,
969            BpfValue::default(),
970        );
971        assert!(!ptr2.is_zero());
972
973        // We want to assert that the value at ptr1 has NOT changed, which means it was not reused.
974        // This assertion will FAIL without the fix (UAF occurs,
975        // ptr1's memory is overwritten with ptr2's init value),
976        // and PASS with the fix (ptr1's memory is kept alive).
977        // SAFETY: ptr1 points to memory that is kept alive by the reference in `context`.
978        unsafe {
979            assert_eq!(*(ptr1.as_ptr::<u64>()), 0x1111111111111111);
980        }
981    }
982
983    #[fuchsia::test]
984    fn test_sk_storage_get_uaf_query() {
985        let schema = MapSchema {
986            map_type: bpf_map_type_BPF_MAP_TYPE_SK_STORAGE,
987            key_size: 4,
988            value_size: 8,
989            max_entries: 0,
990            flags: MapFlags::NoPrealloc,
991        };
992        let map = Map::new(schema, "test").unwrap();
993        let map_value = BpfValue::from(&*map as *const Map);
994
995        let mut context = TestRunContext { map_refs: vec![] };
996
997        // 1. Create entry for socket 42
998        let sk_value1 = BpfValue::from(42u64);
999        let init_value1 = [0x11u8; 8];
1000        let init_value_ptr1 = BpfValue::from(init_value1.as_ptr());
1001        let flags = BpfValue::from(BPF_SK_STORAGE_GET_F_CREATE as u64);
1002
1003        let ptr1 = bpf_sk_storage_get::<TestContext>(
1004            &mut context,
1005            map_value,
1006            sk_value1,
1007            init_value_ptr1,
1008            flags,
1009            BpfValue::default(),
1010        );
1011        assert!(!ptr1.is_zero());
1012
1013        // Clear context to simulate that we don't hold the creation
1014        // reference anymore. The map still holds the reference.
1015        context.map_refs.clear();
1016
1017        // 2. Query entry for socket 42 (without CREATE flag)
1018        let ptr1_query = bpf_sk_storage_get::<TestContext>(
1019            &mut context,
1020            map_value,
1021            sk_value1,
1022            BpfValue::default(),
1023            BpfValue::default(),
1024            BpfValue::default(),
1025        );
1026        assert_eq!(ptr1.as_u64(), ptr1_query.as_u64());
1027
1028        // 3. Delete entry for socket 42 from map
1029        let key_bytes = 42u64.to_ne_bytes();
1030        map.delete(&key_bytes).unwrap();
1031
1032        // 4. Create entry for socket 43
1033        // If UAF exists, this should reuse the same memory block
1034        // because it was freed.
1035        let sk_value2 = BpfValue::from(43u64);
1036        let init_value2 = [0x22u8; 8];
1037        let init_value_ptr2 = BpfValue::from(init_value2.as_ptr());
1038
1039        let ptr2 = bpf_sk_storage_get::<TestContext>(
1040            &mut context,
1041            map_value,
1042            sk_value2,
1043            init_value_ptr2,
1044            flags,
1045            BpfValue::default(),
1046        );
1047        assert!(!ptr2.is_zero());
1048
1049        // We want to assert that the value at ptr1_query has NOT
1050        // changed.
1051        // SAFETY: ptr1_query points to memory that is kept alive by
1052        // the reference in `context` (from the query).
1053        unsafe {
1054            assert_eq!(*(ptr1_query.as_ptr::<u64>()), 0x1111111111111111);
1055        }
1056    }
1057
1058    /// Regression test: the verifier models the probe_read_user{,_str}
1059    /// destination as written (`output: true`), but the runtime helpers are
1060    /// stubs. They must zero the destination so a program cannot read back
1061    /// uninitialized executor memory. Without the fix the sentinel bytes below
1062    /// survive the call.
1063    ///
1064    /// TODO("https://fxbug.dev/534354547","https://fxbug.dev/534355539"): Replace
1065    /// these tests when the real helpers are implemented.
1066    #[fuchsia::test]
1067    fn test_probe_read_user_zeroes_destination() {
1068        let mut context = TestRunContext { map_refs: vec![] };
1069
1070        // Stand in for stale executor-stack memory with a non-zero sentinel.
1071        let mut dst = [0xAAu8; 8];
1072        let ret = bpf_probe_read_user::<TestContext>(
1073            &mut context,
1074            BpfValue::from(dst.as_mut_ptr()),
1075            BpfValue::from(dst.len() as u64),
1076            BpfValue::default(),
1077            BpfValue::default(),
1078            BpfValue::default(),
1079        );
1080        assert_eq!(ret.as_u64(), 0);
1081        assert_eq!(dst, [0u8; 8], "probe_read_user left the destination uninitialized");
1082
1083        let mut dst_str = [0xBBu8; 8];
1084        let _ = bpf_probe_read_user_str::<TestContext>(
1085            &mut context,
1086            BpfValue::from(dst_str.as_mut_ptr()),
1087            BpfValue::from(dst_str.len() as u64),
1088            BpfValue::default(),
1089            BpfValue::default(),
1090            BpfValue::default(),
1091        );
1092        assert_eq!(dst_str, [0u8; 8], "probe_read_user_str left the destination uninitialized");
1093    }
1094}