Commit e9bbda13 authored by Kui-Feng Lee's avatar Kui-Feng Lee Committed by Martin KaFai Lau
Browse files

selftests/bpf: Test case for lacking CFI stub functions.



Ensure struct_ops rejects the registration of struct_ops types without
proper CFI stub functions.

bpf_test_no_cfi.ko is a module that attempts to register a struct_ops type
called "bpf_test_no_cfi_ops" with cfi_stubs of NULL and non-NULL value.
The NULL one should fail, and the non-NULL one should succeed. The module
can only be loaded successfully if these registrations yield the expected
results.

Signed-off-by: default avatarKui-Feng Lee <thinker.li@gmail.com>
Link: https://lore.kernel.org/r/20240222021105.1180475-3-thinker.li@gmail.com


Signed-off-by: default avatarMartin KaFai Lau <martin.lau@kernel.org>
parent 3e000833
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -132,7 +132,7 @@ TEST_GEN_PROGS_EXTENDED = test_sock_addr test_skb_cgroup_id_user \
	flow_dissector_load test_flow_dissector test_tcp_check_syncookie_user \
	test_lirc_mode2_user xdping test_cpp runqslower bench bpf_testmod.ko \
	xskxceiver xdp_redirect_multi xdp_synproxy veristat xdp_hw_metadata \
	xdp_features
	xdp_features bpf_test_no_cfi.ko

TEST_GEN_FILES += liburandom_read.so urandom_read sign-file uprobe_multi

@@ -254,6 +254,12 @@ $(OUTPUT)/bpf_testmod.ko: $(VMLINUX_BTF) $(RESOLVE_BTFIDS) $(wildcard bpf_testmo
	$(Q)$(MAKE) $(submake_extras) RESOLVE_BTFIDS=$(RESOLVE_BTFIDS) -C bpf_testmod
	$(Q)cp bpf_testmod/bpf_testmod.ko $@

$(OUTPUT)/bpf_test_no_cfi.ko: $(VMLINUX_BTF) $(RESOLVE_BTFIDS) $(wildcard bpf_test_no_cfi/Makefile bpf_test_no_cfi/*.[ch])
	$(call msg,MOD,,$@)
	$(Q)$(RM) bpf_test_no_cfi/bpf_test_no_cfi.ko # force re-compilation
	$(Q)$(MAKE) $(submake_extras) RESOLVE_BTFIDS=$(RESOLVE_BTFIDS) -C bpf_test_no_cfi
	$(Q)cp bpf_test_no_cfi/bpf_test_no_cfi.ko $@

DEFAULT_BPFTOOL := $(HOST_SCRATCH_DIR)/sbin/bpftool
ifneq ($(CROSS_COMPILE),)
CROSS_BPFTOOL := $(SCRATCH_DIR)/sbin/bpftool
@@ -631,6 +637,7 @@ TRUNNER_EXTRA_SOURCES := test_progs.c \
			 flow_dissector_load.h	\
			 ip_check_defrag_frags.h
TRUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read $(OUTPUT)/bpf_testmod.ko	\
		       $(OUTPUT)/bpf_test_no_cfi.ko			\
		       $(OUTPUT)/liburandom_read.so			\
		       $(OUTPUT)/xdp_synproxy				\
		       $(OUTPUT)/sign-file				\
@@ -759,6 +766,7 @@ EXTRA_CLEAN := $(SCRATCH_DIR) $(HOST_SCRATCH_DIR) \
	feature bpftool							\
	$(addprefix $(OUTPUT)/,*.o *.skel.h *.lskel.h *.subskel.h	\
			       no_alu32 cpuv4 bpf_gcc bpf_testmod.ko	\
			       bpf_test_no_cfi.ko			\
			       liburandom_read.so)

.PHONY: docs docs-clean
+19 −0
Original line number Diff line number Diff line
BPF_TEST_NO_CFI_DIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
KDIR ?= $(abspath $(BPF_TEST_NO_CFI_DIR)/../../../../..)

ifeq ($(V),1)
Q =
else
Q = @
endif

MODULES = bpf_test_no_cfi.ko

obj-m += bpf_test_no_cfi.o

all:
	+$(Q)make -C $(KDIR) M=$(BPF_TEST_NO_CFI_DIR) modules

clean:
	+$(Q)make -C $(KDIR) M=$(BPF_TEST_NO_CFI_DIR) clean
+84 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
#include <linux/bpf.h>
#include <linux/btf.h>
#include <linux/init.h>
#include <linux/module.h>

struct bpf_test_no_cfi_ops {
	void (*fn_1)(void);
	void (*fn_2)(void);
};

static int dummy_init(struct btf *btf)
{
	return 0;
}

static int dummy_init_member(const struct btf_type *t,
			     const struct btf_member *member,
			     void *kdata, const void *udata)
{
	return 0;
}

static int dummy_reg(void *kdata)
{
	return 0;
}

static void dummy_unreg(void *kdata)
{
}

static const struct bpf_verifier_ops dummy_verifier_ops;

static void bpf_test_no_cfi_ops__fn_1(void)
{
}

static void bpf_test_no_cfi_ops__fn_2(void)
{
}

static struct bpf_test_no_cfi_ops __test_no_cif_ops = {
	.fn_1 = bpf_test_no_cfi_ops__fn_1,
	.fn_2 = bpf_test_no_cfi_ops__fn_2,
};

static struct bpf_struct_ops test_no_cif_ops = {
	.verifier_ops = &dummy_verifier_ops,
	.init = dummy_init,
	.init_member = dummy_init_member,
	.reg = dummy_reg,
	.unreg = dummy_unreg,
	.name = "bpf_test_no_cfi_ops",
	.owner = THIS_MODULE,
};

static int bpf_test_no_cfi_init(void)
{
	int ret;

	ret = register_bpf_struct_ops(&test_no_cif_ops,
				      bpf_test_no_cfi_ops);
	if (!ret)
		return -EINVAL;

	test_no_cif_ops.cfi_stubs = &__test_no_cif_ops;
	ret = register_bpf_struct_ops(&test_no_cif_ops,
				      bpf_test_no_cfi_ops);
	return ret;
}

static void bpf_test_no_cfi_exit(void)
{
}

module_init(bpf_test_no_cfi_init);
module_exit(bpf_test_no_cfi_exit);

MODULE_AUTHOR("Kuifeng Lee");
MODULE_DESCRIPTION("BPF no cfi_stubs test module");
MODULE_LICENSE("Dual BSD/GPL");
+35 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
#include <test_progs.h>
#include <testing_helpers.h>

static void load_bpf_test_no_cfi(void)
{
	int fd;
	int err;

	fd = open("bpf_test_no_cfi.ko", O_RDONLY);
	if (!ASSERT_GE(fd, 0, "open"))
		return;

	/* The module will try to register a struct_ops type without
	 * cfi_stubs and with cfi_stubs.
	 *
	 * The one without cfi_stub should fail. The module will be loaded
	 * successfully only if the result of the registration is as
	 * expected, or it fails.
	 */
	err = finit_module(fd, "", 0);
	close(fd);
	if (!ASSERT_OK(err, "finit_module"))
		return;

	err = delete_module("bpf_test_no_cfi", 0);
	ASSERT_OK(err, "delete_module");
}

void test_struct_ops_no_cfi(void)
{
	if (test__start_subtest("load_bpf_test_no_cfi"))
		load_bpf_test_no_cfi();
}
+2 −2
Original line number Diff line number Diff line
@@ -356,12 +356,12 @@ __u64 read_perf_max_sample_freq(void)
	return sample_freq;
}

static int finit_module(int fd, const char *param_values, int flags)
int finit_module(int fd, const char *param_values, int flags)
{
	return syscall(__NR_finit_module, fd, param_values, flags);
}

static int delete_module(const char *name, int flags)
int delete_module(const char *name, int flags)
{
	return syscall(__NR_delete_module, name, flags);
}
Loading