public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH bpf-next 1/2] libbpf: Don't take direct pointers into BTF data from st_ops
@ 2024-07-24 17:14 David Vernet
  2024-07-24 17:14 ` [PATCH bpf-next 2/2] selftests/bpf: Add test for resizing data map with struct_ops David Vernet
  2024-07-25  0:20 ` [PATCH bpf-next 1/2] libbpf: Don't take direct pointers into BTF data from st_ops patchwork-bot+netdevbpf
  0 siblings, 2 replies; 5+ messages in thread
From: David Vernet @ 2024-07-24 17:14 UTC (permalink / raw)
  To: bpf
  Cc: ast, daniel, andrii, martin.lau, song, yonghong.song,
	john.fastabend, kpsingh, sdf, haoluo, jolsa, linux-kernel,
	kernel-team, tj

In struct bpf_struct_ops, we have take a pointer to a BTF type name, and
a struct btf_type. This was presumably done for convenience, but can
actually result in subtle and confusing bugs given that BTF data can be
invalidated before a program is loaded. For example, in sched_ext, we
may sometimes resize a data section after a skeleton has been opened,
but before the struct_ops scheduler map has been loaded. This may cause
the BTF data to be realloc'd, which can then cause a UAF when loading
the program because the struct_ops map has pointers directly into the
BTF data.

We're already storing the BTF type_id in struct bpf_struct_ops. Because
type_id is stable, we can therefore just update the places where we were
looking at those pointers to instead do the lookups we need from the
type_id.

Fixes: 590a00888250 ("bpf: libbpf: Add STRUCT_OPS support")
Signed-off-by: David Vernet <void@manifault.com>
---
 tools/lib/bpf/libbpf.c | 23 +++++++++++++----------
 1 file changed, 13 insertions(+), 10 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index a3be6f8fac09..e55353887439 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -496,8 +496,6 @@ struct bpf_program {
 };
 
 struct bpf_struct_ops {
-	const char *tname;
-	const struct btf_type *type;
 	struct bpf_program **progs;
 	__u32 *kern_func_off;
 	/* e.g. struct tcp_congestion_ops in bpf_prog's btf format */
@@ -1083,11 +1081,14 @@ static int bpf_object_adjust_struct_ops_autoload(struct bpf_object *obj)
 			continue;
 
 		for (j = 0; j < obj->nr_maps; ++j) {
+			const struct btf_type *type;
+
 			map = &obj->maps[j];
 			if (!bpf_map__is_struct_ops(map))
 				continue;
 
-			vlen = btf_vlen(map->st_ops->type);
+			type = btf__type_by_id(obj->btf, map->st_ops->type_id);
+			vlen = btf_vlen(type);
 			for (k = 0; k < vlen; ++k) {
 				slot_prog = map->st_ops->progs[k];
 				if (prog != slot_prog)
@@ -1121,8 +1122,8 @@ static int bpf_map__init_kern_struct_ops(struct bpf_map *map)
 	int err;
 
 	st_ops = map->st_ops;
-	type = st_ops->type;
-	tname = st_ops->tname;
+	type = btf__type_by_id(btf, st_ops->type_id);
+	tname = btf__name_by_offset(btf, type->name_off);
 	err = find_struct_ops_kern_types(obj, tname, &mod_btf,
 					 &kern_type, &kern_type_id,
 					 &kern_vtype, &kern_vtype_id,
@@ -1423,8 +1424,6 @@ static int init_struct_ops_maps(struct bpf_object *obj, const char *sec_name,
 		memcpy(st_ops->data,
 		       data->d_buf + vsi->offset,
 		       type->size);
-		st_ops->tname = tname;
-		st_ops->type = type;
 		st_ops->type_id = type_id;
 
 		pr_debug("struct_ops init: struct %s(type_id=%u) %s found at offset %u\n",
@@ -8445,11 +8444,13 @@ static int bpf_object__resolve_externs(struct bpf_object *obj,
 
 static void bpf_map_prepare_vdata(const struct bpf_map *map)
 {
+	const struct btf_type *type;
 	struct bpf_struct_ops *st_ops;
 	__u32 i;
 
 	st_ops = map->st_ops;
-	for (i = 0; i < btf_vlen(st_ops->type); i++) {
+	type = btf__type_by_id(map->obj->btf, st_ops->type_id);
+	for (i = 0; i < btf_vlen(type); i++) {
 		struct bpf_program *prog = st_ops->progs[i];
 		void *kern_data;
 		int prog_fd;
@@ -9712,6 +9713,7 @@ static struct bpf_map *find_struct_ops_map_by_offset(struct bpf_object *obj,
 static int bpf_object__collect_st_ops_relos(struct bpf_object *obj,
 					    Elf64_Shdr *shdr, Elf_Data *data)
 {
+	const struct btf_type *type;
 	const struct btf_member *member;
 	struct bpf_struct_ops *st_ops;
 	struct bpf_program *prog;
@@ -9771,13 +9773,14 @@ static int bpf_object__collect_st_ops_relos(struct bpf_object *obj,
 		}
 		insn_idx = sym->st_value / BPF_INSN_SZ;
 
-		member = find_member_by_offset(st_ops->type, moff * 8);
+		type = btf__type_by_id(btf, st_ops->type_id);
+		member = find_member_by_offset(type, moff * 8);
 		if (!member) {
 			pr_warn("struct_ops reloc %s: cannot find member at moff %u\n",
 				map->name, moff);
 			return -EINVAL;
 		}
-		member_idx = member - btf_members(st_ops->type);
+		member_idx = member - btf_members(type);
 		name = btf__name_by_offset(btf, member->name_off);
 
 		if (!resolve_func_ptr(btf, member->type, NULL)) {
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH bpf-next 2/2] selftests/bpf: Add test for resizing data map with struct_ops
  2024-07-24 17:14 [PATCH bpf-next 1/2] libbpf: Don't take direct pointers into BTF data from st_ops David Vernet
@ 2024-07-24 17:14 ` David Vernet
  2024-07-25  0:12   ` Andrii Nakryiko
  2024-07-25  0:20 ` [PATCH bpf-next 1/2] libbpf: Don't take direct pointers into BTF data from st_ops patchwork-bot+netdevbpf
  1 sibling, 1 reply; 5+ messages in thread
From: David Vernet @ 2024-07-24 17:14 UTC (permalink / raw)
  To: bpf
  Cc: ast, daniel, andrii, martin.lau, song, yonghong.song,
	john.fastabend, kpsingh, sdf, haoluo, jolsa, linux-kernel,
	kernel-team, tj

Tests that if you resize a map after opening a skel, that it doesn't
cause a UAF which causes a struct_ops map to fail to be able to load.

Signed-off-by: David Vernet <void@manifault.com>
---
 .../bpf/prog_tests/struct_ops_resize.c        | 30 +++++++++++++++++++
 .../selftests/bpf/progs/struct_ops_resize.c   | 24 +++++++++++++++
 2 files changed, 54 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/struct_ops_resize.c
 create mode 100644 tools/testing/selftests/bpf/progs/struct_ops_resize.c

diff --git a/tools/testing/selftests/bpf/prog_tests/struct_ops_resize.c b/tools/testing/selftests/bpf/prog_tests/struct_ops_resize.c
new file mode 100644
index 000000000000..7584f91c2bd1
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/struct_ops_resize.c
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <test_progs.h>
+#include "struct_ops_resize.skel.h"
+
+static void resize_datasec(void)
+{
+	struct struct_ops_resize *skel;
+	int err;
+
+	skel = struct_ops_resize__open();
+	if (!ASSERT_OK_PTR(skel, "struct_ops_resize__open"))
+		return;
+
+	err  = bpf_map__set_value_size(skel->maps.data_resizable, 1 << 15);
+	if (!ASSERT_OK(err, "bpf_map__set_value_size"))
+		goto cleanup;
+
+	err = struct_ops_resize__load(skel);
+	ASSERT_OK(err, "struct_ops_resize__load");
+
+cleanup:
+	struct_ops_resize__destroy(skel);
+}
+
+void test_struct_ops_resize(void)
+{
+	if (test__start_subtest("resize_datasec"))
+		resize_datasec();
+}
diff --git a/tools/testing/selftests/bpf/progs/struct_ops_resize.c b/tools/testing/selftests/bpf/progs/struct_ops_resize.c
new file mode 100644
index 000000000000..d0b235f4bbaa
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/struct_ops_resize.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+char _license[] SEC("license") = "GPL";
+
+char resizable[1] SEC(".data.resizable");
+
+SEC("struct_ops/test_1")
+int BPF_PROG(test_1)
+{
+	return 0;
+}
+
+struct bpf_testmod_ops {
+	int (*test_1)(void);
+};
+
+SEC(".struct_ops.link")
+struct bpf_testmod_ops testmod = {
+	.test_1 = (void *)test_1
+};
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH bpf-next 2/2] selftests/bpf: Add test for resizing data map with struct_ops
  2024-07-24 17:14 ` [PATCH bpf-next 2/2] selftests/bpf: Add test for resizing data map with struct_ops David Vernet
@ 2024-07-25  0:12   ` Andrii Nakryiko
  2024-07-25  2:54     ` David Vernet
  0 siblings, 1 reply; 5+ messages in thread
From: Andrii Nakryiko @ 2024-07-25  0:12 UTC (permalink / raw)
  To: David Vernet
  Cc: bpf, ast, daniel, andrii, martin.lau, song, yonghong.song,
	john.fastabend, kpsingh, sdf, haoluo, jolsa, linux-kernel,
	kernel-team, tj

On Wed, Jul 24, 2024 at 10:15 AM David Vernet <void@manifault.com> wrote:
>
> Tests that if you resize a map after opening a skel, that it doesn't
> cause a UAF which causes a struct_ops map to fail to be able to load.
>
> Signed-off-by: David Vernet <void@manifault.com>
> ---
>  .../bpf/prog_tests/struct_ops_resize.c        | 30 +++++++++++++++++++
>  .../selftests/bpf/progs/struct_ops_resize.c   | 24 +++++++++++++++
>  2 files changed, 54 insertions(+)
>  create mode 100644 tools/testing/selftests/bpf/prog_tests/struct_ops_resize.c
>  create mode 100644 tools/testing/selftests/bpf/progs/struct_ops_resize.c
>
> diff --git a/tools/testing/selftests/bpf/prog_tests/struct_ops_resize.c b/tools/testing/selftests/bpf/prog_tests/struct_ops_resize.c
> new file mode 100644
> index 000000000000..7584f91c2bd1
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/struct_ops_resize.c
> @@ -0,0 +1,30 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <test_progs.h>
> +#include "struct_ops_resize.skel.h"
> +
> +static void resize_datasec(void)
> +{
> +       struct struct_ops_resize *skel;
> +       int err;
> +
> +       skel = struct_ops_resize__open();
> +       if (!ASSERT_OK_PTR(skel, "struct_ops_resize__open"))
> +               return;
> +
> +       err  = bpf_map__set_value_size(skel->maps.data_resizable, 1 << 15);
> +       if (!ASSERT_OK(err, "bpf_map__set_value_size"))
> +               goto cleanup;
> +
> +       err = struct_ops_resize__load(skel);
> +       ASSERT_OK(err, "struct_ops_resize__load");
> +
> +cleanup:
> +       struct_ops_resize__destroy(skel);
> +}
> +
> +void test_struct_ops_resize(void)
> +{
> +       if (test__start_subtest("resize_datasec"))
> +               resize_datasec();

It seems a bit unnecessary to add an entire new test with a subtest
just for this. Would you mind adding this testing logic into the
already existing prog_tests/global_map_resize.c set of cases?

I've applied patch #1, as it's obviously correct, so I didn't want to
delay the fix. Thanks!

> +}
> diff --git a/tools/testing/selftests/bpf/progs/struct_ops_resize.c b/tools/testing/selftests/bpf/progs/struct_ops_resize.c
> new file mode 100644
> index 000000000000..d0b235f4bbaa
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/struct_ops_resize.c
> @@ -0,0 +1,24 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/bpf.h>
> +#include <bpf/bpf_helpers.h>
> +#include <bpf/bpf_tracing.h>
> +
> +char _license[] SEC("license") = "GPL";
> +
> +char resizable[1] SEC(".data.resizable");
> +
> +SEC("struct_ops/test_1")
> +int BPF_PROG(test_1)
> +{
> +       return 0;
> +}
> +
> +struct bpf_testmod_ops {
> +       int (*test_1)(void);
> +};
> +
> +SEC(".struct_ops.link")
> +struct bpf_testmod_ops testmod = {
> +       .test_1 = (void *)test_1
> +};
> --
> 2.45.2
>

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH bpf-next 1/2] libbpf: Don't take direct pointers into BTF data from st_ops
  2024-07-24 17:14 [PATCH bpf-next 1/2] libbpf: Don't take direct pointers into BTF data from st_ops David Vernet
  2024-07-24 17:14 ` [PATCH bpf-next 2/2] selftests/bpf: Add test for resizing data map with struct_ops David Vernet
@ 2024-07-25  0:20 ` patchwork-bot+netdevbpf
  1 sibling, 0 replies; 5+ messages in thread
From: patchwork-bot+netdevbpf @ 2024-07-25  0:20 UTC (permalink / raw)
  To: David Vernet
  Cc: bpf, ast, daniel, andrii, martin.lau, song, yonghong.song,
	john.fastabend, kpsingh, sdf, haoluo, jolsa, linux-kernel,
	kernel-team, tj

Hello:

This series was applied to bpf/bpf-next.git (master)
by Andrii Nakryiko <andrii@kernel.org>:

On Wed, 24 Jul 2024 12:14:58 -0500 you wrote:
> In struct bpf_struct_ops, we have take a pointer to a BTF type name, and
> a struct btf_type. This was presumably done for convenience, but can
> actually result in subtle and confusing bugs given that BTF data can be
> invalidated before a program is loaded. For example, in sched_ext, we
> may sometimes resize a data section after a skeleton has been opened,
> but before the struct_ops scheduler map has been loaded. This may cause
> the BTF data to be realloc'd, which can then cause a UAF when loading
> the program because the struct_ops map has pointers directly into the
> BTF data.
> 
> [...]

Here is the summary with links:
  - [bpf-next,1/2] libbpf: Don't take direct pointers into BTF data from st_ops
    https://git.kernel.org/bpf/bpf-next/c/7244100e0389
  - [bpf-next,2/2] selftests/bpf: Add test for resizing data map with struct_ops
    (no matching commit)

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH bpf-next 2/2] selftests/bpf: Add test for resizing data map with struct_ops
  2024-07-25  0:12   ` Andrii Nakryiko
@ 2024-07-25  2:54     ` David Vernet
  0 siblings, 0 replies; 5+ messages in thread
From: David Vernet @ 2024-07-25  2:54 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: bpf, ast, daniel, andrii, martin.lau, song, yonghong.song,
	john.fastabend, kpsingh, sdf, haoluo, jolsa, linux-kernel,
	kernel-team, tj

[-- Attachment #1: Type: text/plain, Size: 2376 bytes --]

On Wed, Jul 24, 2024 at 05:12:00PM -0700, Andrii Nakryiko wrote:
> On Wed, Jul 24, 2024 at 10:15 AM David Vernet <void@manifault.com> wrote:
> >
> > Tests that if you resize a map after opening a skel, that it doesn't
> > cause a UAF which causes a struct_ops map to fail to be able to load.
> >
> > Signed-off-by: David Vernet <void@manifault.com>
> > ---
> >  .../bpf/prog_tests/struct_ops_resize.c        | 30 +++++++++++++++++++
> >  .../selftests/bpf/progs/struct_ops_resize.c   | 24 +++++++++++++++
> >  2 files changed, 54 insertions(+)
> >  create mode 100644 tools/testing/selftests/bpf/prog_tests/struct_ops_resize.c
> >  create mode 100644 tools/testing/selftests/bpf/progs/struct_ops_resize.c
> >
> > diff --git a/tools/testing/selftests/bpf/prog_tests/struct_ops_resize.c b/tools/testing/selftests/bpf/prog_tests/struct_ops_resize.c
> > new file mode 100644
> > index 000000000000..7584f91c2bd1
> > --- /dev/null
> > +++ b/tools/testing/selftests/bpf/prog_tests/struct_ops_resize.c
> > @@ -0,0 +1,30 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +#include <test_progs.h>
> > +#include "struct_ops_resize.skel.h"
> > +
> > +static void resize_datasec(void)
> > +{
> > +       struct struct_ops_resize *skel;
> > +       int err;
> > +
> > +       skel = struct_ops_resize__open();
> > +       if (!ASSERT_OK_PTR(skel, "struct_ops_resize__open"))
> > +               return;
> > +
> > +       err  = bpf_map__set_value_size(skel->maps.data_resizable, 1 << 15);
> > +       if (!ASSERT_OK(err, "bpf_map__set_value_size"))
> > +               goto cleanup;
> > +
> > +       err = struct_ops_resize__load(skel);
> > +       ASSERT_OK(err, "struct_ops_resize__load");
> > +
> > +cleanup:
> > +       struct_ops_resize__destroy(skel);
> > +}
> > +
> > +void test_struct_ops_resize(void)
> > +{
> > +       if (test__start_subtest("resize_datasec"))
> > +               resize_datasec();
> 
> It seems a bit unnecessary to add an entire new test with a subtest
> just for this. Would you mind adding this testing logic into the
> already existing prog_tests/global_map_resize.c set of cases?

Sure thing, I'll send a subsequent patch that adds the testcase to
prog_tests/global_map_resize.c.

> I've applied patch #1, as it's obviously correct, so I didn't want to
> delay the fix. Thanks!

Thanks!

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2024-07-25  2:55 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-07-24 17:14 [PATCH bpf-next 1/2] libbpf: Don't take direct pointers into BTF data from st_ops David Vernet
2024-07-24 17:14 ` [PATCH bpf-next 2/2] selftests/bpf: Add test for resizing data map with struct_ops David Vernet
2024-07-25  0:12   ` Andrii Nakryiko
2024-07-25  2:54     ` David Vernet
2024-07-25  0:20 ` [PATCH bpf-next 1/2] libbpf: Don't take direct pointers into BTF data from st_ops patchwork-bot+netdevbpf

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox