From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by smtp.lore.kernel.org (Postfix) with ESMTP id 61F06CD4851 for ; Tue, 19 May 2026 09:33:24 +0000 (UTC) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id C6A7640B9D; Tue, 19 May 2026 11:32:10 +0200 (CEST) Received: from frasgout.his.huawei.com (frasgout.his.huawei.com [185.176.79.56]) by mails.dpdk.org (Postfix) with ESMTP id 1D2C24066B; Tue, 19 May 2026 11:31:58 +0200 (CEST) Received: from mail.maildlp.com (unknown [172.18.224.107]) by frasgout.his.huawei.com (SkyGuard) with ESMTPS id 4gKTvX3Kf4zHnH88; Tue, 19 May 2026 17:31:36 +0800 (CST) Received: from frapema500003.china.huawei.com (unknown [7.182.19.114]) by mail.maildlp.com (Postfix) with ESMTPS id EE42D4058B; Tue, 19 May 2026 17:31:57 +0800 (CST) Received: from localhost.localdomain (10.220.239.45) by frapema500003.china.huawei.com (7.182.19.114) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.11; Tue, 19 May 2026 11:31:57 +0200 From: Marat Khalili To: Konstantin Ananyev CC: , , Claudia Cauli Subject: [PATCH v2 15/25] bpf/validate: fix BPF_JGT/EBPF_JSGT no-jump max Date: Tue, 19 May 2026 10:31:18 +0100 Message-ID: <20260519093131.52022-16-marat.khalili@huawei.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260519093131.52022-1-marat.khalili@huawei.com> References: <20260506173846.64914-1-marat.khalili@huawei.com> <20260519093131.52022-1-marat.khalili@huawei.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain X-Originating-IP: [10.220.239.45] X-ClientProxiedBy: frapema500002.china.huawei.com (7.182.19.148) To frapema500003.china.huawei.com (7.182.19.114) X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Functions `eval_jgt_jle` and `eval_jsgt_jsle` reduced range maximum for BPF_JGT and EBPF_JSGT instructions in the no-jump case to the minimum of src register instead of the maximum, producing more conservative estimate that could cause false positives. E.g. consider the following program with the current validation code: Tested program: 0: mov r0, #0x0 1: ldxdw r2, [r1 + 0] 2: jlt r2, #0x14, L15 3: jgt r2, #0x3c, L15 4: jslt r2, #0x14, L15 5: jsgt r2, #0x3c, L15 6: ldxdw r3, [r1 + 8] 7: jlt r3, #0x1e, L15 8: jgt r3, #0x32, L15 9: jslt r3, #0x1e, L15 10: jsgt r3, #0x32, L15 11: jgt r2, r3, L14 ; tested instruction 12: mov r0, #0x1 13: exit 14: mov r0, #0x2 15: exit Pre-state: r2: 20..60 r3: 30..50 Post-state: r2: 20..60 INTERSECT 0x14..0x1e (!) Immediately after the tested instruction on step 12 validator expects r2 to contain values up to 60, for example 55, however for this value jump condition r2 > r3 on step 11 would be always satisfied since r3 is known to not exceed 50, and thus execution will always jump to step 14 instead of continuing to step 12. Fix range calculation, add tests for cases where range of src register values is a strict subset of dst. Other cases will be covered in the subsequent commits. Fixes: 8021917293d0 ("bpf: add extra validation for input BPF program") Cc: stable@dpdk.org Reported-by: Claudia Cauli Signed-off-by: Marat Khalili Acked-by: Konstantin Ananyev --- Depends-on: series-38149 ("bpf: introduce extensible load API") app/test/test_bpf_validate.c | 90 ++++++++++++++++++++++++++++++++++++ lib/bpf/bpf_validate.c | 4 +- 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/app/test/test_bpf_validate.c b/app/test/test_bpf_validate.c index 15cdc83f4f..acc238f7d3 100644 --- a/app/test/test_bpf_validate.c +++ b/app/test/test_bpf_validate.c @@ -1485,6 +1485,96 @@ test_jmp64_jslt_x(void) REGISTER_FAST_TEST(bpf_validate_jmp64_jslt_x_autotest, NOHUGE_OK, ASAN_OK, test_jmp64_jslt_x); +/* Jump on ordering relationship with narrower range. */ +static int +test_jmp64_jxx_x_ordering_narrower(void) +{ + TEST_ASSERT_SUCCESS(verify_instruction((struct verify_instruction_param){ + .tested_instruction = { + .code = (BPF_JMP | BPF_JGT | BPF_X), + }, + .pre.dst = make_signed_domain(20, 60), + .pre.src = make_signed_domain(30, 50), + .post.dst = make_signed_domain(20, 50), + .jump.dst = make_signed_domain(31, 60), + }), "(BPF_JMP | BPF_JGT | BPF_X) check"); + + TEST_ASSERT_SUCCESS(verify_instruction((struct verify_instruction_param){ + .tested_instruction = { + .code = (BPF_JMP | BPF_JGE | BPF_X), + }, + .pre.dst = make_signed_domain(20, 60), + .pre.src = make_signed_domain(30, 50), + .post.dst = make_signed_domain(20, 49), + .jump.dst = make_signed_domain(30, 60), + }), "(BPF_JMP | BPF_JGE | BPF_X) check"); + + TEST_ASSERT_SUCCESS(verify_instruction((struct verify_instruction_param){ + .tested_instruction = { + .code = (BPF_JMP | EBPF_JLT | BPF_X), + }, + .pre.dst = make_signed_domain(20, 60), + .pre.src = make_signed_domain(30, 50), + .post.dst = make_signed_domain(30, 60), + .jump.dst = make_signed_domain(20, 49), + }), "(BPF_JMP | EBPF_JLT | BPF_X) check"); + + TEST_ASSERT_SUCCESS(verify_instruction((struct verify_instruction_param){ + .tested_instruction = { + .code = (BPF_JMP | EBPF_JLE | BPF_X), + }, + .pre.dst = make_signed_domain(20, 60), + .pre.src = make_signed_domain(30, 50), + .post.dst = make_signed_domain(31, 60), + .jump.dst = make_signed_domain(20, 50), + }), "(BPF_JMP | EBPF_JLE | BPF_X) check"); + + TEST_ASSERT_SUCCESS(verify_instruction((struct verify_instruction_param){ + .tested_instruction = { + .code = (BPF_JMP | EBPF_JSGT | BPF_X), + }, + .pre.dst = make_signed_domain(20, 60), + .pre.src = make_signed_domain(30, 50), + .post.dst = make_signed_domain(20, 50), + .jump.dst = make_signed_domain(31, 60), + }), "(BPF_JMP | EBPF_JSGT | BPF_X) check"); + + TEST_ASSERT_SUCCESS(verify_instruction((struct verify_instruction_param){ + .tested_instruction = { + .code = (BPF_JMP | EBPF_JSGE | BPF_X), + }, + .pre.dst = make_signed_domain(20, 60), + .pre.src = make_signed_domain(30, 50), + .post.dst = make_signed_domain(20, 49), + .jump.dst = make_signed_domain(30, 60), + }), "(BPF_JMP | EBPF_JSGE | BPF_X) check"); + + TEST_ASSERT_SUCCESS(verify_instruction((struct verify_instruction_param){ + .tested_instruction = { + .code = (BPF_JMP | EBPF_JSLT | BPF_X), + }, + .pre.dst = make_signed_domain(20, 60), + .pre.src = make_signed_domain(30, 50), + .post.dst = make_signed_domain(30, 60), + .jump.dst = make_signed_domain(20, 49), + }), "(BPF_JMP | EBPF_JSLT | BPF_X) check"); + + TEST_ASSERT_SUCCESS(verify_instruction((struct verify_instruction_param){ + .tested_instruction = { + .code = (BPF_JMP | EBPF_JSLE | BPF_X), + }, + .pre.dst = make_signed_domain(20, 60), + .pre.src = make_signed_domain(30, 50), + .post.dst = make_signed_domain(31, 60), + .jump.dst = make_signed_domain(20, 50), + }), "(BPF_JMP | EBPF_JSLE | BPF_X) check"); + + return TEST_SUCCESS; +} + +REGISTER_FAST_TEST(bpf_validate_jmp64_jxx_x_ordering_narrower_autotest, NOHUGE_OK, ASAN_OK, + test_jmp64_jxx_x_ordering_narrower); + /* 64-bit load from heap (should be set to unknown). */ static int test_mem_ldx_dw_heap(void) diff --git a/lib/bpf/bpf_validate.c b/lib/bpf/bpf_validate.c index a53048801a..ddc468fa0d 100644 --- a/lib/bpf/bpf_validate.c +++ b/lib/bpf/bpf_validate.c @@ -1521,7 +1521,7 @@ static void eval_jgt_jle(struct bpf_reg_val *trd, struct bpf_reg_val *trs, struct bpf_reg_val *frd, struct bpf_reg_val *frs) { - frd->u.max = RTE_MIN(frd->u.max, frs->u.min); + frd->u.max = RTE_MIN(frd->u.max, frs->u.max); trd->u.min = RTE_MAX(trd->u.min, trs->u.min + 1); } @@ -1537,7 +1537,7 @@ static void eval_jsgt_jsle(struct bpf_reg_val *trd, struct bpf_reg_val *trs, struct bpf_reg_val *frd, struct bpf_reg_val *frs) { - frd->s.max = RTE_MIN(frd->s.max, frs->s.min); + frd->s.max = RTE_MIN(frd->s.max, frs->s.max); trd->s.min = RTE_MAX(trd->s.min, trs->s.min + 1); } -- 2.43.0