From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id DA38E47069 for ; Wed, 17 Dec 2025 08:55:39 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id B18ED4026C; Wed, 17 Dec 2025 08:55:39 +0100 (CET) Received: from mail-vs1-f50.google.com (mail-vs1-f50.google.com [209.85.217.50]) by mails.dpdk.org (Postfix) with ESMTP id 86A4A4026C for ; Wed, 17 Dec 2025 08:55:37 +0100 (CET) Received: by mail-vs1-f50.google.com with SMTP id ada2fe7eead31-5dbd1421182so4675819137.1 for ; Tue, 16 Dec 2025 23:55:37 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=networkplumber-org.20230601.gappssmtp.com; s=20230601; t=1765958137; x=1766562937; darn=dpdk.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=qN57d7UyXs8QFhzjdw5IKCyE3RpQrWCsQnqX9DdnDz0=; b=CDNzKAr+DcUL7ducixwVPIH3WJ6shf8IXVoe+OGUFqLDvm246wecjMdLmNHKeMbHs9 E12Wx0NaPErxaYPiPMbva8Vmhcwu1wJXO9bGTUBz3BBFnMuZg4B1CPlP2+1JrzRn8H0V qK25mzVgJ4OlGAs5uqtLsZ5e3yIDz/L1hrCO2vRoAdJguwKKNq3LdEkDwFWMDzGhAJWk TOoQAXzu9kJeObqM/qwUQNS5eBBJkP4B/9LP0s3nEq3arNtP26B3JTFcki8+4M59fT1J O2iIDqr7ZY9nQya0l/AiFFnkrwWh47yYRvV8BFe40FUdHiP4HiF0InEIasYguwPhMBB8 1YPQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1765958137; x=1766562937; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=qN57d7UyXs8QFhzjdw5IKCyE3RpQrWCsQnqX9DdnDz0=; b=EWG7eBAUa+JCTe7+awH4qgByqmb2gLGy7KMDRjQvfeoCDp+xGU4iMT5MMhOuZW89eq FUH4y8i5NVn3UkJweqydpqAGMwNtS3QTGypK1U9kIEy8j70AUTRngRrGHhyR+cNUyM4M OroWPUyhgAzIJ3lp3gFpgyUrTZJO0CRkPlA5ICCsQoIoDEw5WQwlj2hLWzKWLXpMxIfz tOckOdiRY72IF2oCDV9eNCjV6+nkoNTyEw4kahhe0yigS4ezXkhPKU1frfynhjGbp1/z vgZ6gCIhHB6QHTpqY+rIOhrmJWd/cSCILuLusx9E0mI30lvIvzw0VK28AtMTnLXqSWRo H1mg== X-Forwarded-Encrypted: i=1; AJvYcCX+0r5Ju0AvMt8nDQ0UwGmk7BcuR61JIbR6lR6ZjLpamDZS+X5N80v5u7nXXmyQVib5neXGjuE=@dpdk.org X-Gm-Message-State: AOJu0YzmGWirKdksRroM0OiI6avrq9Y07NuBCopWoo3huVx7je3AMAlq GvArcvLixaewu/ZR4VXdfG73HQZPqpqSlEY6c1iTZn01366GB06CxPpLYNUlqp/n6aiUGhQkVPh zOyj2dCS5UaL7il9G8t32Pnkfrg6VGXIhX7SXMzheAA== X-Gm-Gg: AY/fxX41QfYt+OF4r/ohTbUVbK/Dr7222N2oVcLIamG/fyJ8lYLkrhSx1dSX00ugcQc uKBHiIa/9hHUj16ofEdG15lvrXxXfDPeVFaQy4ootVq5XHJuPi+pajpFwMaSHYGSEmjLuOAU1+9 2jJsYy0kB6idBE+8RGdN30qMQi9pZTRE2pf+ekO3vswNXMfZfpwacv7PSgXAWl8tk+CrShjJqdm iEQ37or46eCMLsHGQGMZ+TL1kyD+oQCKLeUxLfVKOId0+D49p4pOFOmaciCVbjhZez0zd5e1Q== X-Google-Smtp-Source: AGHT+IF+PZ51thPpSAmGVwX5uWXJU81enmm1G/WJ3XSSikTwCv2nq0cv+lCCM/alTM0lSZoYAllW8RpIk3gw0E2n93I= X-Received: by 2002:a67:e707:0:b0:5e4:956c:e7ce with SMTP id ada2fe7eead31-5e8274881dcmr5824126137.1.1765958136618; Tue, 16 Dec 2025 23:55:36 -0800 (PST) MIME-Version: 1.0 References: <20251110153046.63518-1-marat.khalili@huawei.com> <20251216182036.77869-1-marat.khalili@huawei.com> <20251216182036.77869-4-marat.khalili@huawei.com> In-Reply-To: <20251216182036.77869-4-marat.khalili@huawei.com> From: Stephen Hemminger Date: Wed, 17 Dec 2025 16:54:06 -0800 X-Gm-Features: AQt7F2o0Xzp3QLAwXsU5Lu6l45NHd6k0e6BH-EJVH78HKFNzU7-Dfx--2ZzUXF8 Message-ID: Subject: Re: [PATCH v2 3/5] bpf: disallow empty program To: Marat Khalili Cc: Jerin Jacob , =?UTF-8?Q?Morten_Br=C3=B8rup?= , Konstantin Ananyev , dev , stable@dpdk.org Content-Type: multipart/alternative; boundary="0000000000006bee490646212bdb" X-BeenThere: stable@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: patches for DPDK stable branches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: stable-bounces@dpdk.org --0000000000006bee490646212bdb Content-Type: text/plain; charset="UTF-8" I see no need for docbook style comments for static functions. Especially in test only code On Tue, Dec 16, 2025, 10:21 Marat Khalili wrote: > 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 > Acked-by: Konstantin Ananyev > --- > 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 b7c94ba1c7..6ecc49efff 100644 > --- a/app/test/test_bpf.c > +++ b/app/test/test_bpf.c > @@ -34,6 +34,124 @@ test_bpf(void) > #include > > > +/* 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, true, true, > 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, true, true, 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, true, true, 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, true, true, > 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 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" > -- > 2.43.0 > > --0000000000006bee490646212bdb Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
I see no need for docbook style comments for static funct= ions.
Especially in test only code

On Tue, Dec 16, 2025, 10:21 Marat Khalili <marat.khalili@huawei.com> wrote:
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>
Acked-by: Konstantin Ananyev <konstantin.ananyev@huawei.com>
---
=C2=A0app/test/test_bpf.c=C2=A0 =C2=A0 | 118 ++++++++++++++++++++++++++++++= +++++++++++
=C2=A0lib/bpf/bpf_load.c=C2=A0 =C2=A0 =C2=A0|=C2=A0 =C2=A02 +-
=C2=A0lib/bpf/bpf_validate.c |=C2=A0 20 +++++--
=C2=A03 files changed, 135 insertions(+), 5 deletions(-)

diff --git a/app/test/test_bpf.c b/app/test/test_bpf.c
index b7c94ba1c7..6ecc49efff 100644
--- a/app/test/test_bpf.c
+++ b/app/test/test_bpf.c
@@ -34,6 +34,124 @@ test_bpf(void)
=C2=A0#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_err= no`.
+ *
+ * @param nb_ins
+ *=C2=A0 =C2=A0Number of instructions in the `ins` array.
+ * @param ins
+ *=C2=A0 =C2=A0BPF instructions array.
+ * @param expected_errno
+ *=C2=A0 =C2=A0Expected result.
+ * @return
+ *=C2=A0 =C2=A0TEST_SUCCESS on success, error code on failure.
+ */
+static int
+bpf_load_test(uint32_t nb_ins, const struct ebpf_insn *ins, int expected_e= rrno)
+{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0const struct rte_bpf_prm prm =3D {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0.ins =3D ins,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0.nb_ins =3D nb_ins,=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0.prog_arg =3D {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0.type =3D RTE_BPF_ARG_RAW,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0.size =3D sizeof(uint64_t),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0},
+=C2=A0 =C2=A0 =C2=A0 =C2=A0};
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0struct rte_bpf *const bpf =3D rte_bpf_load(&= ;prm);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0const int actual_errno =3D rte_errno;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0rte_bpf_destroy(bpf);
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (expected_errno !=3D 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0RTE_TEST_ASSERT_EQU= AL(bpf, NULL,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0"expect rte_bpf_load() =3D=3D NULL");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0RTE_TEST_ASSERT_EQU= AL(actual_errno, expected_errno,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0"expect rte_errno =3D=3D %d, found %d",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0expected_errno, actual_errno);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0} else
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0RTE_TEST_ASSERT_NOT= _EQUAL(bpf, NULL,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0"expect rte_bpf_load() !=3D NULL");
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0return TEST_SUCCESS;
+}
+
+/*
+ * Try and load completely empty BPF program.
+ * Should fail because there is no EXIT (and also return value is undefine= d).
+ */
+static int
+test_no_instructions(void)
+{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0static const struct ebpf_insn ins[] =3D {};
+=C2=A0 =C2=A0 =C2=A0 =C2=A0return bpf_load_test(RTE_DIM(ins), ins, EINVAL)= ;
+}
+
+REGISTER_FAST_TEST(bpf_no_instructions_autotest, true, true, test_no_instr= uctions);
+
+/*
+ * Try and load a BPF program comprising single EXIT instruction.
+ * Should fail because the return value is undefined.
+ */
+static int
+test_exit_only(void)
+{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0static const struct ebpf_insn ins[] =3D {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0.code =3D (BPF_JMP | EBPF_EXIT),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0},
+=C2=A0 =C2=A0 =C2=A0 =C2=A0};
+=C2=A0 =C2=A0 =C2=A0 =C2=A0return bpf_load_test(RTE_DIM(ins), ins, EINVAL)= ;
+}
+
+REGISTER_FAST_TEST(bpf_exit_only_autotest, true, true, test_exit_only); +
+/*
+ * Try and load a BPF program with no EXIT instruction.
+ * Should fail because of this.
+ */
+static int
+test_no_exit(void)
+{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0static const struct ebpf_insn ins[] =3D {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0/* Set return value to the program argument. */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0.code =3D (EBPF_ALU64 | EBPF_MOV | BPF_X),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0.src_reg =3D EBPF_REG_1,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0.dst_reg =3D EBPF_REG_0,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0},
+=C2=A0 =C2=A0 =C2=A0 =C2=A0};
+=C2=A0 =C2=A0 =C2=A0 =C2=A0return bpf_load_test(RTE_DIM(ins), ins, EINVAL)= ;
+}
+
+REGISTER_FAST_TEST(bpf_no_exit_autotest, true, true, test_no_exit);
+
+/*
+ * Try and load smallest possible valid BPF program.
+ */
+static int
+test_minimal_working(void)
+{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0static const struct ebpf_insn ins[] =3D {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0/* Set return value to the program argument. */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0.code =3D (EBPF_ALU64 | EBPF_MOV | BPF_X),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0.src_reg =3D EBPF_REG_1,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0.dst_reg =3D EBPF_REG_0,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0},
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0.code =3D (BPF_JMP | EBPF_EXIT),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0},
+=C2=A0 =C2=A0 =C2=A0 =C2=A0};
+=C2=A0 =C2=A0 =C2=A0 =C2=A0return bpf_load_test(RTE_DIM(ins), ins, 0);
+}
+
+REGISTER_FAST_TEST(bpf_minimal_working_autotest, true, true, test_minimal_= working);
+
=C2=A0/*
=C2=A0 * Basic functional tests for librte_bpf.
=C2=A0 * The main procedure - load eBPF program, execute it and
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)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 int32_t rc;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 uint32_t i;

-=C2=A0 =C2=A0 =C2=A0 =C2=A0if (prm =3D=3D NULL || prm->ins =3D=3D NULL = ||
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (prm =3D=3D NULL || prm->ins =3D=3D NULL = || prm->nb_ins =3D=3D 0 ||
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 (prm->nb_xsym !=3D 0 && prm->xsym =3D=3D NULL)) {<= br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 rte_errno =3D EINVA= L;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 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)
=C2=A0{
=C2=A0 =C2=A0 =C2=A0 =C2=A0 uint32_t ne;

-=C2=A0 =C2=A0 =C2=A0 =C2=A0if (nidx > bvf->prm->nb_ins) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (nidx >=3D bvf->prm->nb_ins) {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 RTE_BPF_LOG_LINE(ER= R,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 "%s: program boundary violation at pc: %u, next pc: %u"= ;,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 __func__, get_node_idx(bvf, node), nidx);
@@ -1886,14 +1886,20 @@ get_prev_node(struct bpf_verifier *bvf, struct inst= _node *node)
=C2=A0 * Control Flow Graph (CFG).
=C2=A0 * Information collected at this path would be used later
=C2=A0 * to determine is there any loops, and/or unreachable instructions.<= br> + * PREREQUISITE: there is at least one node.
=C2=A0 */
=C2=A0static void
=C2=A0dfs(struct bpf_verifier *bvf)
=C2=A0{
=C2=A0 =C2=A0 =C2=A0 =C2=A0 struct inst_node *next, *node;

-=C2=A0 =C2=A0 =C2=A0 =C2=A0node =3D bvf->in;
-=C2=A0 =C2=A0 =C2=A0 =C2=A0while (node !=3D NULL) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0RTE_ASSERT(bvf->nb_nodes !=3D 0);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0/*
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 * Since there is at least one node, node with = index 0 always exists;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 * it is our program entry point.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0node =3D &bvf->in[0];
+=C2=A0 =C2=A0 =C2=A0 =C2=A0do {

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (node->colour= =3D=3D WHITE)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 set_node_colour(bvf, node, GREY);
@@ -1923,7 +1929,7 @@ dfs(struct bpf_verifier *bvf)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 }
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 } else
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 node =3D NULL;
-=C2=A0 =C2=A0 =C2=A0 =C2=A0}
+=C2=A0 =C2=A0 =C2=A0 =C2=A0} while (node !=3D NULL);
=C2=A0}

=C2=A0/*
@@ -2062,6 +2068,12 @@ validate(struct bpf_verifier *bvf)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (rc !=3D 0)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return rc;

+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (bvf->nb_nodes =3D=3D 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0RTE_BPF_LOG_LINE(ER= R, "%s(%p) the program is empty",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0__func__, bvf);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return -EINVAL;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0}
+
=C2=A0 =C2=A0 =C2=A0 =C2=A0 dfs(bvf);

=C2=A0 =C2=A0 =C2=A0 =C2=A0 RTE_LOG(DEBUG, BPF, "%s(%p) stats:\n"=
--
2.43.0

--0000000000006bee490646212bdb--