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