Function `eval_lsh` when validating left shift by 63 invoked macro
`RTE_LEN2MASK(0, int64_t)` which triggered shift-out-of-bounds 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: jlt r2, #0x3, L8
3: jgt r2, #0x5, L8
4: jslt r2, #0x3, L8
5: jsgt r2, #0x5, L8
6: lsh r2, #0x3f ; tested instruction
7: mov r0, #0x1
8: exit
Pre-state:
r2: 3..5
Post-state:
r2: 0..UINT64_MAX
With sanitizer the following diagnostic is generated:
lib/bpf/bpf_validate.c:785:4: runtime error: shift exponent 64 is
too large for 64-bit type 'long unsigned int'
#0 0x00000274d5e0 in eval_lsh lib/bpf/bpf_validate.c:785
#1 0x00000275a2ea in eval_alu lib/bpf/bpf_validate.c:1310
#2 0x00000276ce3d in evaluate lib/bpf/bpf_validate.c:3284
Add guard for this case, 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 | 3 ++-
2 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/app/test/test_bpf_validate.c b/app/test/test_bpf_validate.c
index 646313cdacf2..64047af44e4a 100644
--- a/app/test/test_bpf_validate.c
+++ b/app/test/test_bpf_validate.c
@@ -1536,6 +1536,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 left shift by 63. */
+static int
+test_alu64_lsh_63(void)
+{
+ return verify_instruction((struct verify_instruction_param){
+ .tested_instruction = {
+ .code = (EBPF_ALU64 | BPF_LSH | BPF_K),
+ .imm = 63,
+ },
+ .pre.dst = make_signed_domain(3, 5),
+ .post.dst = unknown,
+ });
+}
+
+REGISTER_FAST_TEST(bpf_validate_alu64_lsh_63_autotest, NOHUGE_OK, ASAN_OK,
+ test_alu64_lsh_63);
+
/* 64-bit multiplication of constant and immediate with overflow. */
static int
test_alu64_mul_k_overflow(void)
diff --git a/lib/bpf/bpf_validate.c b/lib/bpf/bpf_validate.c
index 4dbf3a3ef892..2c61e5d96a5f 100644
--- a/lib/bpf/bpf_validate.c
+++ b/lib/bpf/bpf_validate.c
@@ -746,7 +746,8 @@ eval_lsh(struct bpf_reg_val *rd, const struct bpf_reg_val
*rs, size_t opsz,
/* check that dreg values are and would remain always positive */
if ((uint64_t)rd->s.min >> (opsz - 1) != 0 || rd->s.max >=
- RTE_LEN2MASK(opsz - rs->u.max - 1, int64_t))
+ (rs->u.max == opsz - 1 ? 0 :
+ RTE_LEN2MASK(opsz - rs->u.max - 1, int64_t)))
eval_smax_bound(rd, msk);
else {
rd->s.max <<= rs->u.max;
--
2.43.0