Consolidate all error handling and provide more user-friendly error messages from sanitize_ptr_alu() and sanitize_val_alu().
Unified · Split
| @@ -5856,6 +5856,14 @@ static struct bpf_insn_aux_data *cur_aux(struct bpf_verifier_env *env) · 5855 unmodified lines | |||
| 5856 | return &env->insn_aux_data[env->insn_idx]; | 5856 | return &env->insn_aux_data[env->insn_idx]; |
| 5857 | } | 5857 | } |
| 5858 | 5858 | ||
| 5859 | enum { | ||
| 5860 | REASON_BOUNDS = -1, | ||
| 5861 | REASON_TYPE = -2, | ||
| 5862 | REASON_PATHS = -3, | ||
| 5863 | REASON_LIMIT = -4, | ||
| 5864 | REASON_STACK = -5, | ||
| 5865 | }; | ||
| 5866 | |||
| 5859 | static int retrieve_ptr_limit(const struct bpf_reg_state *ptr_reg, | 5867 | static int retrieve_ptr_limit(const struct bpf_reg_state *ptr_reg, |
| 5860 | const struct bpf_reg_state *off_reg, | 5868 | const struct bpf_reg_state *off_reg, |
| 5861 | u32 *alu_limit, u8 opcode) | 5869 | u32 *alu_limit, u8 opcode) |
| @@ -5867,7 +5875,7 @@ static int retrieve_ptr_limit(const struct bpf_reg_state *ptr_reg, · 5 unmodified lines | |||
| 5867 | 5875 | ||
| 5868 | if (!tnum_is_const(off_reg->var_off) && | 5876 | if (!tnum_is_const(off_reg->var_off) && |
| 5869 | (off_reg->smin_value < 0) != (off_reg->smax_value < 0)) | 5877 | (off_reg->smin_value < 0) != (off_reg->smax_value < 0)) |
| 5870 | return -EACCES; | 5878 | return REASON_BOUNDS; |
| 5871 | 5879 | ||
| 5872 | switch (ptr_reg->type) { | 5880 | switch (ptr_reg->type) { |
| 5873 | case PTR_TO_STACK: | 5881 | case PTR_TO_STACK: |
| @@ -5894,11 +5902,11 @@ static int retrieve_ptr_limit(const struct bpf_reg_state *ptr_reg, · 20 unmodified lines | |||
| 5894 | } | 5902 | } |
| 5895 | break; | 5903 | break; |
| 5896 | default: | 5904 | default: |
| 5897 | return -EINVAL; | 5905 | return REASON_TYPE; |
| 5898 | } | 5906 | } |
| 5899 | 5907 | ||
| 5900 | if (ptr_limit >= max) | 5908 | if (ptr_limit >= max) |
| 5901 | return -ERANGE; | 5909 | return REASON_LIMIT; |
| 5902 | *alu_limit = ptr_limit; | 5910 | *alu_limit = ptr_limit; |
| 5903 | return 0; | 5911 | return 0; |
| 5904 | } | 5912 | } |
| @@ -5918,7 +5926,7 @@ static int update_alu_sanitation_state(struct bpf_insn_aux_data *aux, · 13 unmodified lines | |||
| 5918 | if (aux->alu_state && | 5926 | if (aux->alu_state && |
| 5919 | (aux->alu_state != alu_state || | 5927 | (aux->alu_state != alu_state || |
| 5920 | aux->alu_limit != alu_limit)) | 5928 | aux->alu_limit != alu_limit)) |
| 5921 | return -EACCES; | 5929 | return REASON_PATHS; |
| 5922 | 5930 | ||
| 5923 | /* Corresponding fixup done in fixup_bpf_calls(). */ | 5931 | /* Corresponding fixup done in fixup_bpf_calls(). */ |
| 5924 | aux->alu_state = alu_state; | 5932 | aux->alu_state = alu_state; |
| @@ -5991,7 +5999,46 @@ do_sim: · 66 unmodified lines | |||
| 5991 | ret = push_stack(env, env->insn_idx + 1, env->insn_idx, true); | 5999 | ret = push_stack(env, env->insn_idx + 1, env->insn_idx, true); |
| 5992 | if (!ptr_is_dst_reg && ret) | 6000 | if (!ptr_is_dst_reg && ret) |
| 5993 | *dst_reg = tmp; | 6001 | *dst_reg = tmp; |
| 5994 | return !ret ? -EFAULT : 0; | 6002 | return !ret ? REASON_STACK : 0; |
| 6003 | } | ||
| 6004 | |||
| 6005 | static int sanitize_err(struct bpf_verifier_env *env, | ||
| 6006 | const struct bpf_insn *insn, int reason, | ||
| 6007 | const struct bpf_reg_state *off_reg, | ||
| 6008 | const struct bpf_reg_state *dst_reg) | ||
| 6009 | { | ||
| 6010 | static const char *err = "pointer arithmetic with it prohibited for !root"; | ||
| 6011 | const char *op = BPF_OP(insn->code) == BPF_ADD ? "add" : "sub"; | ||
| 6012 | u32 dst = insn->dst_reg, src = insn->src_reg; | ||
| 6013 | |||
| 6014 | switch (reason) { | ||
| 6015 | case REASON_BOUNDS: | ||
| 6016 | verbose(env, "R%d has unknown scalar with mixed signed bounds, %s\n", | ||
| 6017 | off_reg == dst_reg ? dst : src, err); | ||
| 6018 | break; | ||
| 6019 | case REASON_TYPE: | ||
| 6020 | verbose(env, "R%d has pointer with unsupported alu operation, %s\n", | ||
| 6021 | off_reg == dst_reg ? src : dst, err); | ||
| 6022 | break; | ||
| 6023 | case REASON_PATHS: | ||
| 6024 | verbose(env, "R%d tried to %s from different maps, paths or scalars, %s\n", | ||
| 6025 | dst, op, err); | ||
| 6026 | break; | ||
| 6027 | case REASON_LIMIT: | ||
| 6028 | verbose(env, "R%d tried to %s beyond pointer bounds, %s\n", | ||
| 6029 | dst, op, err); | ||
| 6030 | break; | ||
| 6031 | case REASON_STACK: | ||
| 6032 | verbose(env, "R%d could not be pushed for speculative verification, %s\n", | ||
| 6033 | dst, err); | ||
| 6034 | break; | ||
| 6035 | default: | ||
| 6036 | verbose(env, "verifier internal error: unknown reason (%d)\n", | ||
| 6037 | reason); | ||
| 6038 | break; | ||
| 6039 | } | ||
| 6040 | |||
| 6041 | return -EACCES; | ||
| 5995 | } | 6042 | } |
| 5996 | 6043 | ||
| 5997 | /* check that stack access falls within stack limits and that 'reg' doesn't | 6044 | /* check that stack access falls within stack limits and that 'reg' doesn't |
| @@ -6116,10 +6163,9 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, · 118 unmodified lines | |||
| 6116 | switch (opcode) { | 6163 | switch (opcode) { |
| 6117 | case BPF_ADD: | 6164 | case BPF_ADD: |
| 6118 | ret = sanitize_ptr_alu(env, insn, ptr_reg, off_reg, dst_reg); | 6165 | ret = sanitize_ptr_alu(env, insn, ptr_reg, off_reg, dst_reg); |
| 6119 | if (ret < 0) { | 6166 | if (ret < 0) |
| 6120 | verbose(env, "R%d tried to add from different maps, paths, or prohibited types\n", dst); | 6167 | return sanitize_err(env, insn, ret, off_reg, dst_reg); |
| 6121 | return ret; | 6168 | |
| 6122 | } | ||
| 6123 | /* We can take a fixed offset as long as it doesn't overflow | 6169 | /* We can take a fixed offset as long as it doesn't overflow |
| 6124 | * the s32 'off' field | 6170 | * the s32 'off' field |
| 6125 | */ | 6171 | */ |
| @@ -6171,10 +6217,9 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, · 45 unmodified lines | |||
| 6171 | break; | 6217 | break; |
| 6172 | case BPF_SUB: | 6218 | case BPF_SUB: |
| 6173 | ret = sanitize_ptr_alu(env, insn, ptr_reg, off_reg, dst_reg); | 6219 | ret = sanitize_ptr_alu(env, insn, ptr_reg, off_reg, dst_reg); |
| 6174 | if (ret < 0) { | 6220 | if (ret < 0) |
| 6175 | verbose(env, "R%d tried to sub from different maps, paths, or prohibited types\n", dst); | 6221 | return sanitize_err(env, insn, ret, off_reg, dst_reg); |
| 6176 | return ret; | 6222 | |
| 6177 | } | ||
| 6178 | if (dst_reg == off_reg) { | 6223 | if (dst_reg == off_reg) { |
| 6179 | /* scalar -= pointer. Creates an unknown scalar */ | 6224 | /* scalar -= pointer. Creates an unknown scalar */ |
| 6180 | verbose(env, "R%d tried to subtract pointer from scalar\n", | 6225 | verbose(env, "R%d tried to subtract pointer from scalar\n", |
| @@ -6863,9 +6908,8 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env, · 682 unmodified lines | |||
| 6863 | s32 s32_min_val, s32_max_val; | 6908 | s32 s32_min_val, s32_max_val; |
| 6864 | u32 u32_min_val, u32_max_val; | 6909 | u32 u32_min_val, u32_max_val; |
| 6865 | u64 insn_bitness = (BPF_CLASS(insn->code) == BPF_ALU64) ? 64 : 32; | 6910 | u64 insn_bitness = (BPF_CLASS(insn->code) == BPF_ALU64) ? 64 : 32; |
| 6866 | u32 dst = insn->dst_reg; | ||
| 6867 | int ret; | ||
| 6868 | bool alu32 = (BPF_CLASS(insn->code) != BPF_ALU64); | 6911 | bool alu32 = (BPF_CLASS(insn->code) != BPF_ALU64); |
| 6912 | int ret; | ||
| 6869 | 6913 | ||
| 6870 | smin_val = src_reg.smin_value; | 6914 | smin_val = src_reg.smin_value; |
| 6871 | smax_val = src_reg.smax_value; | 6915 | smax_val = src_reg.smax_value; |
| @@ -6924,20 +6968,16 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env, · 52 unmodified lines | |||
| 6924 | switch (opcode) { | 6968 | switch (opcode) { |
| 6925 | case BPF_ADD: | 6969 | case BPF_ADD: |
| 6926 | ret = sanitize_val_alu(env, insn); | 6970 | ret = sanitize_val_alu(env, insn); |
| 6927 | if (ret < 0) { | 6971 | if (ret < 0) |
| 6928 | verbose(env, "R%d tried to add from different pointers or scalars\n", dst); | 6972 | return sanitize_err(env, insn, ret, NULL, NULL); |
| 6929 | return ret; | ||
| 6930 | } | ||
| 6931 | scalar32_min_max_add(dst_reg, &src_reg); | 6973 | scalar32_min_max_add(dst_reg, &src_reg); |
| 6932 | scalar_min_max_add(dst_reg, &src_reg); | 6974 | scalar_min_max_add(dst_reg, &src_reg); |
| 6933 | dst_reg->var_off = tnum_add(dst_reg->var_off, src_reg.var_off); | 6975 | dst_reg->var_off = tnum_add(dst_reg->var_off, src_reg.var_off); |
| 6934 | break; | 6976 | break; |
| 6935 | case BPF_SUB: | 6977 | case BPF_SUB: |
| 6936 | ret = sanitize_val_alu(env, insn); | 6978 | ret = sanitize_val_alu(env, insn); |
| 6937 | if (ret < 0) { | 6979 | if (ret < 0) |
| 6938 | verbose(env, "R%d tried to sub from different pointers or scalars\n", dst); | 6980 | return sanitize_err(env, insn, ret, NULL, NULL); |
| 6939 | return ret; | ||
| 6940 | } | ||
| 6941 | scalar32_min_max_sub(dst_reg, &src_reg); | 6981 | scalar32_min_max_sub(dst_reg, &src_reg); |
| 6942 | scalar_min_max_sub(dst_reg, &src_reg); | 6982 | scalar_min_max_sub(dst_reg, &src_reg); |
| 6943 | dst_reg->var_off = tnum_sub(dst_reg->var_off, src_reg.var_off); | 6983 | dst_reg->var_off = tnum_sub(dst_reg->var_off, src_reg.var_off); |