@@ -5856,40 +5856,51 @@ static struct bpf_insn_aux_data *cur_aux(struct bpf_verifier_env *env)
58565856 return & env -> insn_aux_data [env -> insn_idx ];
58575857}
58585858
5859+ enum {
5860+ REASON_BOUNDS = -1 ,
5861+ REASON_TYPE = -2 ,
5862+ REASON_PATHS = -3 ,
5863+ REASON_LIMIT = -4 ,
5864+ REASON_STACK = -5 ,
5865+ };
5866+
58595867static int retrieve_ptr_limit (const struct bpf_reg_state * ptr_reg ,
5860- u32 * ptr_limit , u8 opcode , bool off_is_neg )
5868+ const struct bpf_reg_state * off_reg ,
5869+ u32 * alu_limit , u8 opcode )
58615870{
5871+ bool off_is_neg = off_reg -> smin_value < 0 ;
58625872 bool mask_to_left = (opcode == BPF_ADD && off_is_neg ) ||
58635873 (opcode == BPF_SUB && !off_is_neg );
5864- u32 off , max ;
5874+ u32 max = 0 , ptr_limit = 0 ;
5875+
5876+ if (!tnum_is_const (off_reg -> var_off ) &&
5877+ (off_reg -> smin_value < 0 ) != (off_reg -> smax_value < 0 ))
5878+ return REASON_BOUNDS ;
58655879
58665880 switch (ptr_reg -> type ) {
58675881 case PTR_TO_STACK :
58685882 /* Offset 0 is out-of-bounds, but acceptable start for the
5869- * left direction, see BPF_REG_FP.
5883+ * left direction, see BPF_REG_FP. Also, unknown scalar
5884+ * offset where we would need to deal with min/max bounds is
5885+ * currently prohibited for unprivileged.
58705886 */
58715887 max = MAX_BPF_STACK + mask_to_left ;
5872- /* Indirect variable offset stack access is prohibited in
5873- * unprivileged mode so it's not handled here.
5874- */
5875- off = ptr_reg -> off + ptr_reg -> var_off .value ;
5876- if (mask_to_left )
5877- * ptr_limit = MAX_BPF_STACK + off ;
5878- else
5879- * ptr_limit = - off - 1 ;
5880- return * ptr_limit >= max ? - ERANGE : 0 ;
5888+ ptr_limit = - (ptr_reg -> var_off .value + ptr_reg -> off );
5889+ break ;
58815890 case PTR_TO_MAP_VALUE :
58825891 max = ptr_reg -> map_ptr -> value_size ;
5883- if (mask_to_left ) {
5884- * ptr_limit = ptr_reg -> umax_value + ptr_reg -> off ;
5885- } else {
5886- off = ptr_reg -> smin_value + ptr_reg -> off ;
5887- * ptr_limit = ptr_reg -> map_ptr -> value_size - off - 1 ;
5888- }
5889- return * ptr_limit >= max ? - ERANGE : 0 ;
5892+ ptr_limit = (mask_to_left ?
5893+ ptr_reg -> smin_value :
5894+ ptr_reg -> umax_value ) + ptr_reg -> off ;
5895+ break ;
58905896 default :
5891- return - EINVAL ;
5897+ return REASON_TYPE ;
58925898 }
5899+
5900+ if (ptr_limit >= max )
5901+ return REASON_LIMIT ;
5902+ * alu_limit = ptr_limit ;
5903+ return 0 ;
58935904}
58945905
58955906static bool can_skip_alu_sanitation (const struct bpf_verifier_env * env ,
@@ -5907,7 +5918,7 @@ static int update_alu_sanitation_state(struct bpf_insn_aux_data *aux,
59075918 if (aux -> alu_state &&
59085919 (aux -> alu_state != alu_state ||
59095920 aux -> alu_limit != alu_limit ))
5910- return - EACCES ;
5921+ return REASON_PATHS ;
59115922
59125923 /* Corresponding fixup done in fixup_bpf_calls(). */
59135924 aux -> alu_state = alu_state ;
@@ -5926,14 +5937,22 @@ static int sanitize_val_alu(struct bpf_verifier_env *env,
59265937 return update_alu_sanitation_state (aux , BPF_ALU_NON_POINTER , 0 );
59275938}
59285939
5940+ static bool sanitize_needed (u8 opcode )
5941+ {
5942+ return opcode == BPF_ADD || opcode == BPF_SUB ;
5943+ }
5944+
59295945static int sanitize_ptr_alu (struct bpf_verifier_env * env ,
59305946 struct bpf_insn * insn ,
59315947 const struct bpf_reg_state * ptr_reg ,
5948+ const struct bpf_reg_state * off_reg ,
59325949 struct bpf_reg_state * dst_reg ,
5933- bool off_is_neg )
5950+ struct bpf_insn_aux_data * tmp_aux ,
5951+ const bool commit_window )
59345952{
5953+ struct bpf_insn_aux_data * aux = commit_window ? cur_aux (env ) : tmp_aux ;
59355954 struct bpf_verifier_state * vstate = env -> cur_state ;
5936- struct bpf_insn_aux_data * aux = cur_aux ( env ) ;
5955+ bool off_is_neg = off_reg -> smin_value < 0 ;
59375956 bool ptr_is_dst_reg = ptr_reg == dst_reg ;
59385957 u8 opcode = BPF_OP (insn -> code );
59395958 u32 alu_state , alu_limit ;
@@ -5951,18 +5970,33 @@ static int sanitize_ptr_alu(struct bpf_verifier_env *env,
59515970 if (vstate -> speculative )
59525971 goto do_sim ;
59535972
5954- alu_state = off_is_neg ? BPF_ALU_NEG_VALUE : 0 ;
5955- alu_state |= ptr_is_dst_reg ?
5956- BPF_ALU_SANITIZE_SRC : BPF_ALU_SANITIZE_DST ;
5957-
5958- err = retrieve_ptr_limit (ptr_reg , & alu_limit , opcode , off_is_neg );
5973+ err = retrieve_ptr_limit (ptr_reg , off_reg , & alu_limit , opcode );
59595974 if (err < 0 )
59605975 return err ;
59615976
5977+ if (commit_window ) {
5978+ /* In commit phase we narrow the masking window based on
5979+ * the observed pointer move after the simulated operation.
5980+ */
5981+ alu_state = tmp_aux -> alu_state ;
5982+ alu_limit = abs (tmp_aux -> alu_limit - alu_limit );
5983+ } else {
5984+ alu_state = off_is_neg ? BPF_ALU_NEG_VALUE : 0 ;
5985+ alu_state |= ptr_is_dst_reg ?
5986+ BPF_ALU_SANITIZE_SRC : BPF_ALU_SANITIZE_DST ;
5987+ }
5988+
59625989 err = update_alu_sanitation_state (aux , alu_state , alu_limit );
59635990 if (err < 0 )
59645991 return err ;
59655992do_sim :
5993+ /* If we're in commit phase, we're done here given we already
5994+ * pushed the truncated dst_reg into the speculative verification
5995+ * stack.
5996+ */
5997+ if (commit_window )
5998+ return 0 ;
5999+
59666000 /* Simulate and find potential out-of-bounds access under
59676001 * speculative execution from truncation as a result of
59686002 * masking when off was not within expected range. If off
@@ -5979,7 +6013,46 @@ static int sanitize_ptr_alu(struct bpf_verifier_env *env,
59796013 ret = push_stack (env , env -> insn_idx + 1 , env -> insn_idx , true);
59806014 if (!ptr_is_dst_reg && ret )
59816015 * dst_reg = tmp ;
5982- return !ret ? - EFAULT : 0 ;
6016+ return !ret ? REASON_STACK : 0 ;
6017+ }
6018+
6019+ static int sanitize_err (struct bpf_verifier_env * env ,
6020+ const struct bpf_insn * insn , int reason ,
6021+ const struct bpf_reg_state * off_reg ,
6022+ const struct bpf_reg_state * dst_reg )
6023+ {
6024+ static const char * err = "pointer arithmetic with it prohibited for !root" ;
6025+ const char * op = BPF_OP (insn -> code ) == BPF_ADD ? "add" : "sub" ;
6026+ u32 dst = insn -> dst_reg , src = insn -> src_reg ;
6027+
6028+ switch (reason ) {
6029+ case REASON_BOUNDS :
6030+ verbose (env , "R%d has unknown scalar with mixed signed bounds, %s\n" ,
6031+ off_reg == dst_reg ? dst : src , err );
6032+ break ;
6033+ case REASON_TYPE :
6034+ verbose (env , "R%d has pointer with unsupported alu operation, %s\n" ,
6035+ off_reg == dst_reg ? src : dst , err );
6036+ break ;
6037+ case REASON_PATHS :
6038+ verbose (env , "R%d tried to %s from different maps, paths or scalars, %s\n" ,
6039+ dst , op , err );
6040+ break ;
6041+ case REASON_LIMIT :
6042+ verbose (env , "R%d tried to %s beyond pointer bounds, %s\n" ,
6043+ dst , op , err );
6044+ break ;
6045+ case REASON_STACK :
6046+ verbose (env , "R%d could not be pushed for speculative verification, %s\n" ,
6047+ dst , err );
6048+ break ;
6049+ default :
6050+ verbose (env , "verifier internal error: unknown reason (%d)\n" ,
6051+ reason );
6052+ break ;
6053+ }
6054+
6055+ return - EACCES ;
59836056}
59846057
59856058/* check that stack access falls within stack limits and that 'reg' doesn't
@@ -6016,6 +6089,37 @@ static int check_stack_access_for_ptr_arithmetic(
60166089 return 0 ;
60176090}
60186091
6092+ static int sanitize_check_bounds (struct bpf_verifier_env * env ,
6093+ const struct bpf_insn * insn ,
6094+ const struct bpf_reg_state * dst_reg )
6095+ {
6096+ u32 dst = insn -> dst_reg ;
6097+
6098+ /* For unprivileged we require that resulting offset must be in bounds
6099+ * in order to be able to sanitize access later on.
6100+ */
6101+ if (env -> bypass_spec_v1 )
6102+ return 0 ;
6103+
6104+ switch (dst_reg -> type ) {
6105+ case PTR_TO_STACK :
6106+ if (check_stack_access_for_ptr_arithmetic (env , dst , dst_reg ,
6107+ dst_reg -> off + dst_reg -> var_off .value ))
6108+ return - EACCES ;
6109+ break ;
6110+ case PTR_TO_MAP_VALUE :
6111+ if (check_map_access (env , dst , dst_reg -> off , 1 , false)) {
6112+ verbose (env , "R%d pointer arithmetic of map value goes out of range, "
6113+ "prohibited for !root\n" , dst );
6114+ return - EACCES ;
6115+ }
6116+ break ;
6117+ default :
6118+ break ;
6119+ }
6120+
6121+ return 0 ;
6122+ }
60196123
60206124/* Handles arithmetic on a pointer and a scalar: computes new min/max and var_off.
60216125 * Caller should also handle BPF_MOV case separately.
@@ -6035,8 +6139,9 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
60356139 smin_ptr = ptr_reg -> smin_value , smax_ptr = ptr_reg -> smax_value ;
60366140 u64 umin_val = off_reg -> umin_value , umax_val = off_reg -> umax_value ,
60376141 umin_ptr = ptr_reg -> umin_value , umax_ptr = ptr_reg -> umax_value ;
6038- u32 dst = insn -> dst_reg , src = insn -> src_reg ;
6142+ struct bpf_insn_aux_data tmp_aux = {} ;
60396143 u8 opcode = BPF_OP (insn -> code );
6144+ u32 dst = insn -> dst_reg ;
60406145 int ret ;
60416146
60426147 dst_reg = & regs [dst ];
@@ -6084,13 +6189,6 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
60846189 verbose (env , "R%d pointer arithmetic on %s prohibited\n" ,
60856190 dst , reg_type_str [ptr_reg -> type ]);
60866191 return - EACCES ;
6087- case PTR_TO_MAP_VALUE :
6088- if (!env -> allow_ptr_leaks && !known && (smin_val < 0 ) != (smax_val < 0 )) {
6089- verbose (env , "R%d has unknown scalar with mixed signed bounds, pointer arithmetic with it prohibited for !root\n" ,
6090- off_reg == dst_reg ? dst : src );
6091- return - EACCES ;
6092- }
6093- fallthrough ;
60946192 default :
60956193 break ;
60966194 }
@@ -6108,13 +6206,15 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
61086206 /* pointer types do not carry 32-bit bounds at the moment. */
61096207 __mark_reg32_unbounded (dst_reg );
61106208
6209+ if (sanitize_needed (opcode )) {
6210+ ret = sanitize_ptr_alu (env , insn , ptr_reg , off_reg , dst_reg ,
6211+ & tmp_aux , false);
6212+ if (ret < 0 )
6213+ return sanitize_err (env , insn , ret , off_reg , dst_reg );
6214+ }
6215+
61116216 switch (opcode ) {
61126217 case BPF_ADD :
6113- ret = sanitize_ptr_alu (env , insn , ptr_reg , dst_reg , smin_val < 0 );
6114- if (ret < 0 ) {
6115- verbose (env , "R%d tried to add from different maps, paths, or prohibited types\n" , dst );
6116- return ret ;
6117- }
61186218 /* We can take a fixed offset as long as it doesn't overflow
61196219 * the s32 'off' field
61206220 */
@@ -6165,11 +6265,6 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
61656265 }
61666266 break ;
61676267 case BPF_SUB :
6168- ret = sanitize_ptr_alu (env , insn , ptr_reg , dst_reg , smin_val < 0 );
6169- if (ret < 0 ) {
6170- verbose (env , "R%d tried to sub from different maps, paths, or prohibited types\n" , dst );
6171- return ret ;
6172- }
61736268 if (dst_reg == off_reg ) {
61746269 /* scalar -= pointer. Creates an unknown scalar */
61756270 verbose (env , "R%d tried to subtract pointer from scalar\n" ,
@@ -6250,21 +6345,13 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
62506345 __reg_deduce_bounds (dst_reg );
62516346 __reg_bound_offset (dst_reg );
62526347
6253- /* For unprivileged we require that resulting offset must be in bounds
6254- * in order to be able to sanitize access later on.
6255- */
6256- if (!env -> bypass_spec_v1 ) {
6257- if (dst_reg -> type == PTR_TO_MAP_VALUE &&
6258- check_map_access (env , dst , dst_reg -> off , 1 , false)) {
6259- verbose (env , "R%d pointer arithmetic of map value goes out of range, "
6260- "prohibited for !root\n" , dst );
6261- return - EACCES ;
6262- } else if (dst_reg -> type == PTR_TO_STACK &&
6263- check_stack_access_for_ptr_arithmetic (
6264- env , dst , dst_reg , dst_reg -> off +
6265- dst_reg -> var_off .value )) {
6266- return - EACCES ;
6267- }
6348+ if (sanitize_check_bounds (env , insn , dst_reg ) < 0 )
6349+ return - EACCES ;
6350+ if (sanitize_needed (opcode )) {
6351+ ret = sanitize_ptr_alu (env , insn , dst_reg , off_reg , dst_reg ,
6352+ & tmp_aux , true);
6353+ if (ret < 0 )
6354+ return sanitize_err (env , insn , ret , off_reg , dst_reg );
62686355 }
62696356
62706357 return 0 ;
@@ -6858,9 +6945,8 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
68586945 s32 s32_min_val , s32_max_val ;
68596946 u32 u32_min_val , u32_max_val ;
68606947 u64 insn_bitness = (BPF_CLASS (insn -> code ) == BPF_ALU64 ) ? 64 : 32 ;
6861- u32 dst = insn -> dst_reg ;
6862- int ret ;
68636948 bool alu32 = (BPF_CLASS (insn -> code ) != BPF_ALU64 );
6949+ int ret ;
68646950
68656951 smin_val = src_reg .smin_value ;
68666952 smax_val = src_reg .smax_value ;
@@ -6902,6 +6988,12 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
69026988 return 0 ;
69036989 }
69046990
6991+ if (sanitize_needed (opcode )) {
6992+ ret = sanitize_val_alu (env , insn );
6993+ if (ret < 0 )
6994+ return sanitize_err (env , insn , ret , NULL , NULL );
6995+ }
6996+
69056997 /* Calculate sign/unsigned bounds and tnum for alu32 and alu64 bit ops.
69066998 * There are two classes of instructions: The first class we track both
69076999 * alu32 and alu64 sign/unsigned bounds independently this provides the
@@ -6918,21 +7010,11 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
69187010 */
69197011 switch (opcode ) {
69207012 case BPF_ADD :
6921- ret = sanitize_val_alu (env , insn );
6922- if (ret < 0 ) {
6923- verbose (env , "R%d tried to add from different pointers or scalars\n" , dst );
6924- return ret ;
6925- }
69267013 scalar32_min_max_add (dst_reg , & src_reg );
69277014 scalar_min_max_add (dst_reg , & src_reg );
69287015 dst_reg -> var_off = tnum_add (dst_reg -> var_off , src_reg .var_off );
69297016 break ;
69307017 case BPF_SUB :
6931- ret = sanitize_val_alu (env , insn );
6932- if (ret < 0 ) {
6933- verbose (env , "R%d tried to sub from different pointers or scalars\n" , dst );
6934- return ret ;
6935- }
69367018 scalar32_min_max_sub (dst_reg , & src_reg );
69377019 scalar_min_max_sub (dst_reg , & src_reg );
69387020 dst_reg -> var_off = tnum_sub (dst_reg -> var_off , src_reg .var_off );
0 commit comments