@@ -4263,40 +4263,51 @@ static struct bpf_insn_aux_data *cur_aux(struct bpf_verifier_env *env)
42634263 return & env -> insn_aux_data [env -> insn_idx ];
42644264}
42654265
4266+ enum {
4267+ REASON_BOUNDS = -1 ,
4268+ REASON_TYPE = -2 ,
4269+ REASON_PATHS = -3 ,
4270+ REASON_LIMIT = -4 ,
4271+ REASON_STACK = -5 ,
4272+ };
4273+
42664274static int retrieve_ptr_limit (const struct bpf_reg_state * ptr_reg ,
4267- u32 * ptr_limit , u8 opcode , bool off_is_neg )
4275+ const struct bpf_reg_state * off_reg ,
4276+ u32 * alu_limit , u8 opcode )
42684277{
4278+ bool off_is_neg = off_reg -> smin_value < 0 ;
42694279 bool mask_to_left = (opcode == BPF_ADD && off_is_neg ) ||
42704280 (opcode == BPF_SUB && !off_is_neg );
4271- u32 off , max ;
4281+ u32 max = 0 , ptr_limit = 0 ;
4282+
4283+ if (!tnum_is_const (off_reg -> var_off ) &&
4284+ (off_reg -> smin_value < 0 ) != (off_reg -> smax_value < 0 ))
4285+ return REASON_BOUNDS ;
42724286
42734287 switch (ptr_reg -> type ) {
42744288 case PTR_TO_STACK :
42754289 /* Offset 0 is out-of-bounds, but acceptable start for the
4276- * left direction, see BPF_REG_FP.
4290+ * left direction, see BPF_REG_FP. Also, unknown scalar
4291+ * offset where we would need to deal with min/max bounds is
4292+ * currently prohibited for unprivileged.
42774293 */
42784294 max = MAX_BPF_STACK + mask_to_left ;
4279- /* Indirect variable offset stack access is prohibited in
4280- * unprivileged mode so it's not handled here.
4281- */
4282- off = ptr_reg -> off + ptr_reg -> var_off .value ;
4283- if (mask_to_left )
4284- * ptr_limit = MAX_BPF_STACK + off ;
4285- else
4286- * ptr_limit = - off - 1 ;
4287- return * ptr_limit >= max ? - ERANGE : 0 ;
4295+ ptr_limit = - (ptr_reg -> var_off .value + ptr_reg -> off );
4296+ break ;
42884297 case PTR_TO_MAP_VALUE :
42894298 max = ptr_reg -> map_ptr -> value_size ;
4290- if (mask_to_left ) {
4291- * ptr_limit = ptr_reg -> umax_value + ptr_reg -> off ;
4292- } else {
4293- off = ptr_reg -> smin_value + ptr_reg -> off ;
4294- * ptr_limit = ptr_reg -> map_ptr -> value_size - off - 1 ;
4295- }
4296- return * ptr_limit >= max ? - ERANGE : 0 ;
4299+ ptr_limit = (mask_to_left ?
4300+ ptr_reg -> smin_value :
4301+ ptr_reg -> umax_value ) + ptr_reg -> off ;
4302+ break ;
42974303 default :
4298- return - EINVAL ;
4304+ return REASON_TYPE ;
42994305 }
4306+
4307+ if (ptr_limit >= max )
4308+ return REASON_LIMIT ;
4309+ * alu_limit = ptr_limit ;
4310+ return 0 ;
43004311}
43014312
43024313static bool can_skip_alu_sanitation (const struct bpf_verifier_env * env ,
@@ -4314,7 +4325,7 @@ static int update_alu_sanitation_state(struct bpf_insn_aux_data *aux,
43144325 if (aux -> alu_state &&
43154326 (aux -> alu_state != alu_state ||
43164327 aux -> alu_limit != alu_limit ))
4317- return - EACCES ;
4328+ return REASON_PATHS ;
43184329
43194330 /* Corresponding fixup done in fixup_bpf_calls(). */
43204331 aux -> alu_state = alu_state ;
@@ -4333,14 +4344,22 @@ static int sanitize_val_alu(struct bpf_verifier_env *env,
43334344 return update_alu_sanitation_state (aux , BPF_ALU_NON_POINTER , 0 );
43344345}
43354346
4347+ static bool sanitize_needed (u8 opcode )
4348+ {
4349+ return opcode == BPF_ADD || opcode == BPF_SUB ;
4350+ }
4351+
43364352static int sanitize_ptr_alu (struct bpf_verifier_env * env ,
43374353 struct bpf_insn * insn ,
43384354 const struct bpf_reg_state * ptr_reg ,
4355+ const struct bpf_reg_state * off_reg ,
43394356 struct bpf_reg_state * dst_reg ,
4340- bool off_is_neg )
4357+ struct bpf_insn_aux_data * tmp_aux ,
4358+ const bool commit_window )
43414359{
4360+ struct bpf_insn_aux_data * aux = commit_window ? cur_aux (env ) : tmp_aux ;
43424361 struct bpf_verifier_state * vstate = env -> cur_state ;
4343- struct bpf_insn_aux_data * aux = cur_aux ( env ) ;
4362+ bool off_is_neg = off_reg -> smin_value < 0 ;
43444363 bool ptr_is_dst_reg = ptr_reg == dst_reg ;
43454364 u8 opcode = BPF_OP (insn -> code );
43464365 u32 alu_state , alu_limit ;
@@ -4358,18 +4377,33 @@ static int sanitize_ptr_alu(struct bpf_verifier_env *env,
43584377 if (vstate -> speculative )
43594378 goto do_sim ;
43604379
4361- alu_state = off_is_neg ? BPF_ALU_NEG_VALUE : 0 ;
4362- alu_state |= ptr_is_dst_reg ?
4363- BPF_ALU_SANITIZE_SRC : BPF_ALU_SANITIZE_DST ;
4364-
4365- err = retrieve_ptr_limit (ptr_reg , & alu_limit , opcode , off_is_neg );
4380+ err = retrieve_ptr_limit (ptr_reg , off_reg , & alu_limit , opcode );
43664381 if (err < 0 )
43674382 return err ;
43684383
4384+ if (commit_window ) {
4385+ /* In commit phase we narrow the masking window based on
4386+ * the observed pointer move after the simulated operation.
4387+ */
4388+ alu_state = tmp_aux -> alu_state ;
4389+ alu_limit = abs (tmp_aux -> alu_limit - alu_limit );
4390+ } else {
4391+ alu_state = off_is_neg ? BPF_ALU_NEG_VALUE : 0 ;
4392+ alu_state |= ptr_is_dst_reg ?
4393+ BPF_ALU_SANITIZE_SRC : BPF_ALU_SANITIZE_DST ;
4394+ }
4395+
43694396 err = update_alu_sanitation_state (aux , alu_state , alu_limit );
43704397 if (err < 0 )
43714398 return err ;
43724399do_sim :
4400+ /* If we're in commit phase, we're done here given we already
4401+ * pushed the truncated dst_reg into the speculative verification
4402+ * stack.
4403+ */
4404+ if (commit_window )
4405+ return 0 ;
4406+
43734407 /* Simulate and find potential out-of-bounds access under
43744408 * speculative execution from truncation as a result of
43754409 * masking when off was not within expected range. If off
@@ -4386,7 +4420,81 @@ static int sanitize_ptr_alu(struct bpf_verifier_env *env,
43864420 ret = push_stack (env , env -> insn_idx + 1 , env -> insn_idx , true);
43874421 if (!ptr_is_dst_reg && ret )
43884422 * dst_reg = tmp ;
4389- return !ret ? - EFAULT : 0 ;
4423+ return !ret ? REASON_STACK : 0 ;
4424+ }
4425+
4426+ static int sanitize_err (struct bpf_verifier_env * env ,
4427+ const struct bpf_insn * insn , int reason ,
4428+ const struct bpf_reg_state * off_reg ,
4429+ const struct bpf_reg_state * dst_reg )
4430+ {
4431+ static const char * err = "pointer arithmetic with it prohibited for !root" ;
4432+ const char * op = BPF_OP (insn -> code ) == BPF_ADD ? "add" : "sub" ;
4433+ u32 dst = insn -> dst_reg , src = insn -> src_reg ;
4434+
4435+ switch (reason ) {
4436+ case REASON_BOUNDS :
4437+ verbose (env , "R%d has unknown scalar with mixed signed bounds, %s\n" ,
4438+ off_reg == dst_reg ? dst : src , err );
4439+ break ;
4440+ case REASON_TYPE :
4441+ verbose (env , "R%d has pointer with unsupported alu operation, %s\n" ,
4442+ off_reg == dst_reg ? src : dst , err );
4443+ break ;
4444+ case REASON_PATHS :
4445+ verbose (env , "R%d tried to %s from different maps, paths or scalars, %s\n" ,
4446+ dst , op , err );
4447+ break ;
4448+ case REASON_LIMIT :
4449+ verbose (env , "R%d tried to %s beyond pointer bounds, %s\n" ,
4450+ dst , op , err );
4451+ break ;
4452+ case REASON_STACK :
4453+ verbose (env , "R%d could not be pushed for speculative verification, %s\n" ,
4454+ dst , err );
4455+ break ;
4456+ default :
4457+ verbose (env , "verifier internal error: unknown reason (%d)\n" ,
4458+ reason );
4459+ break ;
4460+ }
4461+
4462+ return - EACCES ;
4463+ }
4464+
4465+ static int sanitize_check_bounds (struct bpf_verifier_env * env ,
4466+ const struct bpf_insn * insn ,
4467+ const struct bpf_reg_state * dst_reg )
4468+ {
4469+ u32 dst = insn -> dst_reg ;
4470+
4471+ /* For unprivileged we require that resulting offset must be in bounds
4472+ * in order to be able to sanitize access later on.
4473+ */
4474+ if (env -> allow_ptr_leaks )
4475+ return 0 ;
4476+
4477+ switch (dst_reg -> type ) {
4478+ case PTR_TO_STACK :
4479+ if (check_stack_access (env , dst_reg , dst_reg -> off +
4480+ dst_reg -> var_off .value , 1 )) {
4481+ verbose (env , "R%d stack pointer arithmetic goes out of range, "
4482+ "prohibited for !root\n" , dst );
4483+ return - EACCES ;
4484+ }
4485+ break ;
4486+ case PTR_TO_MAP_VALUE :
4487+ if (check_map_access (env , dst , dst_reg -> off , 1 , false)) {
4488+ verbose (env , "R%d pointer arithmetic of map value goes out of range, "
4489+ "prohibited for !root\n" , dst );
4490+ return - EACCES ;
4491+ }
4492+ break ;
4493+ default :
4494+ break ;
4495+ }
4496+
4497+ return 0 ;
43904498}
43914499
43924500/* Handles arithmetic on a pointer and a scalar: computes new min/max and var_off.
@@ -4407,8 +4515,9 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
44074515 smin_ptr = ptr_reg -> smin_value , smax_ptr = ptr_reg -> smax_value ;
44084516 u64 umin_val = off_reg -> umin_value , umax_val = off_reg -> umax_value ,
44094517 umin_ptr = ptr_reg -> umin_value , umax_ptr = ptr_reg -> umax_value ;
4410- u32 dst = insn -> dst_reg , src = insn -> src_reg ;
4518+ struct bpf_insn_aux_data tmp_aux = {} ;
44114519 u8 opcode = BPF_OP (insn -> code );
4520+ u32 dst = insn -> dst_reg ;
44124521 int ret ;
44134522
44144523 dst_reg = & regs [dst ];
@@ -4451,13 +4560,6 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
44514560 verbose (env , "R%d pointer arithmetic on %s prohibited\n" ,
44524561 dst , reg_type_str [ptr_reg -> type ]);
44534562 return - EACCES ;
4454- case PTR_TO_MAP_VALUE :
4455- if (!env -> allow_ptr_leaks && !known && (smin_val < 0 ) != (smax_val < 0 )) {
4456- verbose (env , "R%d has unknown scalar with mixed signed bounds, pointer arithmetic with it prohibited for !root\n" ,
4457- off_reg == dst_reg ? dst : src );
4458- return - EACCES ;
4459- }
4460- /* fall-through */
44614563 default :
44624564 break ;
44634565 }
@@ -4472,13 +4574,15 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
44724574 !check_reg_sane_offset (env , ptr_reg , ptr_reg -> type ))
44734575 return - EINVAL ;
44744576
4577+ if (sanitize_needed (opcode )) {
4578+ ret = sanitize_ptr_alu (env , insn , ptr_reg , off_reg , dst_reg ,
4579+ & tmp_aux , false);
4580+ if (ret < 0 )
4581+ return sanitize_err (env , insn , ret , off_reg , dst_reg );
4582+ }
4583+
44754584 switch (opcode ) {
44764585 case BPF_ADD :
4477- ret = sanitize_ptr_alu (env , insn , ptr_reg , dst_reg , smin_val < 0 );
4478- if (ret < 0 ) {
4479- verbose (env , "R%d tried to add from different maps, paths, or prohibited types\n" , dst );
4480- return ret ;
4481- }
44824586 /* We can take a fixed offset as long as it doesn't overflow
44834587 * the s32 'off' field
44844588 */
@@ -4529,11 +4633,6 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
45294633 }
45304634 break ;
45314635 case BPF_SUB :
4532- ret = sanitize_ptr_alu (env , insn , ptr_reg , dst_reg , smin_val < 0 );
4533- if (ret < 0 ) {
4534- verbose (env , "R%d tried to sub from different maps, paths, or prohibited types\n" , dst );
4535- return ret ;
4536- }
45374636 if (dst_reg == off_reg ) {
45384637 /* scalar -= pointer. Creates an unknown scalar */
45394638 verbose (env , "R%d tried to subtract pointer from scalar\n" ,
@@ -4614,22 +4713,13 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
46144713 __reg_deduce_bounds (dst_reg );
46154714 __reg_bound_offset (dst_reg );
46164715
4617- /* For unprivileged we require that resulting offset must be in bounds
4618- * in order to be able to sanitize access later on.
4619- */
4620- if (!env -> allow_ptr_leaks ) {
4621- if (dst_reg -> type == PTR_TO_MAP_VALUE &&
4622- check_map_access (env , dst , dst_reg -> off , 1 , false)) {
4623- verbose (env , "R%d pointer arithmetic of map value goes out of range, "
4624- "prohibited for !root\n" , dst );
4625- return - EACCES ;
4626- } else if (dst_reg -> type == PTR_TO_STACK &&
4627- check_stack_access (env , dst_reg , dst_reg -> off +
4628- dst_reg -> var_off .value , 1 )) {
4629- verbose (env , "R%d stack pointer arithmetic goes out of range, "
4630- "prohibited for !root\n" , dst );
4631- return - EACCES ;
4632- }
4716+ if (sanitize_check_bounds (env , insn , dst_reg ) < 0 )
4717+ return - EACCES ;
4718+ if (sanitize_needed (opcode )) {
4719+ ret = sanitize_ptr_alu (env , insn , dst_reg , off_reg , dst_reg ,
4720+ & tmp_aux , true);
4721+ if (ret < 0 )
4722+ return sanitize_err (env , insn , ret , off_reg , dst_reg );
46334723 }
46344724
46354725 return 0 ;
@@ -4650,7 +4740,6 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
46504740 s64 smin_val , smax_val ;
46514741 u64 umin_val , umax_val ;
46524742 u64 insn_bitness = (BPF_CLASS (insn -> code ) == BPF_ALU64 ) ? 64 : 32 ;
4653- u32 dst = insn -> dst_reg ;
46544743 int ret ;
46554744
46564745 if (insn_bitness == 32 ) {
@@ -4684,13 +4773,14 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
46844773 return 0 ;
46854774 }
46864775
4776+ if (sanitize_needed (opcode )) {
4777+ ret = sanitize_val_alu (env , insn );
4778+ if (ret < 0 )
4779+ return sanitize_err (env , insn , ret , NULL , NULL );
4780+ }
4781+
46874782 switch (opcode ) {
46884783 case BPF_ADD :
4689- ret = sanitize_val_alu (env , insn );
4690- if (ret < 0 ) {
4691- verbose (env , "R%d tried to add from different pointers or scalars\n" , dst );
4692- return ret ;
4693- }
46944784 if (signed_add_overflows (dst_reg -> smin_value , smin_val ) ||
46954785 signed_add_overflows (dst_reg -> smax_value , smax_val )) {
46964786 dst_reg -> smin_value = S64_MIN ;
@@ -4710,11 +4800,6 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
47104800 dst_reg -> var_off = tnum_add (dst_reg -> var_off , src_reg .var_off );
47114801 break ;
47124802 case BPF_SUB :
4713- ret = sanitize_val_alu (env , insn );
4714- if (ret < 0 ) {
4715- verbose (env , "R%d tried to sub from different pointers or scalars\n" , dst );
4716- return ret ;
4717- }
47184803 if (signed_sub_overflows (dst_reg -> smin_value , smax_val ) ||
47194804 signed_sub_overflows (dst_reg -> smax_value , smin_val )) {
47204805 /* Overflow possible, we know nothing */
0 commit comments