Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'bpf-verifier-improve-precision-of-bpf_mul'

Matan Shachnai says:

====================
This patch-set aims to improve precision of BPF_MUL and add testcases
to illustrate precision gains using signed and unsigned bounds.

Changes from v1:
- Fixed typo made in patch.

Changes from v2:
- Added signed multiplication to BPF_MUL.
- Added test cases to exercise BPF_MUL.
- Reordered patches in the series.

Changes from v3:
- Coding style fixes.
====================

Link: https://patch.msgid.link/20241218032337.12214-1-m.shachnai@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

+172 -46
+38 -46
kernel/bpf/verifier.c
··· 14084 14084 static void scalar32_min_max_mul(struct bpf_reg_state *dst_reg, 14085 14085 struct bpf_reg_state *src_reg) 14086 14086 { 14087 - s32 smin_val = src_reg->s32_min_value; 14088 - u32 umin_val = src_reg->u32_min_value; 14089 - u32 umax_val = src_reg->u32_max_value; 14087 + s32 *dst_smin = &dst_reg->s32_min_value; 14088 + s32 *dst_smax = &dst_reg->s32_max_value; 14089 + u32 *dst_umin = &dst_reg->u32_min_value; 14090 + u32 *dst_umax = &dst_reg->u32_max_value; 14091 + s32 tmp_prod[4]; 14090 14092 14091 - if (smin_val < 0 || dst_reg->s32_min_value < 0) { 14092 - /* Ain't nobody got time to multiply that sign */ 14093 - __mark_reg32_unbounded(dst_reg); 14094 - return; 14095 - } 14096 - /* Both values are positive, so we can work with unsigned and 14097 - * copy the result to signed (unless it exceeds S32_MAX). 14098 - */ 14099 - if (umax_val > U16_MAX || dst_reg->u32_max_value > U16_MAX) { 14100 - /* Potential overflow, we know nothing */ 14101 - __mark_reg32_unbounded(dst_reg); 14102 - return; 14103 - } 14104 - dst_reg->u32_min_value *= umin_val; 14105 - dst_reg->u32_max_value *= umax_val; 14106 - if (dst_reg->u32_max_value > S32_MAX) { 14093 + if (check_mul_overflow(*dst_umax, src_reg->u32_max_value, dst_umax) || 14094 + check_mul_overflow(*dst_umin, src_reg->u32_min_value, dst_umin)) { 14107 14095 /* Overflow possible, we know nothing */ 14108 - dst_reg->s32_min_value = S32_MIN; 14109 - dst_reg->s32_max_value = S32_MAX; 14096 + *dst_umin = 0; 14097 + *dst_umax = U32_MAX; 14098 + } 14099 + if (check_mul_overflow(*dst_smin, src_reg->s32_min_value, &tmp_prod[0]) || 14100 + check_mul_overflow(*dst_smin, src_reg->s32_max_value, &tmp_prod[1]) || 14101 + check_mul_overflow(*dst_smax, src_reg->s32_min_value, &tmp_prod[2]) || 14102 + check_mul_overflow(*dst_smax, src_reg->s32_max_value, &tmp_prod[3])) { 14103 + /* Overflow possible, we know nothing */ 14104 + *dst_smin = S32_MIN; 14105 + *dst_smax = S32_MAX; 14110 14106 } else { 14111 - dst_reg->s32_min_value = dst_reg->u32_min_value; 14112 - dst_reg->s32_max_value = dst_reg->u32_max_value; 14107 + *dst_smin = min_array(tmp_prod, 4); 14108 + *dst_smax = max_array(tmp_prod, 4); 14113 14109 } 14114 14110 } 14115 14111 14116 14112 static void scalar_min_max_mul(struct bpf_reg_state *dst_reg, 14117 14113 struct bpf_reg_state *src_reg) 14118 14114 { 14119 - s64 smin_val = src_reg->smin_value; 14120 - u64 umin_val = src_reg->umin_value; 14121 - u64 umax_val = src_reg->umax_value; 14115 + s64 *dst_smin = &dst_reg->smin_value; 14116 + s64 *dst_smax = &dst_reg->smax_value; 14117 + u64 *dst_umin = &dst_reg->umin_value; 14118 + u64 *dst_umax = &dst_reg->umax_value; 14119 + s64 tmp_prod[4]; 14122 14120 14123 - if (smin_val < 0 || dst_reg->smin_value < 0) { 14124 - /* Ain't nobody got time to multiply that sign */ 14125 - __mark_reg64_unbounded(dst_reg); 14126 - return; 14127 - } 14128 - /* Both values are positive, so we can work with unsigned and 14129 - * copy the result to signed (unless it exceeds S64_MAX). 14130 - */ 14131 - if (umax_val > U32_MAX || dst_reg->umax_value > U32_MAX) { 14132 - /* Potential overflow, we know nothing */ 14133 - __mark_reg64_unbounded(dst_reg); 14134 - return; 14135 - } 14136 - dst_reg->umin_value *= umin_val; 14137 - dst_reg->umax_value *= umax_val; 14138 - if (dst_reg->umax_value > S64_MAX) { 14121 + if (check_mul_overflow(*dst_umax, src_reg->umax_value, dst_umax) || 14122 + check_mul_overflow(*dst_umin, src_reg->umin_value, dst_umin)) { 14139 14123 /* Overflow possible, we know nothing */ 14140 - dst_reg->smin_value = S64_MIN; 14141 - dst_reg->smax_value = S64_MAX; 14124 + *dst_umin = 0; 14125 + *dst_umax = U64_MAX; 14126 + } 14127 + if (check_mul_overflow(*dst_smin, src_reg->smin_value, &tmp_prod[0]) || 14128 + check_mul_overflow(*dst_smin, src_reg->smax_value, &tmp_prod[1]) || 14129 + check_mul_overflow(*dst_smax, src_reg->smin_value, &tmp_prod[2]) || 14130 + check_mul_overflow(*dst_smax, src_reg->smax_value, &tmp_prod[3])) { 14131 + /* Overflow possible, we know nothing */ 14132 + *dst_smin = S64_MIN; 14133 + *dst_smax = S64_MAX; 14142 14134 } else { 14143 - dst_reg->smin_value = dst_reg->umin_value; 14144 - dst_reg->smax_value = dst_reg->umax_value; 14135 + *dst_smin = min_array(tmp_prod, 4); 14136 + *dst_smax = max_array(tmp_prod, 4); 14145 14137 } 14146 14138 } 14147 14139
+134
tools/testing/selftests/bpf/progs/verifier_bounds.c
··· 1200 1200 : __clobber_all); 1201 1201 } 1202 1202 1203 + SEC("tc") 1204 + __description("multiply mixed sign bounds. test 1") 1205 + __success __log_level(2) 1206 + __msg("r6 *= r7 {{.*}}; R6_w=scalar(smin=umin=0x1bc16d5cd4927ee1,smax=umax=0x1bc16d674ec80000,smax32=0x7ffffeff,umax32=0xfffffeff,var_off=(0x1bc16d4000000000; 0x3ffffffeff))") 1207 + __naked void mult_mixed0_sign(void) 1208 + { 1209 + asm volatile ( 1210 + "call %[bpf_get_prandom_u32];" 1211 + "r6 = r0;" 1212 + "call %[bpf_get_prandom_u32];" 1213 + "r7 = r0;" 1214 + "r6 &= 0xf;" 1215 + "r6 -= 1000000000;" 1216 + "r7 &= 0xf;" 1217 + "r7 -= 2000000000;" 1218 + "r6 *= r7;" 1219 + "exit" 1220 + : 1221 + : __imm(bpf_get_prandom_u32), 1222 + __imm(bpf_skb_store_bytes) 1223 + : __clobber_all); 1224 + } 1225 + 1226 + SEC("tc") 1227 + __description("multiply mixed sign bounds. test 2") 1228 + __success __log_level(2) 1229 + __msg("r6 *= r7 {{.*}}; R6_w=scalar(smin=smin32=-100,smax=smax32=200)") 1230 + __naked void mult_mixed1_sign(void) 1231 + { 1232 + asm volatile ( 1233 + "call %[bpf_get_prandom_u32];" 1234 + "r6 = r0;" 1235 + "call %[bpf_get_prandom_u32];" 1236 + "r7 = r0;" 1237 + "r6 &= 0xf;" 1238 + "r6 -= 0xa;" 1239 + "r7 &= 0xf;" 1240 + "r7 -= 0x14;" 1241 + "r6 *= r7;" 1242 + "exit" 1243 + : 1244 + : __imm(bpf_get_prandom_u32), 1245 + __imm(bpf_skb_store_bytes) 1246 + : __clobber_all); 1247 + } 1248 + 1249 + SEC("tc") 1250 + __description("multiply negative bounds") 1251 + __success __log_level(2) 1252 + __msg("r6 *= r7 {{.*}}; R6_w=scalar(smin=umin=smin32=umin32=0x3ff280b0,smax=umax=smax32=umax32=0x3fff0001,var_off=(0x3ff00000; 0xf81ff))") 1253 + __naked void mult_sign_bounds(void) 1254 + { 1255 + asm volatile ( 1256 + "r8 = 0x7fff;" 1257 + "call %[bpf_get_prandom_u32];" 1258 + "r6 = r0;" 1259 + "call %[bpf_get_prandom_u32];" 1260 + "r7 = r0;" 1261 + "r6 &= 0xa;" 1262 + "r6 -= r8;" 1263 + "r7 &= 0xf;" 1264 + "r7 -= r8;" 1265 + "r6 *= r7;" 1266 + "exit" 1267 + : 1268 + : __imm(bpf_get_prandom_u32), 1269 + __imm(bpf_skb_store_bytes) 1270 + : __clobber_all); 1271 + } 1272 + 1273 + SEC("tc") 1274 + __description("multiply bounds that don't cross signed boundary") 1275 + __success __log_level(2) 1276 + __msg("r8 *= r6 {{.*}}; R6_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=11,var_off=(0x0; 0xb)) R8_w=scalar(smin=0,smax=umax=0x7b96bb0a94a3a7cd,var_off=(0x0; 0x7fffffffffffffff))") 1277 + __naked void mult_no_sign_crossing(void) 1278 + { 1279 + asm volatile ( 1280 + "r6 = 0xb;" 1281 + "r8 = 0xb3c3f8c99262687 ll;" 1282 + "call %[bpf_get_prandom_u32];" 1283 + "r7 = r0;" 1284 + "r6 &= r7;" 1285 + "r8 *= r6;" 1286 + "exit" 1287 + : 1288 + : __imm(bpf_get_prandom_u32), 1289 + __imm(bpf_skb_store_bytes) 1290 + : __clobber_all); 1291 + } 1292 + 1293 + SEC("tc") 1294 + __description("multiplication overflow, result in unbounded reg. test 1") 1295 + __success __log_level(2) 1296 + __msg("r6 *= r7 {{.*}}; R6_w=scalar()") 1297 + __naked void mult_unsign_ovf(void) 1298 + { 1299 + asm volatile ( 1300 + "r8 = 0x7ffffffffff ll;" 1301 + "call %[bpf_get_prandom_u32];" 1302 + "r6 = r0;" 1303 + "call %[bpf_get_prandom_u32];" 1304 + "r7 = r0;" 1305 + "r6 &= 0x7fffffff;" 1306 + "r7 &= r8;" 1307 + "r6 *= r7;" 1308 + "exit" 1309 + : 1310 + : __imm(bpf_get_prandom_u32), 1311 + __imm(bpf_skb_store_bytes) 1312 + : __clobber_all); 1313 + } 1314 + 1315 + SEC("tc") 1316 + __description("multiplication overflow, result in unbounded reg. test 2") 1317 + __success __log_level(2) 1318 + __msg("r6 *= r7 {{.*}}; R6_w=scalar()") 1319 + __naked void mult_sign_ovf(void) 1320 + { 1321 + asm volatile ( 1322 + "r8 = 0x7ffffffff ll;" 1323 + "call %[bpf_get_prandom_u32];" 1324 + "r6 = r0;" 1325 + "call %[bpf_get_prandom_u32];" 1326 + "r7 = r0;" 1327 + "r6 &= 0xa;" 1328 + "r6 -= r8;" 1329 + "r7 &= 0x7fffffff;" 1330 + "r6 *= r7;" 1331 + "exit" 1332 + : 1333 + : __imm(bpf_get_prandom_u32), 1334 + __imm(bpf_skb_store_bytes) 1335 + : __clobber_all); 1336 + } 1203 1337 char _license[] SEC("license") = "GPL";