Commit a024843d authored by Mykyta Yatsenko's avatar Mykyta Yatsenko Committed by Andrii Nakryiko
Browse files

selftests/bpf: Test freplace from user namespace



Add selftests to verify that it is possible to load freplace program
from user namespace if BPF token is initialized by bpf_object__prepare
before calling bpf_program__set_attach_target.
Negative test is added as well.

Modified type of the priv_prog to xdp, as kprobe did not work on aarch64
and s390x.

Signed-off-by: default avatarMykyta Yatsenko <yatsenko@meta.com>
Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
Acked-by: default avatarYonghong Song <yonghong.song@linux.dev>
Link: https://lore.kernel.org/bpf/20250317174039.161275-5-mykyta.yatsenko5@gmail.com
parent 974ef9f0
Loading
Loading
Loading
Loading
+96 −1
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include "priv_prog.skel.h"
#include "dummy_st_ops_success.skel.h"
#include "token_lsm.skel.h"
#include "priv_freplace_prog.skel.h"

static inline int sys_mount(const char *dev_name, const char *dir_name,
			    const char *type, unsigned long flags,
@@ -788,6 +789,84 @@ static int userns_obj_priv_prog(int mnt_fd, struct token_lsm *lsm_skel)
	return 0;
}

static int userns_obj_priv_freplace_setup(int mnt_fd, struct priv_freplace_prog **fr_skel,
					  struct priv_prog **skel, int *tgt_fd)
{
	LIBBPF_OPTS(bpf_object_open_opts, opts);
	int err;
	char buf[256];

	/* use bpf_token_path to provide BPF FS path */
	snprintf(buf, sizeof(buf), "/proc/self/fd/%d", mnt_fd);
	opts.bpf_token_path = buf;
	*skel = priv_prog__open_opts(&opts);
	if (!ASSERT_OK_PTR(*skel, "priv_prog__open_opts"))
		return -EINVAL;
	err = priv_prog__load(*skel);
	if (!ASSERT_OK(err, "priv_prog__load"))
		return -EINVAL;

	*fr_skel = priv_freplace_prog__open_opts(&opts);
	if (!ASSERT_OK_PTR(*skel, "priv_freplace_prog__open_opts"))
		return -EINVAL;

	*tgt_fd = bpf_program__fd((*skel)->progs.xdp_prog1);
	return 0;
}

/* Verify that freplace works from user namespace, because bpf token is loaded
 * in bpf_object__prepare
 */
static int userns_obj_priv_freplace_prog(int mnt_fd, struct token_lsm *lsm_skel)
{
	struct priv_freplace_prog *fr_skel = NULL;
	struct priv_prog *skel = NULL;
	int err, tgt_fd;

	err = userns_obj_priv_freplace_setup(mnt_fd, &fr_skel, &skel, &tgt_fd);
	if (!ASSERT_OK(err, "setup"))
		goto out;

	err = bpf_object__prepare(fr_skel->obj);
	if (!ASSERT_OK(err, "freplace__prepare"))
		goto out;

	err = bpf_program__set_attach_target(fr_skel->progs.new_xdp_prog2, tgt_fd, "xdp_prog1");
	if (!ASSERT_OK(err, "set_attach_target"))
		goto out;

	err = priv_freplace_prog__load(fr_skel);
	ASSERT_OK(err, "priv_freplace_prog__load");

out:
	priv_freplace_prog__destroy(fr_skel);
	priv_prog__destroy(skel);
	return err;
}

/* Verify that replace fails to set attach target from user namespace without bpf token */
static int userns_obj_priv_freplace_prog_fail(int mnt_fd, struct token_lsm *lsm_skel)
{
	struct priv_freplace_prog *fr_skel = NULL;
	struct priv_prog *skel = NULL;
	int err, tgt_fd;

	err = userns_obj_priv_freplace_setup(mnt_fd, &fr_skel, &skel, &tgt_fd);
	if (!ASSERT_OK(err, "setup"))
		goto out;

	err = bpf_program__set_attach_target(fr_skel->progs.new_xdp_prog2, tgt_fd, "xdp_prog1");
	if (ASSERT_ERR(err, "attach fails"))
		err = 0;
	else
		err = -EINVAL;

out:
	priv_freplace_prog__destroy(fr_skel);
	priv_prog__destroy(skel);
	return err;
}

/* this test is called with BPF FS that doesn't delegate BPF_BTF_LOAD command,
 * which should cause struct_ops application to fail, as BTF won't be uploaded
 * into the kernel, even if STRUCT_OPS programs themselves are allowed
@@ -1004,12 +1083,28 @@ void test_token(void)
	if (test__start_subtest("obj_priv_prog")) {
		struct bpffs_opts opts = {
			.cmds = bit(BPF_PROG_LOAD),
			.progs = bit(BPF_PROG_TYPE_KPROBE),
			.progs = bit(BPF_PROG_TYPE_XDP),
			.attachs = ~0ULL,
		};

		subtest_userns(&opts, userns_obj_priv_prog);
	}
	if (test__start_subtest("obj_priv_freplace_prog")) {
		struct bpffs_opts opts = {
			.cmds = bit(BPF_BTF_LOAD) | bit(BPF_PROG_LOAD) | bit(BPF_BTF_GET_FD_BY_ID),
			.progs = bit(BPF_PROG_TYPE_EXT) | bit(BPF_PROG_TYPE_XDP),
			.attachs = ~0ULL,
		};
		subtest_userns(&opts, userns_obj_priv_freplace_prog);
	}
	if (test__start_subtest("obj_priv_freplace_prog_fail")) {
		struct bpffs_opts opts = {
			.cmds = bit(BPF_BTF_LOAD) | bit(BPF_PROG_LOAD) | bit(BPF_BTF_GET_FD_BY_ID),
			.progs = bit(BPF_PROG_TYPE_EXT) | bit(BPF_PROG_TYPE_XDP),
			.attachs = ~0ULL,
		};
		subtest_userns(&opts, userns_obj_priv_freplace_prog_fail);
	}
	if (test__start_subtest("obj_priv_btf_fail")) {
		struct bpffs_opts opts = {
			/* disallow BTF loading */
+13 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */

#include "vmlinux.h"
#include <bpf/bpf_helpers.h>

char _license[] SEC("license") = "GPL";

SEC("freplace/xdp_prog1")
int new_xdp_prog2(struct xdp_md *xd)
{
	return XDP_DROP;
}
+3 −3
Original line number Diff line number Diff line
@@ -6,8 +6,8 @@

char _license[] SEC("license") = "GPL";

SEC("kprobe")
int kprobe_prog(void *ctx)
SEC("xdp")
int xdp_prog1(struct xdp_md *xdp)
{
	return 1;
	return XDP_DROP;
}