lkml.org 
[lkml]   [2021]   [May]   [31]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.19 050/116] bpf: Improve verifier error messages for users
    Date
    From: Daniel Borkmann <daniel@iogearbox.net>

    commit a6aaece00a57fa6f22575364b3903dfbccf5345d upstream

    Consolidate all error handling and provide more user-friendly error messages
    from sanitize_ptr_alu() and sanitize_val_alu().

    Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
    Reviewed-by: John Fastabend <john.fastabend@gmail.com>
    Acked-by: Alexei Starovoitov <ast@kernel.org>
    [fllinden@amazon.com: backport to 5.4]
    Signed-off-by: Frank van der Linden <fllinden@amazon.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    Signed-off-by: Ovidiu Panait <ovidiu.panait@windriver.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    kernel/bpf/verifier.c | 84 ++++++++++++++++++++++++++++++++++++--------------
    1 file changed, 62 insertions(+), 22 deletions(-)

    --- a/kernel/bpf/verifier.c
    +++ b/kernel/bpf/verifier.c
    @@ -2729,6 +2729,14 @@ static struct bpf_insn_aux_data *cur_aux
    return &env->insn_aux_data[env->insn_idx];
    }

    +enum {
    + REASON_BOUNDS = -1,
    + REASON_TYPE = -2,
    + REASON_PATHS = -3,
    + REASON_LIMIT = -4,
    + REASON_STACK = -5,
    +};
    +
    static int retrieve_ptr_limit(const struct bpf_reg_state *ptr_reg,
    const struct bpf_reg_state *off_reg,
    u32 *alu_limit, u8 opcode)
    @@ -2740,7 +2748,7 @@ static int retrieve_ptr_limit(const stru

    if (!tnum_is_const(off_reg->var_off) &&
    (off_reg->smin_value < 0) != (off_reg->smax_value < 0))
    - return -EACCES;
    + return REASON_BOUNDS;

    switch (ptr_reg->type) {
    case PTR_TO_STACK:
    @@ -2764,11 +2772,11 @@ static int retrieve_ptr_limit(const stru
    }
    break;
    default:
    - return -EINVAL;
    + return REASON_TYPE;
    }

    if (ptr_limit >= max)
    - return -ERANGE;
    + return REASON_LIMIT;
    *alu_limit = ptr_limit;
    return 0;
    }
    @@ -2788,7 +2796,7 @@ static int update_alu_sanitation_state(s
    if (aux->alu_state &&
    (aux->alu_state != alu_state ||
    aux->alu_limit != alu_limit))
    - return -EACCES;
    + return REASON_PATHS;

    /* Corresponding fixup done in fixup_bpf_calls(). */
    aux->alu_state = alu_state;
    @@ -2861,7 +2869,46 @@ do_sim:
    ret = push_stack(env, env->insn_idx + 1, env->insn_idx, true);
    if (!ptr_is_dst_reg && ret)
    *dst_reg = tmp;
    - return !ret ? -EFAULT : 0;
    + return !ret ? REASON_STACK : 0;
    +}
    +
    +static int sanitize_err(struct bpf_verifier_env *env,
    + const struct bpf_insn *insn, int reason,
    + const struct bpf_reg_state *off_reg,
    + const struct bpf_reg_state *dst_reg)
    +{
    + static const char *err = "pointer arithmetic with it prohibited for !root";
    + const char *op = BPF_OP(insn->code) == BPF_ADD ? "add" : "sub";
    + u32 dst = insn->dst_reg, src = insn->src_reg;
    +
    + switch (reason) {
    + case REASON_BOUNDS:
    + verbose(env, "R%d has unknown scalar with mixed signed bounds, %s\n",
    + off_reg == dst_reg ? dst : src, err);
    + break;
    + case REASON_TYPE:
    + verbose(env, "R%d has pointer with unsupported alu operation, %s\n",
    + off_reg == dst_reg ? src : dst, err);
    + break;
    + case REASON_PATHS:
    + verbose(env, "R%d tried to %s from different maps, paths or scalars, %s\n",
    + dst, op, err);
    + break;
    + case REASON_LIMIT:
    + verbose(env, "R%d tried to %s beyond pointer bounds, %s\n",
    + dst, op, err);
    + break;
    + case REASON_STACK:
    + verbose(env, "R%d could not be pushed for speculative verification, %s\n",
    + dst, err);
    + break;
    + default:
    + verbose(env, "verifier internal error: unknown reason (%d)\n",
    + reason);
    + break;
    + }
    +
    + return -EACCES;
    }

    /* Handles arithmetic on a pointer and a scalar: computes new min/max and var_off.
    @@ -2934,10 +2981,9 @@ static int adjust_ptr_min_max_vals(struc
    switch (opcode) {
    case BPF_ADD:
    ret = sanitize_ptr_alu(env, insn, ptr_reg, off_reg, dst_reg);
    - if (ret < 0) {
    - verbose(env, "R%d tried to add from different maps, paths, or prohibited types\n", dst);
    - return ret;
    - }
    + if (ret < 0)
    + return sanitize_err(env, insn, ret, off_reg, dst_reg);
    +
    /* We can take a fixed offset as long as it doesn't overflow
    * the s32 'off' field
    */
    @@ -2989,10 +3035,9 @@ static int adjust_ptr_min_max_vals(struc
    break;
    case BPF_SUB:
    ret = sanitize_ptr_alu(env, insn, ptr_reg, off_reg, dst_reg);
    - if (ret < 0) {
    - verbose(env, "R%d tried to sub from different maps, paths, or prohibited types\n", dst);
    - return ret;
    - }
    + if (ret < 0)
    + return sanitize_err(env, insn, ret, off_reg, dst_reg);
    +
    if (dst_reg == off_reg) {
    /* scalar -= pointer. Creates an unknown scalar */
    verbose(env, "R%d tried to subtract pointer from scalar\n",
    @@ -3109,7 +3154,6 @@ static int adjust_scalar_min_max_vals(st
    s64 smin_val, smax_val;
    u64 umin_val, umax_val;
    u64 insn_bitness = (BPF_CLASS(insn->code) == BPF_ALU64) ? 64 : 32;
    - u32 dst = insn->dst_reg;
    int ret;

    if (insn_bitness == 32) {
    @@ -3146,10 +3190,8 @@ static int adjust_scalar_min_max_vals(st
    switch (opcode) {
    case BPF_ADD:
    ret = sanitize_val_alu(env, insn);
    - if (ret < 0) {
    - verbose(env, "R%d tried to add from different pointers or scalars\n", dst);
    - return ret;
    - }
    + if (ret < 0)
    + return sanitize_err(env, insn, ret, NULL, NULL);
    if (signed_add_overflows(dst_reg->smin_value, smin_val) ||
    signed_add_overflows(dst_reg->smax_value, smax_val)) {
    dst_reg->smin_value = S64_MIN;
    @@ -3170,10 +3212,8 @@ static int adjust_scalar_min_max_vals(st
    break;
    case BPF_SUB:
    ret = sanitize_val_alu(env, insn);
    - if (ret < 0) {
    - verbose(env, "R%d tried to sub from different pointers or scalars\n", dst);
    - return ret;
    - }
    + if (ret < 0)
    + return sanitize_err(env, insn, ret, NULL, NULL);
    if (signed_sub_overflows(dst_reg->smin_value, smax_val) ||
    signed_sub_overflows(dst_reg->smax_value, smin_val)) {
    /* Overflow possible, we know nothing */

    \
     
     \ /
      Last update: 2021-05-31 15:43    [W:3.959 / U:0.020 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site