Skip to content

Commit 11ff146

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 899d8ba commit 11ff146

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
@@ -6876,147 +6876,57 @@ static void coerce_reg_to_size(struct bpf_reg_state *reg, int size)
68766876
reg_bounds_sync(reg);
68776877
}
68786878

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

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

6922-
top_smax_value = ((u64)reg->smax_value >> num_bits) << num_bits;
6923-
top_smin_value = ((u64)reg->smin_value >> num_bits) << num_bits;
6886+
reg->var_off = tnum_scast(reg->var_off, size);
69246887

6925-
if (top_smax_value != top_smin_value)
6926-
goto out;
6888+
smin_value = -(1LL << (size * 8 - 1));
6889+
smax_value = (1LL << (size * 8 - 1)) - 1;
69276890

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

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

6953-
out:
6954-
set_sext64_default_val(reg, size);
6955-
}
6956-
6957-
static void set_sext32_default_val(struct bpf_reg_state *reg, int size)
6958-
{
6959-
if (size == 1) {
6960-
reg->s32_min_value = S8_MIN;
6961-
reg->s32_max_value = S8_MAX;
6962-
} else {
6963-
/* size == 2 */
6964-
reg->s32_min_value = S16_MIN;
6965-
reg->s32_max_value = S16_MAX;
6966-
}
6897+
reg->umin_value = 0;
6898+
reg->umax_value = U64_MAX;
69676899
reg->u32_min_value = 0;
69686900
reg->u32_max_value = U32_MAX;
6969-
reg->var_off = tnum_subreg(tnum_unknown);
6901+
6902+
__update_reg_bounds(reg);
69706903
}
69716904

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

6985-
u32_val = reg->var_off.value;
6986-
reg->s32_min_value = reg->s32_max_value = u32_val;
6987-
reg->u32_min_value = reg->u32_max_value = u32_val;
6909+
if (size >= 4)
69886910
return;
6989-
}
69906911

6991-
top_smax_value = ((u32)reg->s32_max_value >> num_bits) << num_bits;
6992-
top_smin_value = ((u32)reg->s32_min_value >> num_bits) << num_bits;
6912+
reg->var_off = tnum_subreg(tnum_scast(reg->var_off, size));
69936913

6994-
if (top_smax_value != top_smin_value)
6995-
goto out;
6914+
smin_value = -(1 << (size * 8 - 1));
6915+
smax_value = (1 << (size * 8 - 1)) - 1;
69966916

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

7018-
out:
7019-
set_sext32_default_val(reg, size);
6920+
reg->u32_min_value = 0;
6921+
reg->u32_max_value = U32_MAX;
6922+
6923+
__update_reg32_bounds(reg);
6924+
6925+
reg->umin_value = reg->u32_min_value;
6926+
reg->umax_value = reg->u32_max_value;
6927+
6928+
reg->smin_value = reg->umin_value;
6929+
reg->smax_value = reg->umax_value;
70206930
}
70216931

70226932
static bool bpf_map_is_rdonly(const struct bpf_map *map)

0 commit comments

Comments
 (0)