lkml.org 
[lkml]   [2018]   [Jan]   [15]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.4 63/87] bpf: add bpf_patch_insn_single helper
    Date
    4.4-stable review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Daniel Borkmann <daniel@iogearbox.net>

    commit c237ee5eb33bf19fe0591c04ff8db19da7323a83 upstream.

    Move the functionality to patch instructions out of the verifier
    code and into the core as the new bpf_patch_insn_single() helper
    will be needed later on for blinding as well. No changes in
    functionality.

    Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
    Acked-by: Alexei Starovoitov <ast@kernel.org>
    Signed-off-by: David S. Miller <davem@davemloft.net>
    Signed-off-by: Jiri Slaby <jslaby@suse.cz>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>


    ---
    include/linux/filter.h | 3 ++
    kernel/bpf/core.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++
    kernel/bpf/verifier.c | 53 ++++++------------------------------
    3 files changed, 83 insertions(+), 44 deletions(-)

    --- a/include/linux/filter.h
    +++ b/include/linux/filter.h
    @@ -466,6 +466,9 @@ u64 __bpf_call_base(u64 r1, u64 r2, u64
    void bpf_int_jit_compile(struct bpf_prog *fp);
    bool bpf_helper_changes_skb_data(void *func);

    +struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
    + const struct bpf_insn *patch, u32 len);
    +
    #ifdef CONFIG_BPF_JIT
    typedef void (*bpf_jit_fill_hole_t)(void *area, unsigned int size);

    --- a/kernel/bpf/core.c
    +++ b/kernel/bpf/core.c
    @@ -137,6 +137,77 @@ void __bpf_prog_free(struct bpf_prog *fp
    }
    EXPORT_SYMBOL_GPL(__bpf_prog_free);

    +static bool bpf_is_jmp_and_has_target(const struct bpf_insn *insn)
    +{
    + return BPF_CLASS(insn->code) == BPF_JMP &&
    + /* Call and Exit are both special jumps with no
    + * target inside the BPF instruction image.
    + */
    + BPF_OP(insn->code) != BPF_CALL &&
    + BPF_OP(insn->code) != BPF_EXIT;
    +}
    +
    +static void bpf_adj_branches(struct bpf_prog *prog, u32 pos, u32 delta)
    +{
    + struct bpf_insn *insn = prog->insnsi;
    + u32 i, insn_cnt = prog->len;
    +
    + for (i = 0; i < insn_cnt; i++, insn++) {
    + if (!bpf_is_jmp_and_has_target(insn))
    + continue;
    +
    + /* Adjust offset of jmps if we cross boundaries. */
    + if (i < pos && i + insn->off + 1 > pos)
    + insn->off += delta;
    + else if (i > pos + delta && i + insn->off + 1 <= pos + delta)
    + insn->off -= delta;
    + }
    +}
    +
    +struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
    + const struct bpf_insn *patch, u32 len)
    +{
    + u32 insn_adj_cnt, insn_rest, insn_delta = len - 1;
    + struct bpf_prog *prog_adj;
    +
    + /* Since our patchlet doesn't expand the image, we're done. */
    + if (insn_delta == 0) {
    + memcpy(prog->insnsi + off, patch, sizeof(*patch));
    + return prog;
    + }
    +
    + insn_adj_cnt = prog->len + insn_delta;
    +
    + /* Several new instructions need to be inserted. Make room
    + * for them. Likely, there's no need for a new allocation as
    + * last page could have large enough tailroom.
    + */
    + prog_adj = bpf_prog_realloc(prog, bpf_prog_size(insn_adj_cnt),
    + GFP_USER);
    + if (!prog_adj)
    + return NULL;
    +
    + prog_adj->len = insn_adj_cnt;
    +
    + /* Patching happens in 3 steps:
    + *
    + * 1) Move over tail of insnsi from next instruction onwards,
    + * so we can patch the single target insn with one or more
    + * new ones (patching is always from 1 to n insns, n > 0).
    + * 2) Inject new instructions at the target location.
    + * 3) Adjust branch offsets if necessary.
    + */
    + insn_rest = insn_adj_cnt - off - len;
    +
    + memmove(prog_adj->insnsi + off + len, prog_adj->insnsi + off + 1,
    + sizeof(*patch) * insn_rest);
    + memcpy(prog_adj->insnsi + off, patch, sizeof(*patch) * len);
    +
    + bpf_adj_branches(prog_adj, off, insn_delta);
    +
    + return prog_adj;
    +}
    +
    #ifdef CONFIG_BPF_JIT
    struct bpf_binary_header *
    bpf_jit_binary_alloc(unsigned int proglen, u8 **image_ptr,
    --- a/kernel/bpf/verifier.c
    +++ b/kernel/bpf/verifier.c
    @@ -2098,26 +2098,6 @@ static void convert_pseudo_ld_imm64(stru
    insn->src_reg = 0;
    }

    -static void adjust_branches(struct bpf_prog *prog, int pos, int delta)
    -{
    - struct bpf_insn *insn = prog->insnsi;
    - int insn_cnt = prog->len;
    - int i;
    -
    - for (i = 0; i < insn_cnt; i++, insn++) {
    - if (BPF_CLASS(insn->code) != BPF_JMP ||
    - BPF_OP(insn->code) == BPF_CALL ||
    - BPF_OP(insn->code) == BPF_EXIT)
    - continue;
    -
    - /* adjust offset of jmps if necessary */
    - if (i < pos && i + insn->off + 1 > pos)
    - insn->off += delta;
    - else if (i > pos + delta && i + insn->off + 1 <= pos + delta)
    - insn->off -= delta;
    - }
    -}
    -
    /* convert load instructions that access fields of 'struct __sk_buff'
    * into sequence of instructions that access fields of 'struct sk_buff'
    */
    @@ -2127,14 +2107,15 @@ static int convert_ctx_accesses(struct v
    int insn_cnt = env->prog->len;
    struct bpf_insn insn_buf[16];
    struct bpf_prog *new_prog;
    - u32 cnt;
    - int i;
    enum bpf_access_type type;
    + int i;

    if (!env->prog->aux->ops->convert_ctx_access)
    return 0;

    for (i = 0; i < insn_cnt; i++, insn++) {
    + u32 insn_delta, cnt;
    +
    if (insn->code == (BPF_LDX | BPF_MEM | BPF_W))
    type = BPF_READ;
    else if (insn->code == (BPF_STX | BPF_MEM | BPF_W))
    @@ -2156,34 +2137,18 @@ static int convert_ctx_accesses(struct v
    return -EINVAL;
    }

    - if (cnt == 1) {
    - memcpy(insn, insn_buf, sizeof(*insn));
    - continue;
    - }
    -
    - /* several new insns need to be inserted. Make room for them */
    - insn_cnt += cnt - 1;
    - new_prog = bpf_prog_realloc(env->prog,
    - bpf_prog_size(insn_cnt),
    - GFP_USER);
    + new_prog = bpf_patch_insn_single(env->prog, i, insn_buf, cnt);
    if (!new_prog)
    return -ENOMEM;

    - new_prog->len = insn_cnt;
    -
    - memmove(new_prog->insnsi + i + cnt, new_prog->insns + i + 1,
    - sizeof(*insn) * (insn_cnt - i - cnt));
    -
    - /* copy substitute insns in place of load instruction */
    - memcpy(new_prog->insnsi + i, insn_buf, sizeof(*insn) * cnt);
    -
    - /* adjust branches in the whole program */
    - adjust_branches(new_prog, i, cnt - 1);
    + insn_delta = cnt - 1;

    /* keep walking new program and skip insns we just inserted */
    env->prog = new_prog;
    - insn = new_prog->insnsi + i + cnt - 1;
    - i += cnt - 1;
    + insn = new_prog->insnsi + i + insn_delta;
    +
    + insn_cnt += insn_delta;
    + i += insn_delta;
    }

    return 0;

    \
     
     \ /
      Last update: 2018-01-15 15:11    [W:2.453 / U:1.116 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site