Skip to content

Commit

Permalink
libbpf/ebpf: Add pages for global function attributes
Browse files Browse the repository at this point in the history
This commits adds pages for the global function attributes that are
currently supported by the eBPF library.

Signed-off-by: Dylan Reimerink <[email protected]>
  • Loading branch information
dylandreimerink committed Nov 17, 2024
1 parent 987e61d commit f738cba
Show file tree
Hide file tree
Showing 7 changed files with 312 additions and 5 deletions.
6 changes: 6 additions & 0 deletions docs/ebpf-library/libbpf/ebpf/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
- [`__kptr_untrusted`](__kptr_untrusted.md)
- [`__kptr`](__kptr.md)
- [`__percpu_kptr`](__percpu_kptr.md)
- Global function attributes
- [`__arg_ctx`](__arg_ctx.md)
- [`__arg_nonnull`](__arg_nonnull.md)
- [`__arg_nullable`](__arg_nullable.md)
- [`__arg_trusted`](__arg_trusted.md)
- [`__arg_arena`](__arg_arena.md)
- [`SEC`](SEC.md)
- [`KERNEL_VERSION`](KERNEL_VERSION.md)
- [`offsetof`](offsetof.md)
Expand Down
122 changes: 122 additions & 0 deletions docs/ebpf-library/libbpf/ebpf/__arg_arena.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
---
title: "Libbpf eBPF macro '__arg_arena'"
description: "This page documents the '__arg_arena' libbpf eBPF macro, including its definition, usage, and examples."
---
# Libbpf eBPF macro `__arg_arena`

[:octicons-tag-24: v1.4.0](https://github.com/libbpf/libbpf/releases/tag/v1.4.0)

The `__arg_arena` macros is used to tag a function argument to tell the verifier that a value lives on an arena.

## Definition

`#!c #define __arg_arena __attribute((btf_decl_tag("arg:arena")))`

## Usage

This macro can be used to tag a function argument of a [global function](../../../linux/concepts/functions.md#function-by-function-verification) to tell the verifier that its value lives on an arena. Since global functions are verifier out of order, the verifier has implicit way to track this property over global function boundaries. The verifier will enforce at the call site that an actual arena value is passed to the function.

### Example

```c hl_lines="56 69"
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
#pragma once
#include <errno.h>
#include "bpf_arena_alloc.h"
#include "bpf_arena_list.h"

struct htab_bucket {
struct arena_list_head head;
};
typedef struct htab_bucket __arena htab_bucket_t;

struct htab {
htab_bucket_t *buckets;
int n_buckets;
};
typedef struct htab __arena htab_t;

static inline htab_bucket_t *__select_bucket(htab_t *htab, __u32 hash)
{
htab_bucket_t *b = htab->buckets;

cast_kern(b);
return &b[hash & (htab->n_buckets - 1)];
}

static inline arena_list_head_t *select_bucket(htab_t *htab, __u32 hash)
{
return &__select_bucket(htab, hash)->head;
}

struct hashtab_elem {
int hash;
int key;
int value;
struct arena_list_node hash_node;
};
typedef struct hashtab_elem __arena hashtab_elem_t;

static hashtab_elem_t *lookup_elem_raw(arena_list_head_t *head, __u32 hash, int key)
{
hashtab_elem_t *l;

list_for_each_entry(l, head, hash_node)
if (l->hash == hash && l->key == key)
return l;

return NULL;
}

static int htab_hash(int key)
{
return key;
}

__weak int htab_lookup_elem(htab_t *htab __arg_arena, int key)
{
hashtab_elem_t *l_old;
arena_list_head_t *head;

cast_kern(htab);
head = select_bucket(htab, key);
l_old = lookup_elem_raw(head, htab_hash(key), key);
if (l_old)
return l_old->value;
return 0;
}

__weak int htab_update_elem(htab_t *htab __arg_arena, int key, int value)
{
hashtab_elem_t *l_new = NULL, *l_old;
arena_list_head_t *head;

cast_kern(htab);
head = select_bucket(htab, key);
l_old = lookup_elem_raw(head, htab_hash(key), key);

l_new = bpf_alloc(sizeof(*l_new));
if (!l_new)
return -ENOMEM;
l_new->key = key;
l_new->hash = htab_hash(key);
l_new->value = value;

list_add_head(&l_new->hash_node, head);
if (l_old) {
list_del(&l_old->hash_node);
bpf_free(l_old);
}
return 0;
}

void htab_init(htab_t *htab)
{
void __arena *buckets = bpf_arena_alloc_pages(&arena, NULL, 2, NUMA_NO_NODE, 0);

cast_user(buckets);
htab->buckets = buckets;
htab->n_buckets = 2 * PAGE_SIZE / sizeof(struct htab_bucket);
}
```
50 changes: 50 additions & 0 deletions docs/ebpf-library/libbpf/ebpf/__arg_ctx.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
title: "Libbpf eBPF macro '__arg_ctx'"
description: "This page documents the '__arg_ctx' libbpf eBPF macro, including its definition, usage, and examples."
---
# Libbpf eBPF macro `__arg_ctx`

[:octicons-tag-24: v1.4.0](https://github.com/libbpf/libbpf/releases/tag/v1.4.0)

The `__arg_ctx` macros is used to tag a function argument to tell the verifier that it is a program context.

## Definition

`#!c #define __arg_ctx __attribute__((btf_decl_tag("arg:ctx")))`

## Usage

This macro can be used to tag a function argument of a [global function](../../../linux/concepts/functions.md#function-by-function-verification) if you want to write that function in such a way that it can be re-used between different program types. Global functions are verified function-by-function, so the function can be verifier before any of its callers. The verifier therefore has to use type info to determine possible values. The verifier will already implicitly associate types such as `struct __sk_buff*` and `struct xdp_md*` with the program context and assert only the actual, valid context is passed. However, a function that can work with multiple program contexts needs to use `void *` to be able to compile, which means the verifier is missing type info. When this becomes an issue you can add the `__arg_ctx` macro to the function argument to tell the verifier that the argument is a program context. The verifier will treat the argument as a program context for all intents and purposes and it will enforce a valid context is passed on the call site.

### Example

```c hl_lines="7"
struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
__uint(key_size, sizeof(__u32));
__uint(value_size, sizeof(int));
} events SEC(".maps");

int send_msg(void *ctx __arg_ctx)
{
char msg[] = "Some common message";
return bpf_perf_event_output(ctx, &events, 0, msg, sizeof(msg));
}

SEC("kprobe/eth_type_trans")
int kprobe__sys_open(struct pt_regs *ctx)
{
send_msg(ctx);
return 0;
}

SEC("fentry/eth_type_trans")
int BPF_PROG(fentry_eth_type_trans, struct sk_buff *skb,
struct net_device *dev, unsigned short protocol)
{
// Note: The BPF_PROG does some magic to give us typed arguments, but `ctx` is still preserved as the
// context passed into the program.
send_msg(ctx);
return 0;
}
```
38 changes: 38 additions & 0 deletions docs/ebpf-library/libbpf/ebpf/__arg_nonnull.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
title: "Libbpf eBPF macro '__arg_nonnull'"
description: "This page documents the '__arg_nonnull' libbpf eBPF macro, including its definition, usage, and examples."
---
# Libbpf eBPF macro `__arg_nonnull`

[:octicons-tag-24: v1.4.0](https://github.com/libbpf/libbpf/releases/tag/v1.4.0)

The `__arg_nonnull` macros is used to tag a function argument to tell the verifier that its value may never be null.

## Definition

`#!c #define __arg_nonnull __attribute((btf_decl_tag("arg:nonnull")))`

## Usage

This macro can be used to tag a function argument of a [global function](../../../linux/concepts/functions.md#function-by-function-verification) to tell the verifier that it can assume the argument is never null, and to enforce this on the call site. Since global functions can be verifier out of order, the verifier will always assume that a pointer argument may contain a `NULL` value and will force you to implement a check for this. As program author you may know that you always do a `NULL` check on all of your callsites. In that case you can add the `__arg_nonnull` attribute to the function argument, the verifier will assume the argument is never `NULL` while verifying the function and will not enforce that any pointer passed into the function can not be `NULL`.

### Example

Note how the first function is not `static`, thus global. The `__noinline __weak` attributes are added to force the compiler to emit a separate function instead of inlining it. That is not needed in actual usage.

```c hl_lines="1"
__noinline __weak int subprog_nonnull_ptr_good(int *p1 __arg_nonnull, int *p2 __arg_nonnull)
{
return (*p1) * (*p2); /* good, no need for NULL checks */
}

int x = 47;

SEC("?raw_tp")
int arg_tag_nonnull_ptr_good(void *ctx)
{
int y = 74;

return subprog_nonnull_ptr_good(&x, &y);
}
```
55 changes: 55 additions & 0 deletions docs/ebpf-library/libbpf/ebpf/__arg_nullable.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
title: "Libbpf eBPF macro '__arg_nullable'"
description: "This page documents the '__arg_nullable' libbpf eBPF macro, including its definition, usage, and examples."
---
# Libbpf eBPF macro `__arg_nullable`

[:octicons-tag-24: v1.4.0](https://github.com/libbpf/libbpf/releases/tag/v1.4.0)

The `__arg_nullable` macros is used to tag a function argument to tell the verifier that its value may be null.

## Definition

`#!c #define __arg_nullable __attribute((btf_decl_tag("arg:nullable")))`

## Usage

This macro can be used to tag a function argument of a [global function](../../../linux/concepts/functions.md#function-by-function-verification) to tell the verifier that it can assume the argument can be `NULL`. It was introduced alongside the [`__arg_trusted`](__arg_trusted.md) macro which tells the verifier that an argument is a trusted pointer to kernel memory. The verifier will by default assume that any trusted pointer argument is never `NULL` (the opposite of normal pointers see [`__arg_nonnull`](__arg_nonnull.md)). Adding the `__arg_nullable` attribute to a trusted pointer argument will tell the verifier that the argument can be `NULL`, which requires the function to add a `NULL` check, but allows the caller to pass a `NULL` pointer. Thus making the argument optional.

### Example

```c hl_lines="2"
__weak int subprog_nullable_task_flavor(
struct task_struct___local *task __arg_trusted __arg_nullable)
{
char buf[16];

if (!task)
return 0;

return bpf_copy_from_user_task(&buf, sizeof(buf), NULL, (void *)task, 0);
}

SEC("?uprobe.s")
int flavor_ptr_nullable(void *ctx)
{
struct task_struct___local *t = (void *)bpf_get_current_task_btf();

return subprog_nullable_task_flavor(t);
}

__weak int subprog_nonnull_task_flavor(struct task_struct___local *task __arg_trusted)
{
char buf[16];

return bpf_copy_from_user_task(&buf, sizeof(buf), NULL, (void *)task, 0);
}

SEC("?uprobe.s")
int flavor_ptr_nonnull(void *ctx)
{
struct task_struct *t = bpf_get_current_task_btf();

return subprog_nonnull_task_flavor((void *)t);
}
```
36 changes: 36 additions & 0 deletions docs/ebpf-library/libbpf/ebpf/__arg_trusted.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
title: "Libbpf eBPF macro '__arg_trusted'"
description: "This page documents the '__arg_trusted' libbpf eBPF macro, including its definition, usage, and examples."
---
# Libbpf eBPF macro `__arg_trusted`

[:octicons-tag-24: v1.4.0](https://github.com/libbpf/libbpf/releases/tag/v1.4.0)

The `__arg_trusted` macros is used to tag a function argument to tell the verifier that its value is a trusted pointer to kernel memory.

## Definition

`#!c #define __arg_trusted __attribute((btf_decl_tag("arg:trusted")))`

## Usage

This macro can be used to tag a function argument of a [global function](../../../linux/concepts/functions.md#function-by-function-verification) to tell the verifier that its value is a trusted pointer to kernel memory. Similarly to how [`__kptr`](__kptr.md) is used on map values and global variables. By default the verifier will assume the argument is never `NULL`, this can be changed by adding the [`__arg_nullable`](__arg_nullable.md) attribute to the argument. The verifier will enforce that a valid trusted pointer is passed to the function on the callsite.

### Example

```c hl_lines="1"
__weak int subprog_nonnull_task_flavor(struct task_struct___local *task __arg_trusted)
{
char buf[16];

return bpf_copy_from_user_task(&buf, sizeof(buf), NULL, (void *)task, 0);
}

SEC("?uprobe.s")
int flavor_ptr_nonnull(void *ctx)
{
struct task_struct *t = bpf_get_current_task_btf();

return subprog_nonnull_task_flavor((void *)t);
}
```
10 changes: 5 additions & 5 deletions docs/ebpf-library/libbpf/ebpf/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ The file contains definitions for the following:
* [`__kptr`](__kptr.md)
* [`__percpu_kptr`](__percpu_kptr.md)
* Global function attributes
* `__arg_ctx`
* `__arg_nonnull`
* `__arg_nullable`
* `__arg_trusted`
* `__arg_arena`
* [`__arg_ctx`](__arg_ctx.md)
* [`__arg_nonnull`](__arg_nonnull.md)
* [`__arg_nullable`](__arg_nullable.md)
* [`__arg_trusted`](__arg_trusted.md)
* [`__arg_arena`](__arg_arena.md)
* [`SEC`](SEC.md)
* [`KERNEL_VERSION`](KERNEL_VERSION.md)
* [`offsetof`](offsetof.md)
Expand Down

0 comments on commit f738cba

Please sign in to comment.