Skip to content

Commit 4975e9d

Browse files
dkanalievKernel Patches Daemon
authored andcommitted
bpf: verifier: Simplify register sign extension with tnum_scast
This patch refactors the verifier's sign-extension logic for narrow register values to use the new tnum_scast helper. Previously, coerce_reg_to_size_sx and coerce_subreg_to_size_sx employed manual logic to determine bounds, sometimes falling back to loose ranges when sign bits were uncertain. We simplify said logic by delegating the bounds calculation to tnum_scast + the existing bounds synchronization logic: 1. The register's tnum is updated via tnum_scast() 2. The signed bounds (smin/smax) are reset to the maximum theoretical range for the target size. 3. The unsigned bounds are reset to the full register width. 4. __update_reg_bounds() is called. By invoking __update_reg_bounds(), the verifier automatically calculates the intersection between the theoretical signed range and the bitwise info in reg->var_off. This ensures bounds are as tight as possible without requiring custom logic in the coercion functions. This commit also removes set_sext64_default_val() and set_sext32_default_val() as they are no longer used. Signed-off-by: Dimitar Kanaliev <[email protected]>
1 parent 0633681 commit 4975e9d

File tree

1 file changed

+30
-120
lines changed

1 file changed

+30
-120
lines changed

kernel/bpf/verifier.c

Lines changed: 30 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -6878,147 +6878,57 @@ static void coerce_reg_to_size(struct bpf_reg_state *reg, int size)
68786878
reg_bounds_sync(reg);
68796879
}
68806880

6881-
static void set_sext64_default_val(struct bpf_reg_state *reg, int size)
6882-
{
6883-
if (size == 1) {
6884-
reg->smin_value = reg->s32_min_value = S8_MIN;
6885-
reg->smax_value = reg->s32_max_value = S8_MAX;
6886-
} else if (size == 2) {
6887-
reg->smin_value = reg->s32_min_value = S16_MIN;
6888-
reg->smax_value = reg->s32_max_value = S16_MAX;
6889-
} else {
6890-
/* size == 4 */
6891-
reg->smin_value = reg->s32_min_value = S32_MIN;
6892-
reg->smax_value = reg->s32_max_value = S32_MAX;
6893-
}
6894-
reg->umin_value = reg->u32_min_value = 0;
6895-
reg->umax_value = U64_MAX;
6896-
reg->u32_max_value = U32_MAX;
6897-
reg->var_off = tnum_unknown;
6898-
}
6899-
69006881
static void coerce_reg_to_size_sx(struct bpf_reg_state *reg, int size)
69016882
{
6902-
s64 init_s64_max, init_s64_min, s64_max, s64_min, u64_cval;
6903-
u64 top_smax_value, top_smin_value;
6904-
u64 num_bits = size * 8;
6883+
s64 smin_value, smax_value;
69056884

6906-
if (tnum_is_const(reg->var_off)) {
6907-
u64_cval = reg->var_off.value;
6908-
if (size == 1)
6909-
reg->var_off = tnum_const((s8)u64_cval);
6910-
else if (size == 2)
6911-
reg->var_off = tnum_const((s16)u64_cval);
6912-
else
6913-
/* size == 4 */
6914-
reg->var_off = tnum_const((s32)u64_cval);
6915-
6916-
u64_cval = reg->var_off.value;
6917-
reg->smax_value = reg->smin_value = u64_cval;
6918-
reg->umax_value = reg->umin_value = u64_cval;
6919-
reg->s32_max_value = reg->s32_min_value = u64_cval;
6920-
reg->u32_max_value = reg->u32_min_value = u64_cval;
6885+
if (size >= 8)
69216886
return;
6922-
}
69236887

6924-
top_smax_value = ((u64)reg->smax_value >> num_bits) << num_bits;
6925-
top_smin_value = ((u64)reg->smin_value >> num_bits) << num_bits;
6888+
reg->var_off = tnum_scast(reg->var_off, size);
69266889

6927-
if (top_smax_value != top_smin_value)
6928-
goto out;
6890+
smin_value = -(1LL << (size * 8 - 1));
6891+
smax_value = (1LL << (size * 8 - 1)) - 1;
69296892

6930-
/* find the s64_min and s64_min after sign extension */
6931-
if (size == 1) {
6932-
init_s64_max = (s8)reg->smax_value;
6933-
init_s64_min = (s8)reg->smin_value;
6934-
} else if (size == 2) {
6935-
init_s64_max = (s16)reg->smax_value;
6936-
init_s64_min = (s16)reg->smin_value;
6937-
} else {
6938-
init_s64_max = (s32)reg->smax_value;
6939-
init_s64_min = (s32)reg->smin_value;
6940-
}
6941-
6942-
s64_max = max(init_s64_max, init_s64_min);
6943-
s64_min = min(init_s64_max, init_s64_min);
6893+
reg->smin_value = smin_value;
6894+
reg->smax_value = smax_value;
69446895

6945-
/* both of s64_max/s64_min positive or negative */
6946-
if ((s64_max >= 0) == (s64_min >= 0)) {
6947-
reg->s32_min_value = reg->smin_value = s64_min;
6948-
reg->s32_max_value = reg->smax_value = s64_max;
6949-
reg->u32_min_value = reg->umin_value = s64_min;
6950-
reg->u32_max_value = reg->umax_value = s64_max;
6951-
reg->var_off = tnum_range(s64_min, s64_max);
6952-
return;
6953-
}
6896+
reg->s32_min_value = (s32)smin_value;
6897+
reg->s32_max_value = (s32)smax_value;
69546898

6955-
out:
6956-
set_sext64_default_val(reg, size);
6957-
}
6958-
6959-
static void set_sext32_default_val(struct bpf_reg_state *reg, int size)
6960-
{
6961-
if (size == 1) {
6962-
reg->s32_min_value = S8_MIN;
6963-
reg->s32_max_value = S8_MAX;
6964-
} else {
6965-
/* size == 2 */
6966-
reg->s32_min_value = S16_MIN;
6967-
reg->s32_max_value = S16_MAX;
6968-
}
6899+
reg->umin_value = 0;
6900+
reg->umax_value = U64_MAX;
69696901
reg->u32_min_value = 0;
69706902
reg->u32_max_value = U32_MAX;
6971-
reg->var_off = tnum_subreg(tnum_unknown);
6903+
6904+
__update_reg_bounds(reg);
69726905
}
69736906

69746907
static void coerce_subreg_to_size_sx(struct bpf_reg_state *reg, int size)
69756908
{
6976-
s32 init_s32_max, init_s32_min, s32_max, s32_min, u32_val;
6977-
u32 top_smax_value, top_smin_value;
6978-
u32 num_bits = size * 8;
6979-
6980-
if (tnum_is_const(reg->var_off)) {
6981-
u32_val = reg->var_off.value;
6982-
if (size == 1)
6983-
reg->var_off = tnum_const((s8)u32_val);
6984-
else
6985-
reg->var_off = tnum_const((s16)u32_val);
6909+
s32 smin_value, smax_value;
69866910

6987-
u32_val = reg->var_off.value;
6988-
reg->s32_min_value = reg->s32_max_value = u32_val;
6989-
reg->u32_min_value = reg->u32_max_value = u32_val;
6911+
if (size >= 4)
69906912
return;
6991-
}
69926913

6993-
top_smax_value = ((u32)reg->s32_max_value >> num_bits) << num_bits;
6994-
top_smin_value = ((u32)reg->s32_min_value >> num_bits) << num_bits;
6914+
reg->var_off = tnum_subreg(tnum_scast(reg->var_off, size));
69956915

6996-
if (top_smax_value != top_smin_value)
6997-
goto out;
6916+
smin_value = -(1 << (size * 8 - 1));
6917+
smax_value = (1 << (size * 8 - 1)) - 1;
69986918

6999-
/* find the s32_min and s32_min after sign extension */
7000-
if (size == 1) {
7001-
init_s32_max = (s8)reg->s32_max_value;
7002-
init_s32_min = (s8)reg->s32_min_value;
7003-
} else {
7004-
/* size == 2 */
7005-
init_s32_max = (s16)reg->s32_max_value;
7006-
init_s32_min = (s16)reg->s32_min_value;
7007-
}
7008-
s32_max = max(init_s32_max, init_s32_min);
7009-
s32_min = min(init_s32_max, init_s32_min);
7010-
7011-
if ((s32_min >= 0) == (s32_max >= 0)) {
7012-
reg->s32_min_value = s32_min;
7013-
reg->s32_max_value = s32_max;
7014-
reg->u32_min_value = (u32)s32_min;
7015-
reg->u32_max_value = (u32)s32_max;
7016-
reg->var_off = tnum_subreg(tnum_range(s32_min, s32_max));
7017-
return;
7018-
}
6919+
reg->s32_min_value = smin_value;
6920+
reg->s32_max_value = smax_value;
70196921

7020-
out:
7021-
set_sext32_default_val(reg, size);
6922+
reg->u32_min_value = 0;
6923+
reg->u32_max_value = U32_MAX;
6924+
6925+
__update_reg32_bounds(reg);
6926+
6927+
reg->umin_value = reg->u32_min_value;
6928+
reg->umax_value = reg->u32_max_value;
6929+
6930+
reg->smin_value = reg->umin_value;
6931+
reg->smax_value = reg->umax_value;
70226932
}
70236933

70246934
static bool bpf_map_is_rdonly(const struct bpf_map *map)

0 commit comments

Comments
 (0)