All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH bpf-next v3 0/3] bpf: Pin the start cgroup for cgroup iterator
@ 2022-11-21  7:34 Hou Tao
  2022-11-21  7:34 ` [PATCH bpf-next v3 1/3] bpf: Pin the start cgroup in cgroup_iter_seq_init() Hou Tao
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Hou Tao @ 2022-11-21  7:34 UTC (permalink / raw)
  To: bpf, Martin KaFai Lau, Hao Luo, Yonghong Song
  Cc: Andrii Nakryiko, Song Liu, Alexei Starovoitov, Daniel Borkmann,
	KP Singh, Stanislav Fomichev, Jiri Olsa, John Fastabend, houtao1

From: Hou Tao <houtao1@huawei.com>

Hi,

The patchset tries to fix the potential use-after-free problem in cgroup
iterator. The problem is similar with the UAF problem fixed in map
iterator and the fix is also similar: pinning the iterated resource in
.init_seq_private() and unpinning it in .fini_seq_private(). An
alternative fix is pinning iterator link when opening iterator fd, but
it will make iterator link still being visible after the close of
iterator link fd and the behavior is different with other link types, so
just fixing the bug alone by pinning the start cgroup when creating
cgroup iterator. Also adding a selftests to demonstrate the UAF problem
when iterating a dead cgroup.

Comments are always welcome.

Change Log:
v3:
 * Target bpf-next instead of bpf
 * Patch 1: Use the solution proposed in v1, because pinning iterator
   link will make it behaving different with other link types.
 * Patch 3: Add Acked-by from Hao Luo

v2: https://lore.kernel.org/bpf/20221111063417.1603111-1-houtao@huaweicloud.com/
 * Patch 1: Pinning iterator link when opening iterator, instead of
   acquiring the reference of start cgroup in cgroup_iter_seq_init().
 * Patch 2 & 3: Address comments from Yonghong Song and add Acked-by tag

v1: https://lore.kernel.org/bpf/20221107074222.1323017-1-houtao@huaweicloud.com/

Hou Tao (3):
  bpf: Pin the start cgroup in cgroup_iter_seq_init()
  selftests/bpf: Add cgroup helper remove_cgroup()
  selftests/bpf: Add test for cgroup iterator on a dead cgroup

 kernel/bpf/cgroup_iter.c                      | 14 ++++
 tools/testing/selftests/bpf/cgroup_helpers.c  | 19 +++++
 tools/testing/selftests/bpf/cgroup_helpers.h  |  1 +
 .../selftests/bpf/prog_tests/cgroup_iter.c    | 76 +++++++++++++++++++
 4 files changed, 110 insertions(+)

-- 
2.29.2


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

* [PATCH bpf-next v3 1/3] bpf: Pin the start cgroup in cgroup_iter_seq_init()
  2022-11-21  7:34 [PATCH bpf-next v3 0/3] bpf: Pin the start cgroup for cgroup iterator Hou Tao
@ 2022-11-21  7:34 ` Hou Tao
  2022-11-21 16:27   ` Yonghong Song
  2022-11-21  7:34 ` [PATCH bpf-next v3 2/3] selftests/bpf: Add cgroup helper remove_cgroup() Hou Tao
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 6+ messages in thread
From: Hou Tao @ 2022-11-21  7:34 UTC (permalink / raw)
  To: bpf, Martin KaFai Lau, Hao Luo, Yonghong Song
  Cc: Andrii Nakryiko, Song Liu, Alexei Starovoitov, Daniel Borkmann,
	KP Singh, Stanislav Fomichev, Jiri Olsa, John Fastabend, houtao1

From: Hou Tao <houtao1@huawei.com>

bpf_iter_attach_cgroup() has already acquired an extra reference for the
start cgroup, but the reference may be released if the iterator link fd
is closed after the creation of iterator fd, and it may lead to
user-after-free problem when reading the iterator fd.

An alternative fix is pinning iterator link when opening iterator,
but it will make iterator link being still visible after the close of
iterator link fd and the behavior is different with other link types, so
just fixing it by acquiring another reference for the start cgroup.

Fixes: d4ccaf58a847 ("bpf: Introduce cgroup iter")
Signed-off-by: Hou Tao <houtao1@huawei.com>
---
 kernel/bpf/cgroup_iter.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/kernel/bpf/cgroup_iter.c b/kernel/bpf/cgroup_iter.c
index fbc6167c3599..06989d278846 100644
--- a/kernel/bpf/cgroup_iter.c
+++ b/kernel/bpf/cgroup_iter.c
@@ -164,16 +164,30 @@ static int cgroup_iter_seq_init(void *priv, struct bpf_iter_aux_info *aux)
 	struct cgroup_iter_priv *p = (struct cgroup_iter_priv *)priv;
 	struct cgroup *cgrp = aux->cgroup.start;
 
+	/* bpf_iter_attach_cgroup() has already acquired an extra reference
+	 * for the start cgroup, but the reference may be released after
+	 * cgroup_iter_seq_init(), so acquire another reference for the
+	 * start cgroup.
+	 */
 	p->start_css = &cgrp->self;
+	css_get(p->start_css);
 	p->terminate = false;
 	p->visited_all = false;
 	p->order = aux->cgroup.order;
 	return 0;
 }
 
+static void cgroup_iter_seq_fini(void *priv)
+{
+	struct cgroup_iter_priv *p = (struct cgroup_iter_priv *)priv;
+
+	css_put(p->start_css);
+}
+
 static const struct bpf_iter_seq_info cgroup_iter_seq_info = {
 	.seq_ops		= &cgroup_iter_seq_ops,
 	.init_seq_private	= cgroup_iter_seq_init,
+	.fini_seq_private	= cgroup_iter_seq_fini,
 	.seq_priv_size		= sizeof(struct cgroup_iter_priv),
 };
 
-- 
2.29.2


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

* [PATCH bpf-next v3 2/3] selftests/bpf: Add cgroup helper remove_cgroup()
  2022-11-21  7:34 [PATCH bpf-next v3 0/3] bpf: Pin the start cgroup for cgroup iterator Hou Tao
  2022-11-21  7:34 ` [PATCH bpf-next v3 1/3] bpf: Pin the start cgroup in cgroup_iter_seq_init() Hou Tao
@ 2022-11-21  7:34 ` Hou Tao
  2022-11-21  7:34 ` [PATCH bpf-next v3 3/3] selftests/bpf: Add test for cgroup iterator on a dead cgroup Hou Tao
  2022-11-21 16:50 ` [PATCH bpf-next v3 0/3] bpf: Pin the start cgroup for cgroup iterator patchwork-bot+netdevbpf
  3 siblings, 0 replies; 6+ messages in thread
From: Hou Tao @ 2022-11-21  7:34 UTC (permalink / raw)
  To: bpf, Martin KaFai Lau, Hao Luo, Yonghong Song
  Cc: Andrii Nakryiko, Song Liu, Alexei Starovoitov, Daniel Borkmann,
	KP Singh, Stanislav Fomichev, Jiri Olsa, John Fastabend, houtao1

From: Hou Tao <houtao1@huawei.com>

Add remove_cgroup() to remove a cgroup which doesn't have any children
or live processes. It will be used by the following patch to test cgroup
iterator on a dead cgroup.

Acked-by: Yonghong Song <yhs@fb.com>
Signed-off-by: Hou Tao <houtao1@huawei.com>
---
 tools/testing/selftests/bpf/cgroup_helpers.c | 19 +++++++++++++++++++
 tools/testing/selftests/bpf/cgroup_helpers.h |  1 +
 2 files changed, 20 insertions(+)

diff --git a/tools/testing/selftests/bpf/cgroup_helpers.c b/tools/testing/selftests/bpf/cgroup_helpers.c
index dd1aa5afcf5a..9e95b37a7dff 100644
--- a/tools/testing/selftests/bpf/cgroup_helpers.c
+++ b/tools/testing/selftests/bpf/cgroup_helpers.c
@@ -333,6 +333,25 @@ int get_root_cgroup(void)
 	return fd;
 }
 
+/*
+ * remove_cgroup() - Remove a cgroup
+ * @relative_path: The cgroup path, relative to the workdir, to remove
+ *
+ * This function expects a cgroup to already be created, relative to the cgroup
+ * work dir. It also expects the cgroup doesn't have any children or live
+ * processes and it removes the cgroup.
+ *
+ * On failure, it will print an error to stderr.
+ */
+void remove_cgroup(const char *relative_path)
+{
+	char cgroup_path[PATH_MAX + 1];
+
+	format_cgroup_path(cgroup_path, relative_path);
+	if (rmdir(cgroup_path))
+		log_err("rmdiring cgroup %s .. %s", relative_path, cgroup_path);
+}
+
 /**
  * create_and_get_cgroup() - Create a cgroup, relative to workdir, and get the FD
  * @relative_path: The cgroup path, relative to the workdir, to join
diff --git a/tools/testing/selftests/bpf/cgroup_helpers.h b/tools/testing/selftests/bpf/cgroup_helpers.h
index 3358734356ab..f099a166c94d 100644
--- a/tools/testing/selftests/bpf/cgroup_helpers.h
+++ b/tools/testing/selftests/bpf/cgroup_helpers.h
@@ -18,6 +18,7 @@ int write_cgroup_file_parent(const char *relative_path, const char *file,
 int cgroup_setup_and_join(const char *relative_path);
 int get_root_cgroup(void);
 int create_and_get_cgroup(const char *relative_path);
+void remove_cgroup(const char *relative_path);
 unsigned long long get_cgroup_id(const char *relative_path);
 
 int join_cgroup(const char *relative_path);
-- 
2.29.2


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

* [PATCH bpf-next v3 3/3] selftests/bpf: Add test for cgroup iterator on a dead cgroup
  2022-11-21  7:34 [PATCH bpf-next v3 0/3] bpf: Pin the start cgroup for cgroup iterator Hou Tao
  2022-11-21  7:34 ` [PATCH bpf-next v3 1/3] bpf: Pin the start cgroup in cgroup_iter_seq_init() Hou Tao
  2022-11-21  7:34 ` [PATCH bpf-next v3 2/3] selftests/bpf: Add cgroup helper remove_cgroup() Hou Tao
@ 2022-11-21  7:34 ` Hou Tao
  2022-11-21 16:50 ` [PATCH bpf-next v3 0/3] bpf: Pin the start cgroup for cgroup iterator patchwork-bot+netdevbpf
  3 siblings, 0 replies; 6+ messages in thread
From: Hou Tao @ 2022-11-21  7:34 UTC (permalink / raw)
  To: bpf, Martin KaFai Lau, Hao Luo, Yonghong Song
  Cc: Andrii Nakryiko, Song Liu, Alexei Starovoitov, Daniel Borkmann,
	KP Singh, Stanislav Fomichev, Jiri Olsa, John Fastabend, houtao1

From: Hou Tao <houtao1@huawei.com>

The test closes both iterator link fd and cgroup fd, and removes the
cgroup file to make a dead cgroup before reading from cgroup iterator.
It also uses kern_sync_rcu() and usleep() to wait for the release of
start cgroup. If the start cgroup is not pinned by cgroup iterator,
reading from iterator fd will trigger use-after-free.

Acked-by: Yonghong Song <yhs@fb.com>
Acked-by: Hao Luo <haoluo@google.com>
Signed-off-by: Hou Tao <houtao1@huawei.com>
---
 .../selftests/bpf/prog_tests/cgroup_iter.c    | 76 +++++++++++++++++++
 1 file changed, 76 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/cgroup_iter.c b/tools/testing/selftests/bpf/prog_tests/cgroup_iter.c
index c4a2adb38da1..e02feb5fae97 100644
--- a/tools/testing/selftests/bpf/prog_tests/cgroup_iter.c
+++ b/tools/testing/selftests/bpf/prog_tests/cgroup_iter.c
@@ -189,6 +189,80 @@ static void test_walk_self_only(struct cgroup_iter *skel)
 			      BPF_CGROUP_ITER_SELF_ONLY, "self_only");
 }
 
+static void test_walk_dead_self_only(struct cgroup_iter *skel)
+{
+	DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts);
+	char expected_output[128], buf[128];
+	const char *cgrp_name = "/dead";
+	union bpf_iter_link_info linfo;
+	int len, cgrp_fd, iter_fd;
+	struct bpf_link *link;
+	size_t left;
+	char *p;
+
+	cgrp_fd = create_and_get_cgroup(cgrp_name);
+	if (!ASSERT_GE(cgrp_fd, 0, "create cgrp"))
+		return;
+
+	/* The cgroup will be dead during read() iteration, so it only has
+	 * epilogue in the output
+	 */
+	snprintf(expected_output, sizeof(expected_output), EPILOGUE);
+
+	memset(&linfo, 0, sizeof(linfo));
+	linfo.cgroup.cgroup_fd = cgrp_fd;
+	linfo.cgroup.order = BPF_CGROUP_ITER_SELF_ONLY;
+	opts.link_info = &linfo;
+	opts.link_info_len = sizeof(linfo);
+
+	link = bpf_program__attach_iter(skel->progs.cgroup_id_printer, &opts);
+	if (!ASSERT_OK_PTR(link, "attach_iter"))
+		goto close_cgrp;
+
+	iter_fd = bpf_iter_create(bpf_link__fd(link));
+	if (!ASSERT_GE(iter_fd, 0, "iter_create"))
+		goto free_link;
+
+	/* Close link fd and cgroup fd */
+	bpf_link__destroy(link);
+	close(cgrp_fd);
+
+	/* Remove cgroup to mark it as dead */
+	remove_cgroup(cgrp_name);
+
+	/* Two kern_sync_rcu() and usleep() pairs are used to wait for the
+	 * releases of cgroup css, and the last kern_sync_rcu() and usleep()
+	 * pair is used to wait for the free of cgroup itself.
+	 */
+	kern_sync_rcu();
+	usleep(8000);
+	kern_sync_rcu();
+	usleep(8000);
+	kern_sync_rcu();
+	usleep(1000);
+
+	memset(buf, 0, sizeof(buf));
+	left = ARRAY_SIZE(buf);
+	p = buf;
+	while ((len = read(iter_fd, p, left)) > 0) {
+		p += len;
+		left -= len;
+	}
+
+	ASSERT_STREQ(buf, expected_output, "dead cgroup output");
+
+	/* read() after iter finishes should be ok. */
+	if (len == 0)
+		ASSERT_OK(read(iter_fd, buf, sizeof(buf)), "second_read");
+
+	close(iter_fd);
+	return;
+free_link:
+	bpf_link__destroy(link);
+close_cgrp:
+	close(cgrp_fd);
+}
+
 void test_cgroup_iter(void)
 {
 	struct cgroup_iter *skel = NULL;
@@ -217,6 +291,8 @@ void test_cgroup_iter(void)
 		test_early_termination(skel);
 	if (test__start_subtest("cgroup_iter__self_only"))
 		test_walk_self_only(skel);
+	if (test__start_subtest("cgroup_iter__dead_self_only"))
+		test_walk_dead_self_only(skel);
 out:
 	cgroup_iter__destroy(skel);
 	cleanup_cgroups();
-- 
2.29.2


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

* Re: [PATCH bpf-next v3 1/3] bpf: Pin the start cgroup in cgroup_iter_seq_init()
  2022-11-21  7:34 ` [PATCH bpf-next v3 1/3] bpf: Pin the start cgroup in cgroup_iter_seq_init() Hou Tao
@ 2022-11-21 16:27   ` Yonghong Song
  0 siblings, 0 replies; 6+ messages in thread
From: Yonghong Song @ 2022-11-21 16:27 UTC (permalink / raw)
  To: Hou Tao, bpf, Martin KaFai Lau, Hao Luo, Yonghong Song
  Cc: Andrii Nakryiko, Song Liu, Alexei Starovoitov, Daniel Borkmann,
	KP Singh, Stanislav Fomichev, Jiri Olsa, John Fastabend, houtao1



On 11/20/22 11:34 PM, Hou Tao wrote:
> From: Hou Tao <houtao1@huawei.com>
> 
> bpf_iter_attach_cgroup() has already acquired an extra reference for the
> start cgroup, but the reference may be released if the iterator link fd
> is closed after the creation of iterator fd, and it may lead to
> user-after-free problem when reading the iterator fd.
> 
> An alternative fix is pinning iterator link when opening iterator,
> but it will make iterator link being still visible after the close of
> iterator link fd and the behavior is different with other link types, so
> just fixing it by acquiring another reference for the start cgroup.
> 
> Fixes: d4ccaf58a847 ("bpf: Introduce cgroup iter")
> Signed-off-by: Hou Tao <houtao1@huawei.com>

Acked-by: Yonghong Song <yhs@fb.com>

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

* Re: [PATCH bpf-next v3 0/3] bpf: Pin the start cgroup for cgroup iterator
  2022-11-21  7:34 [PATCH bpf-next v3 0/3] bpf: Pin the start cgroup for cgroup iterator Hou Tao
                   ` (2 preceding siblings ...)
  2022-11-21  7:34 ` [PATCH bpf-next v3 3/3] selftests/bpf: Add test for cgroup iterator on a dead cgroup Hou Tao
@ 2022-11-21 16:50 ` patchwork-bot+netdevbpf
  3 siblings, 0 replies; 6+ messages in thread
From: patchwork-bot+netdevbpf @ 2022-11-21 16:50 UTC (permalink / raw)
  To: Hou Tao
  Cc: bpf, martin.lau, haoluo, yhs, andrii, song, ast, daniel, kpsingh,
	sdf, jolsa, john.fastabend, houtao1

Hello:

This series was applied to bpf/bpf-next.git (master)
by Daniel Borkmann <daniel@iogearbox.net>:

On Mon, 21 Nov 2022 15:34:37 +0800 you wrote:
> From: Hou Tao <houtao1@huawei.com>
> 
> Hi,
> 
> The patchset tries to fix the potential use-after-free problem in cgroup
> iterator. The problem is similar with the UAF problem fixed in map
> iterator and the fix is also similar: pinning the iterated resource in
> .init_seq_private() and unpinning it in .fini_seq_private(). An
> alternative fix is pinning iterator link when opening iterator fd, but
> it will make iterator link still being visible after the close of
> iterator link fd and the behavior is different with other link types, so
> just fixing the bug alone by pinning the start cgroup when creating
> cgroup iterator. Also adding a selftests to demonstrate the UAF problem
> when iterating a dead cgroup.
> 
> [...]

Here is the summary with links:
  - [bpf-next,v3,1/3] bpf: Pin the start cgroup in cgroup_iter_seq_init()
    https://git.kernel.org/bpf/bpf-next/c/1a5160d4d8fe
  - [bpf-next,v3,2/3] selftests/bpf: Add cgroup helper remove_cgroup()
    https://git.kernel.org/bpf/bpf-next/c/2a42461a8831
  - [bpf-next,v3,3/3] selftests/bpf: Add test for cgroup iterator on a dead cgroup
    https://git.kernel.org/bpf/bpf-next/c/8589e92675aa

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] 6+ messages in thread

end of thread, other threads:[~2022-11-21 16:50 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-21  7:34 [PATCH bpf-next v3 0/3] bpf: Pin the start cgroup for cgroup iterator Hou Tao
2022-11-21  7:34 ` [PATCH bpf-next v3 1/3] bpf: Pin the start cgroup in cgroup_iter_seq_init() Hou Tao
2022-11-21 16:27   ` Yonghong Song
2022-11-21  7:34 ` [PATCH bpf-next v3 2/3] selftests/bpf: Add cgroup helper remove_cgroup() Hou Tao
2022-11-21  7:34 ` [PATCH bpf-next v3 3/3] selftests/bpf: Add test for cgroup iterator on a dead cgroup Hou Tao
2022-11-21 16:50 ` [PATCH bpf-next v3 0/3] bpf: Pin the start cgroup for cgroup iterator patchwork-bot+netdevbpf

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.