BPF List
 help / color / mirror / Atom feed
From: Martin KaFai Lau <martin.lau@linux.dev>
To: thinker.li@gmail.com
Cc: sinquersw@gmail.com, kuifeng@meta.com, bpf@vger.kernel.org,
	ast@kernel.org, song@kernel.org, kernel-team@meta.com,
	andrii@kernel.org, drosen@google.com
Subject: Re: [PATCH bpf-next v16 09/14] bpf: hold module refcnt in bpf_struct_ops map creation and prog verification.
Date: Thu, 18 Jan 2024 14:18:37 -0800	[thread overview]
Message-ID: <a0a382b1-467c-4c28-8882-8f523826178a@linux.dev> (raw)
In-Reply-To: <20240118014930.1992551-10-thinker.li@gmail.com>

On 1/17/24 5:49 PM, thinker.li@gmail.com wrote:
> From: Kui-Feng Lee <thinker.li@gmail.com>
> 
> To ensure that a module remains accessible whenever a struct_ops object of
> a struct_ops type provided by the module is still in use.
> 
> struct bpf_struct_ops_map doesn't hold a refcnt to btf anymore since a
> module will hold a refcnt to it's btf already. But, struct_ops programs are
> different. They hold their associated btf, not the module since they need
> only btf to assure their types (signatures).
> 
> However, verifier holds the refcnt of the associated module of a struct_ops
> type temporarily when verify a struct_ops prog. Verifier needs the help
> from the verifier operators (struct bpf_verifier_ops) provided by the owner
> module to verify data access of a prog, provide information, and generate
> code.
> 
> This patch also add a count of links (links_cnt) to bpf_struct_ops_map. It
> avoids bpf_struct_ops_map_put_progs() from accessing btf after calling
> module_put() in bpf_struct_ops_map_free().

Good catch in v16.

> 
> Signed-off-by: Kui-Feng Lee <thinker.li@gmail.com>
> ---
>   include/linux/bpf.h          |  1 +
>   include/linux/bpf_verifier.h |  1 +
>   kernel/bpf/bpf_struct_ops.c  | 31 +++++++++++++++++++++++++------
>   kernel/bpf/verifier.c        | 10 ++++++++++
>   4 files changed, 37 insertions(+), 6 deletions(-)
> 
> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> index 3d1c1014fdb2..a977ed75288c 100644
> --- a/include/linux/bpf.h
> +++ b/include/linux/bpf.h
> @@ -1674,6 +1674,7 @@ struct bpf_struct_ops {
>   	int (*update)(void *kdata, void *old_kdata);
>   	int (*validate)(void *kdata);
>   	void *cfi_stubs;
> +	struct module *owner;
>   	const char *name;
>   	struct btf_func_model func_models[BPF_STRUCT_OPS_MAX_NR_MEMBERS];
>   };
> diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
> index d07d857ca67f..e6cf025c9446 100644
> --- a/include/linux/bpf_verifier.h
> +++ b/include/linux/bpf_verifier.h
> @@ -662,6 +662,7 @@ struct bpf_verifier_env {
>   	u32 prev_insn_idx;
>   	struct bpf_prog *prog;		/* eBPF program being verified */
>   	const struct bpf_verifier_ops *ops;
> +	struct module *attach_btf_mod;	/* The owner module of prog->aux->attach_btf */
>   	struct bpf_verifier_stack_elem *head; /* stack of verifier states to be processed */
>   	int stack_size;			/* number of states to be processed */
>   	bool strict_alignment;		/* perform strict pointer alignment checks */
> diff --git a/kernel/bpf/bpf_struct_ops.c b/kernel/bpf/bpf_struct_ops.c
> index 3b8d689ece5d..61486f6595ea 100644
> --- a/kernel/bpf/bpf_struct_ops.c
> +++ b/kernel/bpf/bpf_struct_ops.c
> @@ -40,6 +40,7 @@ struct bpf_struct_ops_map {
>   	 * (in kvalue.data).
>   	 */
>   	struct bpf_link **links;
> +	u32 links_cnt;
>   	/* image is a page that has all the trampolines
>   	 * that stores the func args before calling the bpf_prog.
>   	 * A PAGE_SIZE "image" is enough to store all trampoline for
> @@ -306,10 +307,9 @@ static void *bpf_struct_ops_map_lookup_elem(struct bpf_map *map, void *key)
>   
>   static void bpf_struct_ops_map_put_progs(struct bpf_struct_ops_map *st_map)
>   {
> -	const struct btf_type *t = st_map->st_ops_desc->type;
>   	u32 i;
>   
> -	for (i = 0; i < btf_type_vlen(t); i++) {
> +	for (i = 0; i < st_map->links_cnt; i++) {
>   		if (st_map->links[i]) {
>   			bpf_link_put(st_map->links[i]);
>   			st_map->links[i] = NULL;
> @@ -641,12 +641,20 @@ static void __bpf_struct_ops_map_free(struct bpf_map *map)
>   		bpf_jit_uncharge_modmem(PAGE_SIZE);
>   	}
>   	bpf_map_area_free(st_map->uvalue);
> -	btf_put(st_map->btf);
>   	bpf_map_area_free(st_map);
>   }
>   
>   static void bpf_struct_ops_map_free(struct bpf_map *map)
>   {
> +	struct bpf_struct_ops_map *st_map = (struct bpf_struct_ops_map *)map;
> +
> +	/* st_ops->owner was acquired during map_alloc to implicitly holds
> +	 * the btf's refcnt. The acquire was only done when btf_is_module()
> +	 * st_map->btf cannot be NULL here.
> +	 */
> +	if (btf_is_module(st_map->btf))
> +		module_put(st_map->st_ops_desc->st_ops->owner);
> +
>   	/* The struct_ops's function may switch to another struct_ops.
>   	 *
>   	 * For example, bpf_tcp_cc_x->init() may switch to
> @@ -682,6 +690,7 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr)
>   	size_t st_map_size;
>   	struct bpf_struct_ops_map *st_map;
>   	const struct btf_type *t, *vt;
> +	struct module *mod = NULL;
>   	struct bpf_map *map;
>   	struct btf *btf;
>   	int ret;
> @@ -695,11 +704,20 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr)
>   			btf_put(btf);
>   			return ERR_PTR(-EINVAL);
>   		}
> +
> +		mod = btf_try_get_module(btf);

nit. btf_put(btf) here.

> +		if (!mod) {
> +			btf_put(btf);
> +			return ERR_PTR(-EINVAL);
> +		}
> +		/* mod holds a refcnt to btf. We don't need an extra refcnt
> +		 * here.
> +		 */
> +		btf_put(btf);
>   	} else {
>   		btf = bpf_get_btf_vmlinux();
>   		if (IS_ERR(btf))
>   			return ERR_CAST(btf);
> -		btf_get(btf);
>   	}
>   
>   	st_ops_desc = bpf_struct_ops_find_value(btf, attr->btf_vmlinux_value_type_id);
> @@ -746,8 +764,9 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr)
>   		goto errout_free;
>   	}
>   	st_map->uvalue = bpf_map_area_alloc(vt->size, NUMA_NO_NODE);
> +	st_map->links_cnt = btf_type_vlen(t);
>   	st_map->links =
> -		bpf_map_area_alloc(btf_type_vlen(t) * sizeof(struct bpf_links *),
> +		bpf_map_area_alloc(st_map->links_cnt * sizeof(struct bpf_links *),
>   				   NUMA_NO_NODE);
>   	if (!st_map->uvalue || !st_map->links) {
>   		ret = -ENOMEM;
> @@ -763,7 +782,7 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr)
>   errout_free:
>   	__bpf_struct_ops_map_free(map);
>   errout:
> -	btf_put(btf);
> +	module_put(mod);
>   
>   	return ERR_PTR(ret);
>   }
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index ff41f7736618..60f08f468399 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -20243,6 +20243,14 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env)
>   	}
>   
>   	btf = prog->aux->attach_btf ?: bpf_get_btf_vmlinux();
> +	if (btf_is_module(btf)) {
> +		/* Make sure st_ops is valid through the lifetime of env */
> +		env->attach_btf_mod = btf_try_get_module(btf);
> +		if (!env->attach_btf_mod) {
> +			verbose(env, "owner module of btf is not found\n");

nit. A better message, something like:

			verbose(env, "struct_ops module %s is not found\n",
				btf_get_name(btf));

> +			return -ENOTSUPP;
> +		}
> +	}
>   
>   	btf_id = prog->aux->attach_btf_id;
>   	st_ops_desc = bpf_struct_ops_find(btf, btf_id);
> @@ -20968,6 +20976,8 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u3
>   		env->prog->expected_attach_type = 0;
>   
>   	*prog = env->prog;
> +
> +	module_put(env->attach_btf_mod);
>   err_unlock:
>   	if (!is_priv)
>   		mutex_unlock(&bpf_verifier_lock);


  reply	other threads:[~2024-01-18 22:18 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-01-18  1:49 [PATCH bpf-next v16 00/14] Registrating struct_ops types from modules thinker.li
2024-01-18  1:49 ` [PATCH bpf-next v16 01/14] bpf: refactory struct_ops type initialization to a function thinker.li
2024-01-18  1:49 ` [PATCH bpf-next v16 02/14] bpf: get type information with BTF_ID_LIST thinker.li
2024-01-18  1:49 ` [PATCH bpf-next v16 03/14] bpf, net: introduce bpf_struct_ops_desc thinker.li
2024-01-18 21:30   ` Martin KaFai Lau
2024-01-19  0:47     ` Kui-Feng Lee
2024-01-18  1:49 ` [PATCH bpf-next v16 04/14] bpf: add struct_ops_tab to btf thinker.li
2024-01-18 21:36   ` Martin KaFai Lau
2024-01-19  1:13     ` Kui-Feng Lee
2024-01-18  1:49 ` [PATCH bpf-next v16 05/14] bpf: make struct_ops_map support btfs other than btf_vmlinux thinker.li
2024-01-18  1:49 ` [PATCH bpf-next v16 06/14] bpf: pass btf object id in bpf_map_info thinker.li
2024-01-18 21:42   ` Martin KaFai Lau
2024-01-19 21:38     ` Kui-Feng Lee
2024-01-18  1:49 ` [PATCH bpf-next v16 07/14] bpf: lookup struct_ops types from a given module BTF thinker.li
2024-01-18  1:49 ` [PATCH bpf-next v16 08/14] bpf: pass attached BTF to the bpf_struct_ops subsystem thinker.li
2024-01-18 21:56   ` Martin KaFai Lau
2024-01-19 18:05     ` Kui-Feng Lee
2024-01-19 18:43       ` Kui-Feng Lee
2024-01-18  1:49 ` [PATCH bpf-next v16 09/14] bpf: hold module refcnt in bpf_struct_ops map creation and prog verification thinker.li
2024-01-18 22:18   ` Martin KaFai Lau [this message]
2024-01-19 19:10     ` Kui-Feng Lee
2024-01-18  1:49 ` [PATCH bpf-next v16 10/14] bpf: validate value_type thinker.li
2024-01-18  1:49 ` [PATCH bpf-next v16 11/14] bpf, net: switch to dynamic registration thinker.li
2024-01-18 22:25   ` Martin KaFai Lau
2024-01-19 19:29     ` Kui-Feng Lee
2024-01-18  1:49 ` [PATCH bpf-next v16 12/14] libbpf: Find correct module BTFs for struct_ops maps and progs thinker.li
2024-01-18  1:49 ` [PATCH bpf-next v16 13/14] bpf: export btf_ctx_access to modules thinker.li
2024-01-18  1:49 ` [PATCH bpf-next v16 14/14] selftests/bpf: test case for register_bpf_struct_ops() thinker.li
2024-01-18 22:41   ` Martin KaFai Lau
2024-01-19 19:31     ` Kui-Feng Lee

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=a0a382b1-467c-4c28-8882-8f523826178a@linux.dev \
    --to=martin.lau@linux.dev \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=drosen@google.com \
    --cc=kernel-team@meta.com \
    --cc=kuifeng@meta.com \
    --cc=sinquersw@gmail.com \
    --cc=song@kernel.org \
    --cc=thinker.li@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox