BPF List
 help / color / mirror / Atom feed
From: Andrii Nakryiko <andrii@kernel.org>
To: <bpf@vger.kernel.org>, <ast@kernel.org>, <daniel@iogearbox.net>,
	<martin.lau@kernel.org>
Cc: <andrii@kernel.org>, <kernel-team@meta.com>,
	Eduard Zingerman <eddyz87@gmail.com>
Subject: [PATCH v6 bpf-next 07/17] bpf: improve deduction of 64-bit bounds from 32-bit bounds
Date: Wed, 1 Nov 2023 20:37:49 -0700	[thread overview]
Message-ID: <20231102033759.2541186-8-andrii@kernel.org> (raw)
In-Reply-To: <20231102033759.2541186-1-andrii@kernel.org>

Add a few interesting cases in which we can tighten 64-bit bounds based
on newly learnt information about 32-bit bounds. E.g., when full u64/s64
registers are used in BPF program, and then eventually compared as
u32/s32. The latter comparison doesn't change the value of full
register, but it does impose new restrictions on possible lower 32 bits
of such full registers. And we can use that to derive additional full
register bounds information.

Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
---
 kernel/bpf/verifier.c | 44 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 08888784cbc8..d0d0a1a1b662 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -2536,10 +2536,54 @@ static void __reg64_deduce_bounds(struct bpf_reg_state *reg)
 	}
 }
 
+static void __reg_deduce_mixed_bounds(struct bpf_reg_state *reg)
+{
+	/* Try to tighten 64-bit bounds from 32-bit knowledge, using 32-bit
+	 * values on both sides of 64-bit range in hope to have tigher range.
+	 * E.g., if r1 is [0x1'00000000, 0x3'80000000], and we learn from
+	 * 32-bit signed > 0 operation that s32 bounds are now [1; 0x7fffffff].
+	 * With this, we can substitute 1 as low 32-bits of _low_ 64-bit bound
+	 * (0x100000000 -> 0x100000001) and 0x7fffffff as low 32-bits of
+	 * _high_ 64-bit bound (0x380000000 -> 0x37fffffff) and arrive at a
+	 * better overall bounds for r1 as [0x1'000000001; 0x3'7fffffff].
+	 * We just need to make sure that derived bounds we are intersecting
+	 * with are well-formed ranges in respecitve s64 or u64 domain, just
+	 * like we do with similar kinds of 32-to-64 or 64-to-32 adjustments.
+	 */
+	__u64 new_umin, new_umax;
+	__s64 new_smin, new_smax;
+
+	/* u32 -> u64 tightening, it's always well-formed */
+	new_umin = (reg->umin_value & ~0xffffffffULL) | reg->u32_min_value;
+	new_umax = (reg->umax_value & ~0xffffffffULL) | reg->u32_max_value;
+	reg->umin_value = max_t(u64, reg->umin_value, new_umin);
+	reg->umax_value = min_t(u64, reg->umax_value, new_umax);
+	/* u32 -> s64 tightening, u32 range embedded into s64 preserves range validity */
+	new_smin = (reg->smin_value & ~0xffffffffULL) | reg->u32_min_value;
+	new_smax = (reg->smax_value & ~0xffffffffULL) | reg->u32_max_value;
+	reg->smin_value = max_t(s64, reg->smin_value, new_smin);
+	reg->smax_value = min_t(s64, reg->smax_value, new_smax);
+
+	/* if s32 can be treated as valid u32 range, we can use it as well */
+	if ((u32)reg->s32_min_value <= (u32)reg->s32_max_value) {
+		/* s32 -> u64 tightening */
+		new_umin = (reg->umin_value & ~0xffffffffULL) | (u32)reg->s32_min_value;
+		new_umax = (reg->umax_value & ~0xffffffffULL) | (u32)reg->s32_max_value;
+		reg->umin_value = max_t(u64, reg->umin_value, new_umin);
+		reg->umax_value = min_t(u64, reg->umax_value, new_umax);
+		/* s32 -> s64 tightening */
+		new_smin = (reg->smin_value & ~0xffffffffULL) | (u32)reg->s32_min_value;
+		new_smax = (reg->smax_value & ~0xffffffffULL) | (u32)reg->s32_max_value;
+		reg->smin_value = max_t(s64, reg->smin_value, new_smin);
+		reg->smax_value = min_t(s64, reg->smax_value, new_smax);
+	}
+}
+
 static void __reg_deduce_bounds(struct bpf_reg_state *reg)
 {
 	__reg32_deduce_bounds(reg);
 	__reg64_deduce_bounds(reg);
+	__reg_deduce_mixed_bounds(reg);
 }
 
 /* Attempts to improve var_off based on unsigned min/max information */
-- 
2.34.1


  parent reply	other threads:[~2023-11-02  3:38 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-11-02  3:37 [PATCH v6 bpf-next 00/17] BPF register bounds logic and testing improvements Andrii Nakryiko
2023-11-02  3:37 ` [PATCH v6 bpf-next 01/17] selftests/bpf: fix RELEASE=1 build for tc_opts Andrii Nakryiko
2023-11-02  3:37 ` [PATCH v6 bpf-next 02/17] selftests/bpf: satisfy compiler by having explicit return in btf test Andrii Nakryiko
2023-11-02  3:37 ` [PATCH v6 bpf-next 03/17] bpf: derive smin/smax from umin/max bounds Andrii Nakryiko
2023-11-02  3:37 ` [PATCH v6 bpf-next 04/17] bpf: derive smin32/smax32 from umin32/umax32 bounds Andrii Nakryiko
2023-11-02  3:37 ` [PATCH v6 bpf-next 05/17] bpf: derive subreg bounds from full bounds when upper 32 bits are constant Andrii Nakryiko
2023-11-02  3:37 ` [PATCH v6 bpf-next 06/17] bpf: add special smin32/smax32 derivation from 64-bit bounds Andrii Nakryiko
2023-11-02  3:37 ` Andrii Nakryiko [this message]
2023-11-02 14:39   ` [PATCH v6 bpf-next 07/17] bpf: improve deduction of 64-bit bounds from 32-bit bounds Shung-Hsi Yu
2023-11-02 16:17     ` Andrii Nakryiko
2023-11-03  3:43       ` Shung-Hsi Yu
2023-11-02  3:37 ` [PATCH v6 bpf-next 08/17] bpf: try harder to deduce register bounds from different numeric domains Andrii Nakryiko
2023-11-02  3:37 ` [PATCH v6 bpf-next 09/17] bpf: drop knowledge-losing __reg_combine_{32,64}_into_{64,32} logic Andrii Nakryiko
2023-11-02 15:14   ` Shung-Hsi Yu
2023-11-02  3:37 ` [PATCH v6 bpf-next 10/17] selftests/bpf: BPF register range bounds tester Andrii Nakryiko
2023-11-02  3:37 ` [PATCH v6 bpf-next 11/17] bpf: rename is_branch_taken reg arguments to prepare for the second one Andrii Nakryiko
2023-11-02 15:15   ` Shung-Hsi Yu
2023-11-02  3:37 ` [PATCH v6 bpf-next 12/17] bpf: generalize is_branch_taken() to work with two registers Andrii Nakryiko
2023-11-02 15:19   ` Shung-Hsi Yu
2023-11-02  3:37 ` [PATCH v6 bpf-next 13/17] bpf: move is_branch_taken() down Andrii Nakryiko
2023-11-02  3:37 ` [PATCH v6 bpf-next 14/17] bpf: generalize is_branch_taken to handle all conditional jumps in one place Andrii Nakryiko
2023-11-02  3:37 ` [PATCH v6 bpf-next 15/17] bpf: unify 32-bit and 64-bit is_branch_taken logic Andrii Nakryiko
2023-11-02  3:37 ` [PATCH v6 bpf-next 16/17] bpf: prepare reg_set_min_max for second set of registers Andrii Nakryiko
2023-11-02  3:37 ` [PATCH v6 bpf-next 17/17] bpf: generalize reg_set_min_max() to handle two sets of two registers Andrii Nakryiko
2023-11-02 16:10 ` [PATCH v6 bpf-next 00/17] BPF register bounds logic and testing improvements patchwork-bot+netdevbpf

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20231102033759.2541186-8-andrii@kernel.org \
    --to=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=eddyz87@gmail.com \
    --cc=kernel-team@meta.com \
    --cc=martin.lau@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox