patch 'bpf: disallow empty program' has been queued to stable release 25.11.1

Kevin Traynor ktraynor at redhat.com
Thu Feb 26 14:09:15 CET 2026


Hi,

FYI, your patch has been queued to stable release 25.11.1

Note it hasn't been pushed to http://dpdk.org/browse/dpdk-stable yet.
It will be pushed if I get no objections before 03/02/26. So please
shout if anyone has objections.

Also note that after the patch there's a diff of the upstream commit vs the
patch applied to the branch. This will indicate if there was any rebasing
needed to apply to the stable branch. If there were code changes for rebasing
(ie: not only metadata diffs), please double check that the rebase was
correctly done.

Queued patches are on a temporary branch at:
https://github.com/kevintraynor/dpdk-stable

This queued commit can be viewed at:
https://github.com/kevintraynor/dpdk-stable/commit/42c977a61a954a4a861a6dfa0551790ab022d60c

Thanks.

Kevin

---
>From 42c977a61a954a4a861a6dfa0551790ab022d60c Mon Sep 17 00:00:00 2001
From: Marat Khalili <marat.khalili at huawei.com>
Date: Tue, 27 Jan 2026 11:49:40 +0000
Subject: [PATCH] bpf: disallow empty program

[ upstream commit cee21cc5be82faef74bb1b8f84407cc92de5dee7 ]

Add tests for some simple cases:
* Program with no instructions;
* Program with only EXIT instruction but no return value set;
* Program with return value set but no EXIT instruction;
* Minimal valid program with return value set and an EXIT instruction.

Fix found bugs:
* a program with no instructions was accepted;
* a program with no EXIT instruction read outside the buffer.

Fixes: 6e12ec4c4d6d ("bpf: add more checks")

Signed-off-by: Marat Khalili <marat.khalili at huawei.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev at huawei.com>
Acked-by: Stephen Hemminger <stephen at networkplumber.org>
---
 app/test/test_bpf.c    | 118 +++++++++++++++++++++++++++++++++++++++++
 lib/bpf/bpf_load.c     |   2 +-
 lib/bpf/bpf_validate.c |  20 +++++--
 3 files changed, 135 insertions(+), 5 deletions(-)

diff --git a/app/test/test_bpf.c b/app/test/test_bpf.c
index 204dd38466..f3ff21f576 100644
--- a/app/test/test_bpf.c
+++ b/app/test/test_bpf.c
@@ -35,4 +35,122 @@ test_bpf(void)
 
 
+/* Tests of most simple BPF programs (no instructions, one instruction etc.) */
+
+/*
+ * Try to load a simple bpf program from the instructions array.
+ *
+ * When `expected_errno` is zero, expect it to load successfully.
+ * When `expected_errno` is non-zero, expect it to fail with this `rte_errno`.
+ *
+ * @param nb_ins
+ *   Number of instructions in the `ins` array.
+ * @param ins
+ *   BPF instructions array.
+ * @param expected_errno
+ *   Expected result.
+ * @return
+ *   TEST_SUCCESS on success, error code on failure.
+ */
+static int
+bpf_load_test(uint32_t nb_ins, const struct ebpf_insn *ins, int expected_errno)
+{
+	const struct rte_bpf_prm prm = {
+		.ins = ins,
+		.nb_ins = nb_ins,
+		.prog_arg = {
+			.type = RTE_BPF_ARG_RAW,
+			.size = sizeof(uint64_t),
+		},
+	};
+
+	struct rte_bpf *const bpf = rte_bpf_load(&prm);
+	const int actual_errno = rte_errno;
+	rte_bpf_destroy(bpf);
+
+	if (expected_errno != 0) {
+		RTE_TEST_ASSERT_EQUAL(bpf, NULL,
+			"expect rte_bpf_load() == NULL");
+		RTE_TEST_ASSERT_EQUAL(actual_errno, expected_errno,
+			"expect rte_errno == %d, found %d",
+			expected_errno, actual_errno);
+	} else
+		RTE_TEST_ASSERT_NOT_EQUAL(bpf, NULL,
+			"expect rte_bpf_load() != NULL");
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Try and load completely empty BPF program.
+ * Should fail because there is no EXIT (and also return value is undefined).
+ */
+static int
+test_no_instructions(void)
+{
+	static const struct ebpf_insn ins[] = {};
+	return bpf_load_test(RTE_DIM(ins), ins, EINVAL);
+}
+
+REGISTER_FAST_TEST(bpf_no_instructions_autotest, NOHUGE_OK, ASAN_OK, test_no_instructions);
+
+/*
+ * Try and load a BPF program comprising single EXIT instruction.
+ * Should fail because the return value is undefined.
+ */
+static int
+test_exit_only(void)
+{
+	static const struct ebpf_insn ins[] = {
+		{
+			.code = (BPF_JMP | EBPF_EXIT),
+		},
+	};
+	return bpf_load_test(RTE_DIM(ins), ins, EINVAL);
+}
+
+REGISTER_FAST_TEST(bpf_exit_only_autotest, NOHUGE_OK, ASAN_OK, test_exit_only);
+
+/*
+ * Try and load a BPF program with no EXIT instruction.
+ * Should fail because of this.
+ */
+static int
+test_no_exit(void)
+{
+	static const struct ebpf_insn ins[] = {
+		{
+			/* Set return value to the program argument. */
+			.code = (EBPF_ALU64 | EBPF_MOV | BPF_X),
+			.src_reg = EBPF_REG_1,
+			.dst_reg = EBPF_REG_0,
+		},
+	};
+	return bpf_load_test(RTE_DIM(ins), ins, EINVAL);
+}
+
+REGISTER_FAST_TEST(bpf_no_exit_autotest, NOHUGE_OK, ASAN_OK, test_no_exit);
+
+/*
+ * Try and load smallest possible valid BPF program.
+ */
+static int
+test_minimal_working(void)
+{
+	static const struct ebpf_insn ins[] = {
+		{
+			/* Set return value to the program argument. */
+			.code = (EBPF_ALU64 | EBPF_MOV | BPF_X),
+			.src_reg = EBPF_REG_1,
+			.dst_reg = EBPF_REG_0,
+		},
+		{
+			.code = (BPF_JMP | EBPF_EXIT),
+		},
+	};
+	return bpf_load_test(RTE_DIM(ins), ins, 0);
+}
+
+REGISTER_FAST_TEST(bpf_minimal_working_autotest, NOHUGE_OK, ASAN_OK, test_minimal_working);
+
 /*
  * Basic functional tests for librte_bpf.
diff --git a/lib/bpf/bpf_load.c b/lib/bpf/bpf_load.c
index 556e613762..6983c026af 100644
--- a/lib/bpf/bpf_load.c
+++ b/lib/bpf/bpf_load.c
@@ -89,5 +89,5 @@ rte_bpf_load(const struct rte_bpf_prm *prm)
 	uint32_t i;
 
-	if (prm == NULL || prm->ins == NULL ||
+	if (prm == NULL || prm->ins == NULL || prm->nb_ins == 0 ||
 			(prm->nb_xsym != 0 && prm->xsym == NULL)) {
 		rte_errno = EINVAL;
diff --git a/lib/bpf/bpf_validate.c b/lib/bpf/bpf_validate.c
index 4f47d6dc7b..23444b3eaa 100644
--- a/lib/bpf/bpf_validate.c
+++ b/lib/bpf/bpf_validate.c
@@ -1828,5 +1828,5 @@ add_edge(struct bpf_verifier *bvf, struct inst_node *node, uint32_t nidx)
 	uint32_t ne;
 
-	if (nidx > bvf->prm->nb_ins) {
+	if (nidx >= bvf->prm->nb_ins) {
 		RTE_BPF_LOG_LINE(ERR,
 			"%s: program boundary violation at pc: %u, next pc: %u",
@@ -1887,4 +1887,5 @@ get_prev_node(struct bpf_verifier *bvf, struct inst_node *node)
  * Information collected at this path would be used later
  * to determine is there any loops, and/or unreachable instructions.
+ * PREREQUISITE: there is at least one node.
  */
 static void
@@ -1893,6 +1894,11 @@ dfs(struct bpf_verifier *bvf)
 	struct inst_node *next, *node;
 
-	node = bvf->in;
-	while (node != NULL) {
+	RTE_ASSERT(bvf->nb_nodes != 0);
+	/*
+	 * Since there is at least one node, node with index 0 always exists;
+	 * it is our program entry point.
+	 */
+	node = &bvf->in[0];
+	do {
 
 		if (node->colour == WHITE)
@@ -1924,5 +1930,5 @@ dfs(struct bpf_verifier *bvf)
 		} else
 			node = NULL;
-	}
+	} while (node != NULL);
 }
 
@@ -2063,4 +2069,10 @@ validate(struct bpf_verifier *bvf)
 		return rc;
 
+	if (bvf->nb_nodes == 0) {
+		RTE_BPF_LOG_LINE(ERR, "%s(%p) the program is empty",
+			__func__, bvf);
+		return -EINVAL;
+	}
+
 	dfs(bvf);
 
-- 
2.53.0

---
  Diff of the applied patch vs upstream commit (please double-check if non-empty:
---
--- -	2026-02-26 10:16:49.988738776 +0000
+++ 0073-bpf-disallow-empty-program.patch	2026-02-26 10:16:47.003459465 +0000
@@ -1 +1 @@
-From cee21cc5be82faef74bb1b8f84407cc92de5dee7 Mon Sep 17 00:00:00 2001
+From 42c977a61a954a4a861a6dfa0551790ab022d60c Mon Sep 17 00:00:00 2001
@@ -5,0 +6,2 @@
+[ upstream commit cee21cc5be82faef74bb1b8f84407cc92de5dee7 ]
+
@@ -17 +18,0 @@
-Cc: stable at dpdk.org
@@ -29 +30 @@
-index 30dae2b87f..2d34137718 100644
+index 204dd38466..f3ff21f576 100644
@@ -167 +168 @@
-index c6f6bfab23..ba03293d17 100644
+index 4f47d6dc7b..23444b3eaa 100644
@@ -170 +171 @@
-@@ -1838,5 +1838,5 @@ add_edge(struct bpf_verifier *bvf, struct inst_node *node, uint32_t nidx)
+@@ -1828,5 +1828,5 @@ add_edge(struct bpf_verifier *bvf, struct inst_node *node, uint32_t nidx)
@@ -177 +178 @@
-@@ -1897,4 +1897,5 @@ get_prev_node(struct bpf_verifier *bvf, struct inst_node *node)
+@@ -1887,4 +1887,5 @@ get_prev_node(struct bpf_verifier *bvf, struct inst_node *node)
@@ -183 +184 @@
-@@ -1903,6 +1904,11 @@ dfs(struct bpf_verifier *bvf)
+@@ -1893,6 +1894,11 @@ dfs(struct bpf_verifier *bvf)
@@ -197 +198 @@
-@@ -1934,5 +1940,5 @@ dfs(struct bpf_verifier *bvf)
+@@ -1924,5 +1930,5 @@ dfs(struct bpf_verifier *bvf)
@@ -204 +205 @@
-@@ -2073,4 +2079,10 @@ validate(struct bpf_verifier *bvf)
+@@ -2063,4 +2069,10 @@ validate(struct bpf_verifier *bvf)



More information about the stable mailing list