ALU operations on pointers such as scalar_reg += map_value_ptr are handled in adjust_ptr_min_max_vals(). Problem is however that map_ptr and range in the register state share a union, so transferring state through dst_reg->range = ptr_reg->range is just buggy as any new map_ptr in the dst_reg is then truncated (or null) for subsequent checks. Fix this by adding a raw member and use it for copying state over to dst_reg.
Unified · Split
| @@ -51,6 +51,9 @@ struct bpf_reg_state { · 50 unmodified lines | ||
| 51 | 51 | * PTR_TO_MAP_VALUE_OR_NULL |
| 52 | 52 | */ |
| 53 | 53 | struct bpf_map *map_ptr; |
| 54 | + | |
| 55 | + /* Max size from any of the above. */ | |
| 56 | + unsigned long raw; | |
| 54 | 57 | }; |
| 55 | 58 | /* Fixed part of pointer offset, pointer types only */ |
| 56 | 59 | s32 off; |
| @@ -3046,7 +3046,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, · 3045 unmodified lines | ||
| 3046 | 3046 | dst_reg->umax_value = umax_ptr; |
| 3047 | 3047 | dst_reg->var_off = ptr_reg->var_off; |
| 3048 | 3048 | dst_reg->off = ptr_reg->off + smin_val; |
| 3049 | − dst_reg->range = ptr_reg->range; | |
| 3049 | + dst_reg->raw = ptr_reg->raw; | |
| 3050 | 3050 | break; |
| 3051 | 3051 | } |
| 3052 | 3052 | /* A new variable offset is created. Note that off_reg->off |
| @@ -3076,10 +3076,11 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, · 23 unmodified lines | ||
| 3076 | 3076 | } |
| 3077 | 3077 | dst_reg->var_off = tnum_add(ptr_reg->var_off, off_reg->var_off); |
| 3078 | 3078 | dst_reg->off = ptr_reg->off; |
| 3079 | + dst_reg->raw = ptr_reg->raw; | |
| 3079 | 3080 | if (reg_is_pkt_pointer(ptr_reg)) { |
| 3080 | 3081 | dst_reg->id = ++env->id_gen; |
| 3081 | 3082 | /* something was added to pkt_ptr, set range to zero */ |
| 3082 | − dst_reg->range = 0; | |
| 3083 | + dst_reg->raw = 0; | |
| 3083 | 3084 | } |
| 3084 | 3085 | break; |
| 3085 | 3086 | case BPF_SUB: |
| @@ -3108,7 +3109,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, · 22 unmodified lines | ||
| 3108 | 3109 | dst_reg->var_off = ptr_reg->var_off; |
| 3109 | 3110 | dst_reg->id = ptr_reg->id; |
| 3110 | 3111 | dst_reg->off = ptr_reg->off - smin_val; |
| 3111 | − dst_reg->range = ptr_reg->range; | |
| 3112 | + dst_reg->raw = ptr_reg->raw; | |
| 3112 | 3113 | break; |
| 3113 | 3114 | } |
| 3114 | 3115 | /* A new variable offset is created. If the subtrahend is known |
| @@ -3134,11 +3135,12 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, · 19 unmodified lines | ||
| 3134 | 3135 | } |
| 3135 | 3136 | dst_reg->var_off = tnum_sub(ptr_reg->var_off, off_reg->var_off); |
| 3136 | 3137 | dst_reg->off = ptr_reg->off; |
| 3138 | + dst_reg->raw = ptr_reg->raw; | |
| 3137 | 3139 | if (reg_is_pkt_pointer(ptr_reg)) { |
| 3138 | 3140 | dst_reg->id = ++env->id_gen; |
| 3139 | 3141 | /* something was added to pkt_ptr, set range to zero */ |
| 3140 | 3142 | if (smin_val < 0) |
| 3141 | − dst_reg->range = 0; | |
| 3143 | + dst_reg->raw = 0; | |
| 3142 | 3144 | } |
| 3143 | 3145 | break; |
| 3144 | 3146 | case BPF_AND: |