patch 'bpf: disallow empty program' has been queued to stable release 24.11.5
luca.boccassi at gmail.com
luca.boccassi at gmail.com
Fri Feb 20 15:56:04 CET 2026
Hi,
FYI, your patch has been queued to stable release 24.11.5
Note it hasn't been pushed to http://dpdk.org/browse/dpdk-stable yet.
It will be pushed if I get no objections before 02/22/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/bluca/dpdk-stable
This queued commit can be viewed at:
https://github.com/bluca/dpdk-stable/commit/39cee2c883c0638e4a575433b7f249e6fb97e754
Thanks.
Luca Boccassi
---
>From 39cee2c883c0638e4a575433b7f249e6fb97e754 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 ebe575fd14..098e4b513f 100644
--- a/app/test/test_bpf.c
+++ b/app/test/test_bpf.c
@@ -32,6 +32,124 @@ test_bpf(void)
#include <rte_ip.h>
+/* 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.
* The main procedure - load eBPF program, execute it and
diff --git a/lib/bpf/bpf_load.c b/lib/bpf/bpf_load.c
index de43347405..6151e9545f 100644
--- a/lib/bpf/bpf_load.c
+++ b/lib/bpf/bpf_load.c
@@ -86,7 +86,7 @@ rte_bpf_load(const struct rte_bpf_prm *prm)
int32_t rc;
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;
return NULL;
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
@@ -1827,7 +1827,7 @@ 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",
__func__, get_node_idx(bvf, node), nidx);
@@ -1886,14 +1886,20 @@ get_prev_node(struct bpf_verifier *bvf, struct inst_node *node)
* Control Flow Graph (CFG).
* 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
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)
set_node_colour(bvf, node, GREY);
@@ -1923,7 +1929,7 @@ dfs(struct bpf_verifier *bvf)
}
} else
node = NULL;
- }
+ } while (node != NULL);
}
/*
@@ -2062,6 +2068,12 @@ validate(struct bpf_verifier *bvf)
if (rc != 0)
return rc;
+ if (bvf->nb_nodes == 0) {
+ RTE_BPF_LOG_LINE(ERR, "%s(%p) the program is empty",
+ __func__, bvf);
+ return -EINVAL;
+ }
+
dfs(bvf);
RTE_LOG(DEBUG, BPF, "%s(%p) stats:\n"
--
2.47.3
---
Diff of the applied patch vs upstream commit (please double-check if non-empty:
---
--- - 2026-02-20 14:55:45.642059526 +0000
+++ 0062-bpf-disallow-empty-program.patch 2026-02-20 14:55:43.248191821 +0000
@@ -1 +1 @@
-From cee21cc5be82faef74bb1b8f84407cc92de5dee7 Mon Sep 17 00:00:00 2001
+From 39cee2c883c0638e4a575433b7f249e6fb97e754 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 ebe575fd14..098e4b513f 100644
@@ -32 +33 @@
-@@ -34,6 +34,124 @@ test_bpf(void)
+@@ -32,6 +32,124 @@ test_bpf(void)
@@ -158 +159 @@
-index 556e613762..6983c026af 100644
+index de43347405..6151e9545f 100644
@@ -161 +162 @@
-@@ -88,7 +88,7 @@ rte_bpf_load(const struct rte_bpf_prm *prm)
+@@ -86,7 +86,7 @@ rte_bpf_load(const struct rte_bpf_prm *prm)
@@ -171 +172 @@
-index c6f6bfab23..ba03293d17 100644
+index 4f47d6dc7b..23444b3eaa 100644
@@ -174 +175 @@
-@@ -1837,7 +1837,7 @@ add_edge(struct bpf_verifier *bvf, struct inst_node *node, uint32_t nidx)
+@@ -1827,7 +1827,7 @@ add_edge(struct bpf_verifier *bvf, struct inst_node *node, uint32_t nidx)
@@ -183 +184 @@
-@@ -1896,14 +1896,20 @@ get_prev_node(struct bpf_verifier *bvf, struct inst_node *node)
+@@ -1886,14 +1886,20 @@ get_prev_node(struct bpf_verifier *bvf, struct inst_node *node)
@@ -206 +207 @@
-@@ -1933,7 +1939,7 @@ dfs(struct bpf_verifier *bvf)
+@@ -1923,7 +1929,7 @@ dfs(struct bpf_verifier *bvf)
@@ -215 +216 @@
-@@ -2072,6 +2078,12 @@ validate(struct bpf_verifier *bvf)
+@@ -2062,6 +2068,12 @@ validate(struct bpf_verifier *bvf)
More information about the stable
mailing list