Function `eval_mul` triggered signed overflow for large constants.

E.g. consider the following program with the current validation code:

    Tested program:
        0:  mov r0, #0x0
        1:  lddw r2, #0x9876543210
        3:  mul r2, #0x12345678  ; tested instruction
        4:  mov r0, #0x1
        5:  exit

With sanitizer the following diagnostic is generated:

    lib/bpf/bpf_validate.c:1032:26: runtime error: signed integer
    overflow: 654820258320 * 305419896 cannot be represented in type
    'long int'
        #0 0x000002746bfd in eval_mul lib/bpf/bpf_validate.c:1032
        #1 0x00000274b6ac in eval_alu lib/bpf/bpf_validate.c:1260
        #2 0x00000275c526 in evaluate lib/bpf/bpf_validate.c:3174
        ...

    SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior
    lib/bpf/bpf_validate.c:1032:26

Multiply constants as unsigned which will produce mathematically correct
result in two's complement representation, 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 3e0493f831ae..b4cb5d8cdf8d 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 multiplication of constant and immediate with overflow. */
+static int
+test_alu64_mul_k_overflow(void)
+{
+       return verify_instruction((struct verify_instruction_param){
+               .tested_instruction = {
+                       .code = (EBPF_ALU64 | BPF_MUL | BPF_K),
+                       .imm = 0x12345678,
+               },
+               .pre.dst = make_singleton_domain(0x9876543210),
+               .post.dst = make_singleton_domain(0x9876543210u * 0x12345678),
+       });
+}
+
+REGISTER_FAST_TEST(bpf_validate_alu64_mul_k_overflow_autotest, NOHUGE_OK, 
ASAN_OK,
+       test_alu64_mul_k_overflow);
+
 /* 64-bit mul of small scalar range and immediate. */
 static int
 test_alu64_mul_k_range_small(void)
diff --git a/lib/bpf/bpf_validate.c b/lib/bpf/bpf_validate.c
index 39c75bbcd76f..a53048801a23 100644
--- a/lib/bpf/bpf_validate.c
+++ b/lib/bpf/bpf_validate.c
@@ -921,8 +921,8 @@ eval_mul(struct bpf_reg_val *rd, const struct bpf_reg_val 
*rs, size_t opsz,
 
        /* both operands are constants */
        if (rd->s.min == rd->s.max && rs->s.min == rs->s.max) {
-               rd->s.min = (rd->s.min * rs->s.min) & msk;
-               rd->s.max = (rd->s.max * rs->s.max) & msk;
+               rd->s.min = ((uint64_t)rd->s.min * (uint64_t)rs->s.min) & msk;
+               rd->s.max = ((uint64_t)rd->s.max * (uint64_t)rs->s.max) & msk;
        /* 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;
-- 
2.43.0

Reply via email to