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    sk: BpfValue,
586    _: BpfValue,
587    _: BpfValue,
588    _: BpfValue,
589    _: BpfValue,
590) -> BpfValue {
591    sk
592}
593
594pub trait ReturnValueContext {
595    fn set_retval(&mut self, value: i32) -> i32;
596    fn get_retval(&self) -> i32;
597}
598
599pub trait ReturnValueProgramContext: EbpfProgramContext {
600    fn set_retval<'a>(context: &mut Self::RunContext<'a>, value: i32) -> i32;
601    fn get_retval<'a>(context: &mut Self::RunContext<'a>) -> i32;
602}
603
604impl<C: EbpfProgramContext> ReturnValueProgramContext for C
605where
606    for<'a> C::RunContext<'a>: ReturnValueContext,
607{
608    fn set_retval<'a>(context: &mut Self::RunContext<'a>, value: i32) -> i32 {
609        context.set_retval(value)
610    }
611    fn get_retval<'a>(context: &mut Self::RunContext<'a>) -> i32 {
612        context.get_retval()
613    }
614}
615
616fn bpf_set_retval<C: ReturnValueProgramContext>(
617    context: &mut C::RunContext<'_>,
618    value: BpfValue,
619    _: BpfValue,
620    _: BpfValue,
621    _: BpfValue,
622    _: BpfValue,
623) -> BpfValue {
624    C::set_retval(context, value.as_i32()).into()
625}
626
627fn bpf_get_retval<C: ReturnValueProgramContext>(
628    context: &mut C::RunContext<'_>,
629    _: BpfValue,
630    _: BpfValue,
631    _: BpfValue,
632    _: BpfValue,
633    _: BpfValue,
634) -> BpfValue {
635    C::get_retval(context).into()
636}
637
638fn bpf_sk_lookup_tcp<C: EbpfProgramContext>(
639    _context: &mut C::RunContext<'_>,
640    _: BpfValue,
641    _: BpfValue,
642    _: BpfValue,
643    _: BpfValue,
644    _: BpfValue,
645) -> BpfValue {
646    track_stub!(TODO("https://fxbug.dev/534355706"), "bpf_sk_lookup_tcp");
647    0.into()
648}
649
650fn bpf_sk_lookup_udp<C: EbpfProgramContext>(
651    _context: &mut C::RunContext<'_>,
652    _: BpfValue,
653    _: BpfValue,
654    _: BpfValue,
655    _: BpfValue,
656    _: BpfValue,
657) -> BpfValue {
658    track_stub!(TODO("https://fxbug.dev/534355185"), "bpf_sk_lookup_udp");
659    0.into()
660}
661
662fn bpf_sk_release<C: EbpfProgramContext>(
663    _context: &mut C::RunContext<'_>,
664    _: BpfValue,
665    _: BpfValue,
666    _: BpfValue,
667    _: BpfValue,
668    _: BpfValue,
669) -> BpfValue {
670    track_stub!(TODO("https://fxbug.dev/534355132"), "bpf_sk_release");
671    0.into()
672}
673
674fn bpf_get_netns_cookie<C: EbpfProgramContext>(
675    _context: &mut C::RunContext<'_>,
676    _: BpfValue,
677    _: BpfValue,
678    _: BpfValue,
679    _: BpfValue,
680    _: BpfValue,
681) -> BpfValue {
682    track_stub!(TODO("https://fxbug.dev/534354509"), "bpf_get_netns_cookie");
683    const DEFAULT_NETWORK_NAMESPACE_COOKIE: u64 = 1;
684    DEFAULT_NETWORK_NAMESPACE_COOKIE.into()
685}
686
687fn get_common_helpers<C: MapsProgramContext>() -> impl Iterator<Item = (u32, EbpfHelperImpl<C>)> {
688    [
689        (bpf_func_id_BPF_FUNC_ktime_get_boot_ns, EbpfHelperImpl(bpf_ktime_get_boot_ns::<C>)),
690        (bpf_func_id_BPF_FUNC_ktime_get_coarse_ns, EbpfHelperImpl(bpf_ktime_get_coarse_ns::<C>)),
691        (bpf_func_id_BPF_FUNC_ktime_get_ns, EbpfHelperImpl(bpf_ktime_get_ns::<C>)),
692        (bpf_func_id_BPF_FUNC_map_delete_elem, EbpfHelperImpl(bpf_map_delete_elem::<C>)),
693        (bpf_func_id_BPF_FUNC_map_lookup_elem, EbpfHelperImpl(bpf_map_lookup_elem::<C>)),
694        (bpf_func_id_BPF_FUNC_map_update_elem, EbpfHelperImpl(bpf_map_update_elem::<C>)),
695        (bpf_func_id_BPF_FUNC_probe_read_str, EbpfHelperImpl(bpf_probe_read_str::<C>)),
696        (bpf_func_id_BPF_FUNC_probe_read_user, EbpfHelperImpl(bpf_probe_read_user::<C>)),
697        (bpf_func_id_BPF_FUNC_probe_read_user_str, EbpfHelperImpl(bpf_probe_read_user_str::<C>)),
698        (bpf_func_id_BPF_FUNC_ringbuf_discard, EbpfHelperImpl(bpf_ringbuf_discard::<C>)),
699        (bpf_func_id_BPF_FUNC_ringbuf_reserve, EbpfHelperImpl(bpf_ringbuf_reserve::<C>)),
700        (bpf_func_id_BPF_FUNC_ringbuf_submit, EbpfHelperImpl(bpf_ringbuf_submit::<C>)),
701        (bpf_func_id_BPF_FUNC_trace_printk, EbpfHelperImpl(bpf_trace_printk::<C>)),
702        (bpf_func_id_BPF_FUNC_get_smp_processor_id, EbpfHelperImpl(bpf_get_smp_processor_id::<C>)),
703    ]
704    .into_iter()
705}
706
707/// Returns helper implementations that depend on `CurrentTask`.
708fn get_current_task_helpers<C: CurrentTaskProgramContext>()
709-> impl Iterator<Item = (u32, EbpfHelperImpl<C>)> {
710    [
711        (bpf_func_id_BPF_FUNC_get_current_uid_gid, EbpfHelperImpl(bpf_get_current_uid_gid::<C>)),
712        (bpf_func_id_BPF_FUNC_get_current_pid_tgid, EbpfHelperImpl(bpf_get_current_pid_tgid::<C>)),
713    ]
714    .into_iter()
715}
716
717// Trait for `EbpfProgramContext` implementations that are used for
718// `BPF_PROG_TYPE_CGROUP_SOCK` programs.
719pub trait CgroupSockProgramContext:
720    MapsProgramContext
721    + SocketCookieProgramContext
722    + CurrentTaskProgramContext
723    + SkStorageProgramContext
724{
725    fn get_helpers() -> HelperSet<Self> {
726        [
727            (bpf_func_id_BPF_FUNC_get_netns_cookie, EbpfHelperImpl(bpf_get_netns_cookie::<Self>)),
728            (bpf_func_id_BPF_FUNC_get_socket_cookie, EbpfHelperImpl(bpf_get_socket_cookie::<Self>)),
729            (bpf_func_id_BPF_FUNC_sk_storage_get, EbpfHelperImpl(bpf_sk_storage_get::<Self>)),
730            (bpf_func_id_BPF_FUNC_sk_lookup_tcp, EbpfHelperImpl(bpf_sk_lookup_tcp::<Self>)),
731            (bpf_func_id_BPF_FUNC_sk_lookup_udp, EbpfHelperImpl(bpf_sk_lookup_udp::<Self>)),
732            (bpf_func_id_BPF_FUNC_sk_release, EbpfHelperImpl(bpf_sk_release::<Self>)),
733        ]
734        .into_iter()
735        .chain(get_common_helpers())
736        .chain(get_current_task_helpers())
737        .collect()
738    }
739}
740
741// Trait for `EbpfProgramContext` implementations that are used for
742// `BPF_PROG_TYPE_CGROUP_SOCKADDR` programs.
743pub trait CgroupSockAddrProgramContext:
744    MapsProgramContext
745    + SocketCookieProgramContext
746    + CurrentTaskProgramContext
747    + SkStorageProgramContext
748{
749    fn get_helpers() -> HelperSet<Self> {
750        [
751            (bpf_func_id_BPF_FUNC_get_netns_cookie, EbpfHelperImpl(bpf_get_netns_cookie::<Self>)),
752            (bpf_func_id_BPF_FUNC_get_socket_cookie, EbpfHelperImpl(bpf_get_socket_cookie::<Self>)),
753            (bpf_func_id_BPF_FUNC_sk_storage_get, EbpfHelperImpl(bpf_sk_storage_get::<Self>)),
754            (bpf_func_id_BPF_FUNC_sk_lookup_tcp, EbpfHelperImpl(bpf_sk_lookup_tcp::<Self>)),
755            (bpf_func_id_BPF_FUNC_sk_lookup_udp, EbpfHelperImpl(bpf_sk_lookup_udp::<Self>)),
756            (bpf_func_id_BPF_FUNC_sk_release, EbpfHelperImpl(bpf_sk_release::<Self>)),
757        ]
758        .into_iter()
759        .chain(get_common_helpers())
760        .chain(get_current_task_helpers())
761        .collect()
762    }
763}
764
765// Trait for `EbpfProgramContext` implementations that are used for
766// `BPF_PROG_TYPE_CGROUP_SOCKOPT` programs.
767pub trait CgroupSockOptProgramContext:
768    MapsProgramContext + CurrentTaskProgramContext + ReturnValueProgramContext + SkStorageProgramContext
769{
770    fn get_helpers() -> HelperSet<Self> {
771        [
772            (bpf_func_id_BPF_FUNC_get_netns_cookie, EbpfHelperImpl(bpf_get_netns_cookie::<Self>)),
773            (bpf_func_id_BPF_FUNC_set_retval, EbpfHelperImpl(bpf_set_retval::<Self>)),
774            (bpf_func_id_BPF_FUNC_get_retval, EbpfHelperImpl(bpf_get_retval::<Self>)),
775            (bpf_func_id_BPF_FUNC_sk_storage_get, EbpfHelperImpl(bpf_sk_storage_get::<Self>)),
776            (bpf_func_id_BPF_FUNC_sk_lookup_tcp, EbpfHelperImpl(bpf_sk_lookup_tcp::<Self>)),
777            (bpf_func_id_BPF_FUNC_sk_lookup_udp, EbpfHelperImpl(bpf_sk_lookup_udp::<Self>)),
778            (bpf_func_id_BPF_FUNC_sk_release, EbpfHelperImpl(bpf_sk_release::<Self>)),
779        ]
780        .into_iter()
781        .chain(get_common_helpers())
782        .chain(get_current_task_helpers())
783        .collect()
784    }
785}
786
787// Trait for `EbpfProgramContext` implementations that are used for
788// `BPF_PROG_TYPE_SOCKET_FILTER` programs.
789pub trait SocketFilterProgramContext:
790    MapsProgramContext
791    + SocketUidProgramContext
792    + SocketCookieProgramContext
793    + SkbLoadBytesProgramContext
794{
795    fn get_helpers() -> HelperSet<Self> {
796        vec![
797            (bpf_func_id_BPF_FUNC_get_netns_cookie, EbpfHelperImpl(bpf_get_netns_cookie::<Self>)),
798            (bpf_func_id_BPF_FUNC_get_socket_uid, EbpfHelperImpl(bpf_get_socket_uid::<Self>)),
799            (bpf_func_id_BPF_FUNC_get_socket_cookie, EbpfHelperImpl(bpf_get_socket_cookie::<Self>)),
800            (bpf_func_id_BPF_FUNC_skb_load_bytes, EbpfHelperImpl(bpf_skb_load_bytes::<Self>)),
801            (
802                bpf_func_id_BPF_FUNC_skb_load_bytes_relative,
803                EbpfHelperImpl(bpf_skb_load_bytes_relative::<Self>),
804            ),
805        ]
806        .into_iter()
807        .chain(get_common_helpers())
808        .collect()
809    }
810}
811
812// Trait for `EbpfProgramContext` implementations that are used for
813// `BPF_PROG_TYPE_CGROUP_SKB` programs.
814pub trait CgroupSkbProgramContext:
815    MapsProgramContext
816    + SocketUidProgramContext
817    + SocketCookieProgramContext
818    + SkbLoadBytesProgramContext
819    + SkStorageProgramContext
820{
821    fn get_helpers() -> HelperSet<Self> {
822        vec![
823            (bpf_func_id_BPF_FUNC_get_netns_cookie, EbpfHelperImpl(bpf_get_netns_cookie::<Self>)),
824            (bpf_func_id_BPF_FUNC_get_socket_uid, EbpfHelperImpl(bpf_get_socket_uid::<Self>)),
825            (bpf_func_id_BPF_FUNC_get_socket_cookie, EbpfHelperImpl(bpf_get_socket_cookie::<Self>)),
826            (bpf_func_id_BPF_FUNC_skb_load_bytes, EbpfHelperImpl(bpf_skb_load_bytes::<Self>)),
827            (
828                bpf_func_id_BPF_FUNC_skb_load_bytes_relative,
829                EbpfHelperImpl(bpf_skb_load_bytes_relative::<Self>),
830            ),
831            (bpf_func_id_BPF_FUNC_sk_storage_get, EbpfHelperImpl(bpf_sk_storage_get::<Self>)),
832            (bpf_func_id_BPF_FUNC_sk_lookup_tcp, EbpfHelperImpl(bpf_sk_lookup_tcp::<Self>)),
833            (bpf_func_id_BPF_FUNC_sk_lookup_udp, EbpfHelperImpl(bpf_sk_lookup_udp::<Self>)),
834            (bpf_func_id_BPF_FUNC_sk_release, EbpfHelperImpl(bpf_sk_release::<Self>)),
835            (bpf_func_id_BPF_FUNC_sk_fullsock, EbpfHelperImpl(bpf_sk_fullsock::<Self>)),
836        ]
837        .into_iter()
838        .chain(get_common_helpers())
839        .collect()
840    }
841}
842
843/// Macro used to declare program type for a `EbpfProgramContext` implementation.
844/// Implements `StaticHelperSet` trait for the context type.
845///
846/// # Example
847///
848/// The following example declares that `MyEbpfProgramContext` is used to run
849/// socket filter programs:
850///
851/// ```
852/// ebpf_program_context_type!(MyEbpfProgramContext, SocketFilterProgramContext);
853/// ```
854#[macro_export]
855macro_rules! ebpf_program_context_type {
856    ($context:ty, $subtrait:ty) => {
857        impl $subtrait for $context {}
858        ebpf::static_helper_set!($context, <$context as $subtrait>::get_helpers());
859    };
860}
861
862#[cfg(test)]
863mod tests {
864    use super::*;
865    use crate::maps::{Map, PinnedMap};
866    use ebpf::{BpfValue, EbpfProgramContext, FromBpfValue, MapFlags, MapSchema};
867    use linux_uapi::{BPF_SK_STORAGE_GET_F_CREATE, bpf_map_type_BPF_MAP_TYPE_SK_STORAGE};
868
869    struct MockSocket {
870        cookie: u64,
871    }
872    impl SocketRef for MockSocket {
873        fn get_socket_cookie(&self) -> Option<u64> {
874            Some(self.cookie)
875        }
876        fn get_socket_uid(&self) -> Option<uid_t> {
877            Some(0)
878        }
879    }
880    impl<'a> FromBpfValue<TestRunContext<'a>> for MockSocket {
881        unsafe fn from_bpf_value(_context: &mut TestRunContext<'a>, value: BpfValue) -> Self {
882            Self { cookie: value.as_u64() }
883        }
884    }
885
886    struct TestRunContext<'a> {
887        map_refs: Vec<MapValueRef<'a>>,
888    }
889    impl<'a> BpfSockContext for TestRunContext<'a> {
890        type BpfSockRef = MockSocket;
891    }
892    impl<'a> MapsContext<'a> for TestRunContext<'a> {
893        fn on_map_access(&mut self, _map: &Map) {}
894        fn add_value_ref(&mut self, map_ref: MapValueRef<'a>) {
895            self.map_refs.push(map_ref);
896        }
897    }
898
899    struct TestContext;
900    impl EbpfProgramContext for TestContext {
901        type RunContext<'a> = TestRunContext<'a>;
902        type Packet<'a> = ();
903        type Arg1<'a> = ();
904        type Arg2<'a> = ();
905        type Arg3<'a> = ();
906        type Arg4<'a> = ();
907        type Arg5<'a> = ();
908        type Map = PinnedMap;
909    }
910
911    #[fuchsia::test]
912    fn test_sk_storage_get_uaf() {
913        let schema = MapSchema {
914            map_type: bpf_map_type_BPF_MAP_TYPE_SK_STORAGE,
915            key_size: 4,
916            value_size: 8,
917            max_entries: 0,
918            flags: MapFlags::NoPrealloc,
919        };
920        let map = Map::new(schema, "test").unwrap();
921        let map_value = BpfValue::from(&*map as *const Map);
922
923        let mut context = TestRunContext { map_refs: vec![] };
924
925        // 1. Create entry for socket 42
926        let sk_value1 = BpfValue::from(42u64);
927        let init_value1 = [0x11u8; 8];
928        let init_value_ptr1 = BpfValue::from(init_value1.as_ptr());
929        let flags = BpfValue::from(BPF_SK_STORAGE_GET_F_CREATE as u64);
930
931        let ptr1 = bpf_sk_storage_get::<TestContext>(
932            &mut context,
933            map_value,
934            sk_value1,
935            init_value_ptr1,
936            flags,
937            BpfValue::default(),
938        );
939        assert!(!ptr1.is_zero());
940
941        // Verify initial value
942        // SAFETY: ptr1 is a valid pointer to the map value.
943        unsafe {
944            assert_eq!(*(ptr1.as_ptr::<u64>()), 0x1111111111111111);
945        }
946
947        // 2. Delete entry for socket 42 from map
948        let key_bytes = 42u64.to_ne_bytes();
949        map.delete(&key_bytes).unwrap();
950
951        // 3. Create entry for socket 43
952        // If UAF exists, this should reuse the same memory block because it was freed.
953        let sk_value2 = BpfValue::from(43u64);
954        let init_value2 = [0x22u8; 8];
955        let init_value_ptr2 = BpfValue::from(init_value2.as_ptr());
956
957        let ptr2 = bpf_sk_storage_get::<TestContext>(
958            &mut context,
959            map_value,
960            sk_value2,
961            init_value_ptr2,
962            flags,
963            BpfValue::default(),
964        );
965        assert!(!ptr2.is_zero());
966
967        // We want to assert that the value at ptr1 has NOT changed, which means it was not reused.
968        // This assertion will FAIL without the fix (UAF occurs,
969        // ptr1's memory is overwritten with ptr2's init value),
970        // and PASS with the fix (ptr1's memory is kept alive).
971        // SAFETY: ptr1 points to memory that is kept alive by the reference in `context`.
972        unsafe {
973            assert_eq!(*(ptr1.as_ptr::<u64>()), 0x1111111111111111);
974        }
975    }
976
977    #[fuchsia::test]
978    fn test_sk_storage_get_uaf_query() {
979        let schema = MapSchema {
980            map_type: bpf_map_type_BPF_MAP_TYPE_SK_STORAGE,
981            key_size: 4,
982            value_size: 8,
983            max_entries: 0,
984            flags: MapFlags::NoPrealloc,
985        };
986        let map = Map::new(schema, "test").unwrap();
987        let map_value = BpfValue::from(&*map as *const Map);
988
989        let mut context = TestRunContext { map_refs: vec![] };
990
991        // 1. Create entry for socket 42
992        let sk_value1 = BpfValue::from(42u64);
993        let init_value1 = [0x11u8; 8];
994        let init_value_ptr1 = BpfValue::from(init_value1.as_ptr());
995        let flags = BpfValue::from(BPF_SK_STORAGE_GET_F_CREATE as u64);
996
997        let ptr1 = bpf_sk_storage_get::<TestContext>(
998            &mut context,
999            map_value,
1000            sk_value1,
1001            init_value_ptr1,
1002            flags,
1003            BpfValue::default(),
1004        );
1005        assert!(!ptr1.is_zero());
1006
1007        // Clear context to simulate that we don't hold the creation
1008        // reference anymore. The map still holds the reference.
1009        context.map_refs.clear();
1010
1011        // 2. Query entry for socket 42 (without CREATE flag)
1012        let ptr1_query = bpf_sk_storage_get::<TestContext>(
1013            &mut context,
1014            map_value,
1015            sk_value1,
1016            BpfValue::default(),
1017            BpfValue::default(),
1018            BpfValue::default(),
1019        );
1020        assert_eq!(ptr1.as_u64(), ptr1_query.as_u64());
1021
1022        // 3. Delete entry for socket 42 from map
1023        let key_bytes = 42u64.to_ne_bytes();
1024        map.delete(&key_bytes).unwrap();
1025
1026        // 4. Create entry for socket 43
1027        // If UAF exists, this should reuse the same memory block
1028        // because it was freed.
1029        let sk_value2 = BpfValue::from(43u64);
1030        let init_value2 = [0x22u8; 8];
1031        let init_value_ptr2 = BpfValue::from(init_value2.as_ptr());
1032
1033        let ptr2 = bpf_sk_storage_get::<TestContext>(
1034            &mut context,
1035            map_value,
1036            sk_value2,
1037            init_value_ptr2,
1038            flags,
1039            BpfValue::default(),
1040        );
1041        assert!(!ptr2.is_zero());
1042
1043        // We want to assert that the value at ptr1_query has NOT
1044        // changed.
1045        // SAFETY: ptr1_query points to memory that is kept alive by
1046        // the reference in `context` (from the query).
1047        unsafe {
1048            assert_eq!(*(ptr1_query.as_ptr::<u64>()), 0x1111111111111111);
1049        }
1050    }
1051
1052    /// Regression test: the verifier models the probe_read_user{,_str}
1053    /// destination as written (`output: true`), but the runtime helpers are
1054    /// stubs. They must zero the destination so a program cannot read back
1055    /// uninitialized executor memory. Without the fix the sentinel bytes below
1056    /// survive the call.
1057    ///
1058    /// TODO("https://fxbug.dev/534354547","https://fxbug.dev/534355539"): Replace
1059    /// these tests when the real helpers are implemented.
1060    #[fuchsia::test]
1061    fn test_probe_read_user_zeroes_destination() {
1062        let mut context = TestRunContext { map_refs: vec![] };
1063
1064        // Stand in for stale executor-stack memory with a non-zero sentinel.
1065        let mut dst = [0xAAu8; 8];
1066        let ret = bpf_probe_read_user::<TestContext>(
1067            &mut context,
1068            BpfValue::from(dst.as_mut_ptr()),
1069            BpfValue::from(dst.len() as u64),
1070            BpfValue::default(),
1071            BpfValue::default(),
1072            BpfValue::default(),
1073        );
1074        assert_eq!(ret.as_u64(), 0);
1075        assert_eq!(dst, [0u8; 8], "probe_read_user left the destination uninitialized");
1076
1077        let mut dst_str = [0xBBu8; 8];
1078        let _ = bpf_probe_read_user_str::<TestContext>(
1079            &mut context,
1080            BpfValue::from(dst_str.as_mut_ptr()),
1081            BpfValue::from(dst_str.len() as u64),
1082            BpfValue::default(),
1083            BpfValue::default(),
1084            BpfValue::default(),
1085        );
1086        assert_eq!(dst_str, [0u8; 8], "probe_read_user_str left the destination uninitialized");
1087    }
1088}