Function `eval_mul` calculated minimum of the both signed and unsigned
ranges as destination square instead of product with source due to a
typo.

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, #0x11, L8
         3:  jgt r2, #0x1d, L8
         4:  jslt r2, #0x11, L8
         5:  jsgt r2, #0x1d, L8
         6:  mul r2, #0xb  ; tested instruction
         7:  mov r0, #0x1
         8:  exit
     Pre-state:
        r2:  17..29
     Post-state:
        r2:  289..319

After the tested instruction validator considers r2 to be no less than
289, however if 20 was loaded on step 1 it is possible for it after
multiplying by 11 to become 220 which is less than 289.

Fix the typo, add test.

Fixes: 8021917293d0 ("bpf: add extra validation for input BPF program")
Cc: [email protected]

Signed-off-by: Marat Khalili <[email protected]>
---
 app/test/test_bpf_validate.c | 17 +++++++++++++++++
 lib/bpf/bpf_validate.c       |  4 ++--
 2 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/app/test/test_bpf_validate.c b/app/test/test_bpf_validate.c
index aada6e110337..3e0493f831ae 100644
--- a/app/test/test_bpf_validate.c
+++ b/app/test/test_bpf_validate.c
@@ -1289,6 +1289,23 @@ test_alu64_div_mod_overflow(void)
 REGISTER_FAST_TEST(bpf_validate_alu64_div_mod_overflow_autotest, NOHUGE_OK, 
ASAN_OK,
        test_alu64_div_mod_overflow);
 
+/* 64-bit mul of small scalar range and immediate. */
+static int
+test_alu64_mul_k_range_small(void)
+{
+       return verify_instruction((struct verify_instruction_param){
+               .tested_instruction = {
+                       .code = (EBPF_ALU64 | BPF_MUL | BPF_K),
+                       .imm = 11,
+               },
+               .pre.dst = make_unsigned_domain(17, 29),
+               .post.dst = make_unsigned_domain(17 * 11, 29 * 11),
+       });
+}
+
+REGISTER_FAST_TEST(bpf_validate_alu64_mul_k_range_small_autotest, NOHUGE_OK, 
ASAN_OK,
+       test_alu64_mul_k_range_small);
+
 /* 64-bit negation when interval first element is INT64_MIN. */
 static int
 test_alu64_neg_int64min_first(void)
diff --git a/lib/bpf/bpf_validate.c b/lib/bpf/bpf_validate.c
index b784777bbb6b..39c75bbcd76f 100644
--- a/lib/bpf/bpf_validate.c
+++ b/lib/bpf/bpf_validate.c
@@ -915,7 +915,7 @@ eval_mul(struct bpf_reg_val *rd, const struct bpf_reg_val 
*rs, size_t opsz,
        /* check for overflow */
        } else if (rd->u.max <= msk >> opsz / 2 && rs->u.max <= msk >> opsz) {
                rd->u.max *= rs->u.max;
-               rd->u.min *= rd->u.min;
+               rd->u.min *= rs->u.min;
        } else
                eval_umax_bound(rd, msk);
 
@@ -926,7 +926,7 @@ eval_mul(struct bpf_reg_val *rd, const struct bpf_reg_val 
*rs, size_t opsz,
        /* check that both operands are positive and no overflow */
        } else if (rd->s.min >= 0 && rs->s.min >= 0) {
                rd->s.max *= rs->s.max;
-               rd->s.min *= rd->s.min;
+               rd->s.min *= rs->s.min;
        } else
                eval_smax_bound(rd, msk);
 }
-- 
2.43.0

Reply via email to