Skip to content

Commit d7d7b48

Browse files
pchaignogregkh
authored andcommitted
bpf: Improve bounds when s64 crosses sign boundary
[ Upstream commit 00bf8d0 ] __reg64_deduce_bounds currently improves the s64 range using the u64 range and vice versa, but only if it doesn't cross the sign boundary. This patch improves __reg64_deduce_bounds to cover the case where the s64 range crosses the sign boundary but overlaps with the u64 range on only one end. In that case, we can improve both ranges. Consider the following example, with the s64 range crossing the sign boundary: 0 U64_MAX | [xxxxxxxxxxxxxx u64 range xxxxxxxxxxxxxx] | |----------------------------|----------------------------| |xxxxx s64 range xxxxxxxxx] [xxxxxxx| 0 S64_MAX S64_MIN -1 The u64 range overlaps only with positive portion of the s64 range. We can thus derive the following new s64 and u64 ranges. 0 U64_MAX | [xxxxxx u64 range xxxxx] | |----------------------------|----------------------------| | [xxxxxx s64 range xxxxx] | 0 S64_MAX S64_MIN -1 The same logic can probably apply to the s32/u32 ranges, but this patch doesn't implement that change. In addition to the selftests, the __reg64_deduce_bounds change was also tested with Agni, the formal verification tool for the range analysis [1]. Link: https://github.com/bpfverif/agni [1] Acked-by: Eduard Zingerman <eddyz87@gmail.com> Acked-by: Shung-Hsi Yu <shung-hsi.yu@suse.com> Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com> Link: https://lore.kernel.org/r/933bd9ce1f36ded5559f92fdc09e5dbc823fa245.1753695655.git.paul.chaignon@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 3708bea commit d7d7b48

1 file changed

Lines changed: 52 additions & 0 deletions

File tree

kernel/bpf/verifier.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2129,6 +2129,58 @@ static void __reg64_deduce_bounds(struct bpf_reg_state *reg)
21292129
if ((u64)reg->smin_value <= (u64)reg->smax_value) {
21302130
reg->umin_value = max_t(u64, reg->smin_value, reg->umin_value);
21312131
reg->umax_value = min_t(u64, reg->smax_value, reg->umax_value);
2132+
} else {
2133+
/* If the s64 range crosses the sign boundary, then it's split
2134+
* between the beginning and end of the U64 domain. In that
2135+
* case, we can derive new bounds if the u64 range overlaps
2136+
* with only one end of the s64 range.
2137+
*
2138+
* In the following example, the u64 range overlaps only with
2139+
* positive portion of the s64 range.
2140+
*
2141+
* 0 U64_MAX
2142+
* | [xxxxxxxxxxxxxx u64 range xxxxxxxxxxxxxx] |
2143+
* |----------------------------|----------------------------|
2144+
* |xxxxx s64 range xxxxxxxxx] [xxxxxxx|
2145+
* 0 S64_MAX S64_MIN -1
2146+
*
2147+
* We can thus derive the following new s64 and u64 ranges.
2148+
*
2149+
* 0 U64_MAX
2150+
* | [xxxxxx u64 range xxxxx] |
2151+
* |----------------------------|----------------------------|
2152+
* | [xxxxxx s64 range xxxxx] |
2153+
* 0 S64_MAX S64_MIN -1
2154+
*
2155+
* If they overlap in two places, we can't derive anything
2156+
* because reg_state can't represent two ranges per numeric
2157+
* domain.
2158+
*
2159+
* 0 U64_MAX
2160+
* | [xxxxxxxxxxxxxxxxx u64 range xxxxxxxxxxxxxxxxx] |
2161+
* |----------------------------|----------------------------|
2162+
* |xxxxx s64 range xxxxxxxxx] [xxxxxxxxxx|
2163+
* 0 S64_MAX S64_MIN -1
2164+
*
2165+
* The first condition below corresponds to the first diagram
2166+
* above.
2167+
*/
2168+
if (reg->umax_value < (u64)reg->smin_value) {
2169+
reg->smin_value = (s64)reg->umin_value;
2170+
reg->umax_value = min_t(u64, reg->umax_value, reg->smax_value);
2171+
} else if ((u64)reg->smax_value < reg->umin_value) {
2172+
/* This second condition considers the case where the u64 range
2173+
* overlaps with the negative portion of the s64 range:
2174+
*
2175+
* 0 U64_MAX
2176+
* | [xxxxxxxxxxxxxx u64 range xxxxxxxxxxxxxx] |
2177+
* |----------------------------|----------------------------|
2178+
* |xxxxxxxxx] [xxxxxxxxxxxx s64 range |
2179+
* 0 S64_MAX S64_MIN -1
2180+
*/
2181+
reg->smax_value = (s64)reg->umax_value;
2182+
reg->umin_value = max_t(u64, reg->umin_value, reg->smin_value);
2183+
}
21322184
}
21332185
}
21342186

0 commit comments

Comments
 (0)