patch 'bpf/validate: fix BPF_NEG of INT64_MIN and 0' has been queued to stable release 24.11.7
luca.boccassi at gmail.com
luca.boccassi at gmail.com
Mon Jul 6 12:21:02 CEST 2026
Hi,
FYI, your patch has been queued to stable release 24.11.7
Note it hasn't been pushed to http://dpdk.org/browse/dpdk-stable yet.
It will be pushed if I get no objections before 07/05/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/4602f1c5c67fe9097868adc9cb3d40d3f4be959f
Thanks.
Luca Boccassi
---
>From 4602f1c5c67fe9097868adc9cb3d40d3f4be959f Mon Sep 17 00:00:00 2001
From: Marat Khalili <marat.khalili at huawei.com>
Date: Wed, 24 Jun 2026 13:17:44 +0100
Subject: [PATCH] bpf/validate: fix BPF_NEG of INT64_MIN and 0
[ upstream commit ec4d63bf13fafbc1745bdbb9000c38a3e461fa41 ]
Function `eval_neg` did not treat values of INT64_MIN and 0 specially
when calculating negation ranges (e.g. negated unsigned range 0..2
should turn into 0..UINT64_MAX) producing incorrect results. On top of
this negating signed INT64_MIN caused undefined behaviour.
E.g. consider the following program with the current validation code:
Tested program:
0: mov r0, #0x0
1: ldxdw r2, [r1 + 0]
2: lddw r4, #0x8000000000000000
4: jgt r2, r4, L7
5: neg r2, #0x0 ; tested instruction
6: mov r0, #0x1
7: exit
Pre-state:
r2: 0..0x8000000000000000
Post-state:
r2: INT64_MIN..INT64_MIN+1 INTERSECT 0..0x8000000000000000 (!)
After the tested instruction validator considers r2 to be within
INT64_MIN..INT64_MIN+1 if viewed as signed, or within
0..0x8000000000000000 if viewed as unsigned, however if 1 was loaded on
step 1 into r2 it is possible for it to become -1 after the tested
instruction which satisfies neither of the ranges.
With sanitizer the following diagnostic is generated:
lib/bpf/bpf_validate.c:1120:7: runtime error: negation of
-9223372036854775808 cannot be represented in type 'long int'; cast
to an unsigned type to negate
#0 0x000002747230 in eval_neg lib/bpf/bpf_validate.c:1120
#1 0x000002748fb6 in eval_alu lib/bpf/bpf_validate.c:1251
#2 0x000002759dd3 in evaluate lib/bpf/bpf_validate.c:3161
...
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior
lib/bpf/bpf_validate.c:1120:7
Add missing handling of special cases, add tests.
Fixes: 8021917293d0 ("bpf: add extra validation for input BPF program")
Reported-by: Claudia Cauli <claudiacauli at gmail.com>
Signed-off-by: Marat Khalili <marat.khalili at huawei.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev at huawei.com>
---
.mailmap | 1 +
lib/bpf/bpf_validate.c | 55 ++++++++++++++++++++++++++++++++++++------
2 files changed, 48 insertions(+), 8 deletions(-)
diff --git a/.mailmap b/.mailmap
index 79593ba852..b7badeefb4 100644
--- a/.mailmap
+++ b/.mailmap
@@ -284,6 +284,7 @@ Cian Ferriter <cian.ferriter at intel.com>
Ciara Loftus <ciara.loftus at intel.com>
Ciara Power <ciara.power at intel.com>
Claire Murphy <claire.k.murphy at intel.com>
+Claudia Cauli <claudiacauli at gmail.com>
Clemens Famulla-Conrad <cfamullaconrad at suse.com>
Cody Doucette <doucette at bu.edu>
Congjie Zhou <zcjie0802 at qq.com>
diff --git a/lib/bpf/bpf_validate.c b/lib/bpf/bpf_validate.c
index 4876ab0ed3..b057ac8fa8 100644
--- a/lib/bpf/bpf_validate.c
+++ b/lib/bpf/bpf_validate.c
@@ -582,6 +582,11 @@ eval_neg(struct bpf_reg_val *rd, size_t opsz, uint64_t msk)
{
uint64_t ux, uy;
int64_t sx, sy;
+ /* additional limits imposed by signed on unsigned and back */
+ struct bpf_reg_val cross_limits = {
+ .s = { INT64_MIN, INT64_MAX },
+ .u = { 0, UINT64_MAX },
+ };
/* if we have 32-bit values - extend them to 64-bit */
if (opsz == sizeof(uint32_t) * CHAR_BIT) {
@@ -589,11 +594,29 @@ eval_neg(struct bpf_reg_val *rd, size_t opsz, uint64_t msk)
rd->u.max = (int32_t)rd->u.max;
}
- ux = -(int64_t)rd->u.min & msk;
- uy = -(int64_t)rd->u.max & msk;
+ if (rd->u.min == 0) {
+ /* special case: ranges that include 0 and, possibly, 1 */
- rd->u.max = RTE_MAX(ux, uy);
- rd->u.min = RTE_MIN(ux, uy);
+ /*
+ * Calculate requirements on the signed range of negation.
+ * It is only possible when negated range does not cross from
+ * INT64_MIN to INT64_MAX, which means our original range does
+ * not reach (uint64_t)-INT64_MAX.
+ */
+ if (rd->u.max < (uint64_t)-INT64_MAX) {
+ cross_limits.s.min = -rd->u.max;
+ cross_limits.s.max = -rd->u.min;
+ }
+
+ if (rd->u.max != 0)
+ rd->u.max = UINT64_MAX;
+ } else {
+ ux = -rd->u.min & msk;
+ uy = -rd->u.max & msk;
+
+ rd->u.max = RTE_MAX(ux, uy);
+ rd->u.min = RTE_MIN(ux, uy);
+ }
/* if we have 32-bit values - extend them to 64-bit */
if (opsz == sizeof(uint32_t) * CHAR_BIT) {
@@ -601,11 +624,27 @@ eval_neg(struct bpf_reg_val *rd, size_t opsz, uint64_t msk)
rd->s.max = (int32_t)rd->s.max;
}
- sx = -rd->s.min & msk;
- sy = -rd->s.max & msk;
+ if (rd->s.min == INT64_MIN) {
+ /* special case: negation of INT64_MIN is INT64_MIN */
+ if (rd->s.max <= 0) {
+ cross_limits.u.min = -(uint64_t)rd->s.max;
+ cross_limits.u.max = -(uint64_t)rd->s.min;
+ }
+ if (rd->s.max != INT64_MIN)
+ rd->s.max = INT64_MAX;
+ } else {
+ /* since max >= min, neither can be INT64_MIN here */
+ sx = -rd->s.min & msk;
+ sy = -rd->s.max & msk;
- rd->s.max = RTE_MAX(sx, sy);
- rd->s.min = RTE_MIN(sx, sy);
+ rd->s.max = RTE_MAX(sx, sy);
+ rd->s.min = RTE_MIN(sx, sy);
+ }
+
+ rd->s.min = RTE_MAX(rd->s.min, cross_limits.s.min) & msk;
+ rd->s.max = RTE_MIN(rd->s.max, cross_limits.s.max) & msk;
+ rd->u.min = RTE_MAX(rd->u.min, cross_limits.u.min) & msk;
+ rd->u.max = RTE_MIN(rd->u.max, cross_limits.u.max) & msk;
}
static const char *
--
2.47.3
---
Diff of the applied patch vs upstream commit (please double-check if non-empty:
---
--- - 2026-07-03 12:55:49.334408645 +0100
+++ 0070-bpf-validate-fix-BPF_NEG-of-INT64_MIN-and-0.patch 2026-07-03 12:55:46.718575241 +0100
@@ -1 +1 @@
-From ec4d63bf13fafbc1745bdbb9000c38a3e461fa41 Mon Sep 17 00:00:00 2001
+From 4602f1c5c67fe9097868adc9cb3d40d3f4be959f Mon Sep 17 00:00:00 2001
@@ -5,0 +6,2 @@
+[ upstream commit ec4d63bf13fafbc1745bdbb9000c38a3e461fa41 ]
+
@@ -48 +49,0 @@
-Cc: stable at dpdk.org
@@ -54,4 +55,3 @@
- .mailmap | 1 +
- app/test/test_bpf_validate.c | 126 +++++++++++++++++++++++++++++++++++
- lib/bpf/bpf_validate.c | 55 ++++++++++++---
- 3 files changed, 174 insertions(+), 8 deletions(-)
+ .mailmap | 1 +
+ lib/bpf/bpf_validate.c | 55 ++++++++++++++++++++++++++++++++++++------
+ 2 files changed, 48 insertions(+), 8 deletions(-)
@@ -60 +60 @@
-index 2bc2c2ae33..05a55c0bd6 100644
+index 79593ba852..b7badeefb4 100644
@@ -63 +63 @@
-@@ -299,6 +299,7 @@ Cian Ferriter <cian.ferriter at intel.com>
+@@ -284,6 +284,7 @@ Cian Ferriter <cian.ferriter at intel.com>
@@ -71,137 +70,0 @@
-diff --git a/app/test/test_bpf_validate.c b/app/test/test_bpf_validate.c
-index c2d21a9572..9d7ed6a2ac 100644
---- a/app/test/test_bpf_validate.c
-+++ b/app/test/test_bpf_validate.c
-@@ -1154,6 +1154,132 @@ test_alu64_add_x_scalar_scalar(void)
- REGISTER_FAST_TEST(bpf_validate_alu64_add_x_scalar_scalar_autotest, NOHUGE_OK, ASAN_OK,
- test_alu64_add_x_scalar_scalar);
-
-+/* 64-bit negation when interval first element is INT64_MIN. */
-+static int
-+test_alu64_neg_int64min_first(void)
-+{
-+ static const int64_t other_values[] = {
-+ INT64_MIN,
-+ INT64_MIN + 1,
-+ INT64_MIN + 13,
-+ -17,
-+ -1,
-+ 0,
-+ 1,
-+ 19,
-+ INT64_MAX - 23,
-+ INT64_MAX - 1,
-+ INT64_MAX,
-+ };
-+ for (int other_index = 0; other_index != RTE_DIM(other_values); ++other_index) {
-+ const int64_t other_value = other_values[other_index];
-+ TEST_ASSERT_SUCCESS(verify_instruction((struct verify_instruction_param){
-+ .tested_instruction = {
-+ .code = (EBPF_ALU64 | BPF_NEG),
-+ },
-+ .pre.dst = make_signed_domain(INT64_MIN, other_value),
-+ .post.dst = other_value > 0 ? unknown :
-+ make_unsigned_domain(-(uint64_t)other_value, INT64_MIN),
-+ }), "other_index=%d", other_index);
-+ }
-+ return TEST_SUCCESS;
-+}
-+
-+REGISTER_FAST_TEST(bpf_validate_alu64_neg_int64min_first_autotest, NOHUGE_OK, ASAN_OK,
-+ test_alu64_neg_int64min_first);
-+
-+/* 64-bit negation when interval last element is INT64_MIN. */
-+static int
-+test_alu64_neg_int64min_last(void)
-+{
-+ static const uint64_t other_values[] = {
-+ 0,
-+ 1,
-+ 19,
-+ INT64_MAX - 23,
-+ INT64_MAX - 1,
-+ INT64_MAX,
-+ INT64_MIN,
-+ };
-+ for (int other_index = 0; other_index != RTE_DIM(other_values); ++other_index) {
-+ const int64_t other_value = other_values[other_index];
-+ TEST_ASSERT_SUCCESS(verify_instruction((struct verify_instruction_param){
-+ .tested_instruction = {
-+ .code = (EBPF_ALU64 | BPF_NEG),
-+ },
-+ .pre.dst = make_unsigned_domain(other_value, INT64_MIN),
-+ .post.dst = make_signed_domain(INT64_MIN, -(uint64_t)other_value),
-+ }), "other_index=%d", other_index);
-+ }
-+ return TEST_SUCCESS;
-+}
-+
-+REGISTER_FAST_TEST(bpf_validate_alu64_neg_int64min_last_autotest, NOHUGE_OK, ASAN_OK,
-+ test_alu64_neg_int64min_last);
-+
-+/* 64-bit negation when interval first element is zero. */
-+static int
-+test_alu64_neg_zero_first(void)
-+{
-+ static const uint64_t other_values[] = {
-+ 0,
-+ 1,
-+ 19,
-+ INT64_MAX - 23,
-+ INT64_MAX - 1,
-+ INT64_MAX,
-+ INT64_MIN,
-+ INT64_MIN + 1,
-+ INT64_MIN + 13,
-+ -17,
-+ -1,
-+ };
-+ for (int other_index = 0; other_index != RTE_DIM(other_values); ++other_index) {
-+ const uint64_t other_value = other_values[other_index];
-+ TEST_ASSERT_SUCCESS(verify_instruction((struct verify_instruction_param){
-+ .tested_instruction = {
-+ .code = (EBPF_ALU64 | BPF_NEG),
-+ },
-+ .pre.dst = make_unsigned_domain(0, other_value),
-+ .post.dst = other_value > (uint64_t)INT64_MIN ? unknown :
-+ make_signed_domain(-(uint64_t)other_value, 0),
-+ }), "other_index=%d", other_index);
-+ }
-+ return TEST_SUCCESS;
-+}
-+
-+REGISTER_FAST_TEST(bpf_validate_alu64_neg_zero_first_autotest, NOHUGE_OK, ASAN_OK,
-+ test_alu64_neg_zero_first);
-+
-+/* 64-bit negation when interval last element is zero. */
-+static int
-+test_alu64_neg_zero_last(void)
-+{
-+ static const int64_t other_values[] = {
-+ INT64_MIN,
-+ INT64_MIN + 1,
-+ INT64_MIN + 13,
-+ -17,
-+ -1,
-+ 0,
-+ };
-+ for (int other_index = 0; other_index != RTE_DIM(other_values); ++other_index) {
-+ const int64_t other_value = other_values[other_index];
-+ TEST_ASSERT_SUCCESS(verify_instruction((struct verify_instruction_param){
-+ .tested_instruction = {
-+ .code = (EBPF_ALU64 | BPF_NEG),
-+ },
-+ .pre.dst = make_signed_domain(other_value, 0),
-+ .post.dst = make_unsigned_domain(0, -(uint64_t)other_value),
-+ }), "other_index=%d", other_index);
-+ }
-+
-+ return TEST_SUCCESS;
-+}
-+
-+REGISTER_FAST_TEST(bpf_validate_alu64_neg_zero_last_autotest, NOHUGE_OK, ASAN_OK,
-+ test_alu64_neg_zero_last);
-+
- /* Jump if greater than immediate. */
- static int
- test_jmp64_jeq_k(void)
@@ -209 +72 @@
-index 73e925a7df..38e9b033c6 100644
+index 4876ab0ed3..b057ac8fa8 100644
@@ -212 +75 @@
-@@ -990,6 +990,11 @@ eval_neg(struct bpf_reg_val *rd, size_t opsz, uint64_t msk)
+@@ -582,6 +582,11 @@ eval_neg(struct bpf_reg_val *rd, size_t opsz, uint64_t msk)
@@ -224 +87 @@
-@@ -997,11 +1002,29 @@ eval_neg(struct bpf_reg_val *rd, size_t opsz, uint64_t msk)
+@@ -589,11 +594,29 @@ eval_neg(struct bpf_reg_val *rd, size_t opsz, uint64_t msk)
@@ -258 +121 @@
-@@ -1009,11 +1032,27 @@ eval_neg(struct bpf_reg_val *rd, size_t opsz, uint64_t msk)
+@@ -601,11 +624,27 @@ eval_neg(struct bpf_reg_val *rd, size_t opsz, uint64_t msk)
More information about the stable
mailing list