From: Konstantin Ananyev <konstantin.ananyev@huawei.com>
To: Marat Khalili <marat.khalili@huawei.com>
Cc: Stephen Hemminger <stephen@networkplumber.org>,
"dev@dpdk.org" <dev@dpdk.org>
Subject: RE: [PATCH 2/3] bpf: disallow empty program
Date: Wed, 12 Nov 2025 15:35:29 +0000 [thread overview]
Message-ID: <7431f0d4eb3e460eb89e75e3da3d9fa3@huawei.com> (raw)
In-Reply-To: <20251110153046.63518-3-marat.khalili@huawei.com>
> 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.
>
> Signed-off-by: Marat Khalili <marat.khalili@huawei.com>
> ---
> app/test/meson.build | 1 +
> app/test/test_bpf_simple.c | 131 +++++++++++++++++++++++++++++++++++++
> lib/bpf/bpf_load.c | 2 +-
> lib/bpf/bpf_validate.c | 20 ++++--
> 4 files changed, 149 insertions(+), 5 deletions(-)
> create mode 100644 app/test/test_bpf_simple.c
>
> diff --git a/app/test/meson.build b/app/test/meson.build
> index 8df8d3edd1..9d48431ba0 100644
> --- a/app/test/meson.build
> +++ b/app/test/meson.build
> @@ -35,6 +35,7 @@ source_file_deps = {
> 'test_bitset.c': [],
> 'test_bitratestats.c': ['metrics', 'bitratestats', 'ethdev'] +
> sample_packet_forward_deps,
> 'test_bpf.c': ['bpf', 'net'],
> + 'test_bpf_simple.c': ['bpf'],
> 'test_byteorder.c': [],
> 'test_cfgfile.c': ['cfgfile'],
> 'test_cksum.c': ['net'],
> diff --git a/app/test/test_bpf_simple.c b/app/test/test_bpf_simple.c
> new file mode 100644
> index 0000000000..576a6ed029
> --- /dev/null
> +++ b/app/test/test_bpf_simple.c
> @@ -0,0 +1,131 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2025 Huawei Technologies Co., Ltd
> + */
> +
> +#include "test.h"
> +
> +#include <rte_bpf.h>
> +#include <rte_errno.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
> +simple_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_simple_no_instructions(void)
> +{
> + static const struct ebpf_insn ins[] = {};
> + return simple_bpf_load_test(RTE_DIM(ins), ins, EINVAL);
> +}
> +
> +REGISTER_FAST_TEST(bpf_simple_no_instructions_autotest, true, true,
> + test_simple_no_instructions);
> +
> +/*
> + * Try and load a BPF program comprising single EXIT instruction.
> + * Should fail because the return value is undefined.
> + */
> +static int
> +test_simple_exit_only(void)
> +{
> + static const struct ebpf_insn ins[] = {
> + {
> + .code = (BPF_JMP | EBPF_EXIT),
> + },
> + };
> + return simple_bpf_load_test(RTE_DIM(ins), ins, EINVAL);
> +}
> +
> +REGISTER_FAST_TEST(bpf_simple_exit_only_autotest, true, true,
> + test_simple_exit_only);
> +
> +/*
> + * Try and load a BPF program with no EXIT instruction.
> + * Should fail because of this.
> + */
> +static int
> +test_simple_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 simple_bpf_load_test(RTE_DIM(ins), ins, EINVAL);
> +}
> +
> +REGISTER_FAST_TEST(bpf_simple_no_exit_autotest, true, true,
> + test_simple_no_exit);
> +
> +/*
> + * Try and load smallest possible valid BPF program.
> + */
> +static int
> +test_simple_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 simple_bpf_load_test(RTE_DIM(ins), ins, 0);
> +}
> +
> +REGISTER_FAST_TEST(bpf_simple_minimal_working_autotest, true, true,
> + test_simple_minimal_working);
> 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
> @@ -88,7 +88,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"
> --
Acked-by: Konstantin Ananyev <konstantin.ananyev@huawei.com>
> 2.43.0
next prev parent reply other threads:[~2025-11-12 15:35 UTC|newest]
Thread overview: 51+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-11-10 15:30 [PATCH 0/3] bpf: simple tests and fixes Marat Khalili
2025-11-10 15:30 ` [PATCH 1/3] bpf: fix signed shift overflows in ARM JIT Marat Khalili
2025-11-11 6:25 ` Jerin Jacob
2025-11-11 7:53 ` Morten Brørup
2025-11-11 10:10 ` Marat Khalili
2025-11-11 16:29 ` Jerin Jacob
2025-11-11 16:31 ` Jerin Jacob
2025-11-11 16:39 ` Marat Khalili
2025-11-12 5:23 ` Jerin Jacob
2025-11-12 10:16 ` Marat Khalili
2025-11-10 15:30 ` [PATCH 2/3] bpf: disallow empty program Marat Khalili
2025-11-10 16:40 ` Stephen Hemminger
2025-11-10 16:46 ` Marat Khalili
2025-11-12 15:35 ` Konstantin Ananyev [this message]
2025-11-10 15:30 ` [PATCH 3/3] bpf: make add/subtract one program validate Marat Khalili
2025-11-12 15:37 ` Konstantin Ananyev
2025-12-16 18:20 ` [PATCH v2 0/5] bpf: simple tests and fixes Marat Khalili
2025-12-16 18:20 ` [PATCH v2 1/5] eal: variable first arguments of RTE_SHIFT_VALxx Marat Khalili
2025-12-17 9:25 ` Morten Brørup
2025-12-16 18:20 ` [PATCH v2 2/5] bpf: fix signed shift overflows in ARM JIT Marat Khalili
2025-12-17 9:49 ` Morten Brørup
2025-12-16 18:20 ` [PATCH v2 3/5] bpf: disallow empty program Marat Khalili
2025-12-18 0:54 ` Stephen Hemminger
2025-12-17 8:58 ` Marat Khalili
2025-12-16 18:20 ` [PATCH v2 4/5] bpf: make add/subtract one program validate Marat Khalili
2025-12-16 18:20 ` [PATCH v2 5/5] bpf: fix BPF validation w/ conditional jump first Marat Khalili
2025-12-17 18:01 ` [PATCH v3 0/6] bpf: simple tests and fixes Marat Khalili
2025-12-17 18:01 ` [PATCH v3 1/6] eal: variable first arguments of RTE_SHIFT_VALxx Marat Khalili
2025-12-19 13:06 ` Konstantin Ananyev
2025-12-17 18:01 ` [PATCH v3 2/6] bpf: fix signed shift overflows in ARM JIT Marat Khalili
2025-12-19 13:13 ` Konstantin Ananyev
2025-12-17 18:01 ` [PATCH v3 3/6] bpf: mark ARM opcodes with UINT32_C Marat Khalili
2025-12-19 13:14 ` Konstantin Ananyev
2025-12-17 18:01 ` [PATCH v3 4/6] bpf: disallow empty program Marat Khalili
2025-12-17 18:01 ` [PATCH v3 5/6] bpf: make add/subtract one program validate Marat Khalili
2025-12-17 18:01 ` [PATCH v3 6/6] bpf: fix BPF validation w/ conditional jump first Marat Khalili
2026-01-08 11:10 ` Konstantin Ananyev
2026-01-14 19:50 ` [PATCH v3 0/6] bpf: simple tests and fixes Stephen Hemminger
2026-01-23 5:22 ` Stephen Hemminger
2026-01-27 11:49 ` [PATCH v4 " Marat Khalili
2026-01-27 11:49 ` [PATCH v4 1/6] eal: variable first arguments of RTE_SHIFT_VALxx Marat Khalili
2026-01-28 0:41 ` fengchengwen
2026-01-27 11:49 ` [PATCH v4 2/6] bpf: fix signed shift overflows in ARM JIT Marat Khalili
2026-01-27 11:49 ` [PATCH v4 3/6] bpf: mark ARM opcodes with UINT32_C Marat Khalili
2026-01-27 11:49 ` [PATCH v4 4/6] bpf: disallow empty program Marat Khalili
2026-01-27 11:49 ` [PATCH v4 5/6] bpf: make add/subtract one program validate Marat Khalili
2026-01-27 11:49 ` [PATCH v4 6/6] bpf: fix validation when conditional jump is first instruction Marat Khalili
2026-01-27 14:02 ` [PATCH v4 0/6] bpf: simple tests and fixes Stephen Hemminger
2026-02-04 11:44 ` Thomas Monjalon
2026-02-04 13:33 ` Konstantin Ananyev
2026-02-04 16:58 ` Thomas Monjalon
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=7431f0d4eb3e460eb89e75e3da3d9fa3@huawei.com \
--to=konstantin.ananyev@huawei.com \
--cc=dev@dpdk.org \
--cc=marat.khalili@huawei.com \
--cc=stephen@networkplumber.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.