lkml.org 
[lkml]   [2018]   [Jan]   [18]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    From
    Subject[PATCH 06/35] objtool: Implement base jump_assert support
    Implement a jump_label assertion that asserts that the code location
    is indeed only reachable through a static_branch. Because if GCC is
    absolutely retaded it could generate code like:

    xor rax,rax
    NOP/JMP 1f
    mov $1, rax
    1:
    test rax,rax
    jz 2f
    <do-code>
    2:

    instead of the sensible:

    NOP/JMP 1f
    <do-code>
    1:

    This implements objtool infrastructure for ensuring the code ends up
    sane, since we'll rely on that for correctness and security.

    We tag the instructions after the static branch with static_jump_dest=true;
    that is the instruction after the NOP and the instruction at the
    JMP+disp site.

    Then, when we read the .discard.jump_assert section, we assert that
    each entry points to an instruction that has static_jump_dest set.

    With this we can assert that the code emitted for the if statement
    ends up at the static jump location and nothing untowards happened.

    Cc: Borislav Petkov <bp@alien8.de>
    Cc: Thomas Gleixner <tglx@linutronix.de>
    Cc: Josh Poimboeuf <jpoimboe@redhat.com>
    Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
    ---
    tools/objtool/check.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++--
    tools/objtool/check.h | 1
    2 files changed, 69 insertions(+), 2 deletions(-)

    --- a/tools/objtool/check.c
    +++ b/tools/objtool/check.c
    @@ -687,8 +687,17 @@ static int handle_jump_alt(struct objtoo
    struct instruction *orig_insn,
    struct instruction **new_insn)
    {
    - if (orig_insn->type == INSN_NOP)
    + struct instruction *next_insn = list_next_entry(orig_insn, list);
    +
    + if (orig_insn->type == INSN_NOP) {
    + /*
    + * If orig_insn is a NOP, then new_insn is the branch target
    + * for when it would've been a JMP.
    + */
    + next_insn->static_jump_dest = true;
    + (*new_insn)->static_jump_dest = true;
    return 0;
    + }

    if (orig_insn->type != INSN_JUMP_UNCONDITIONAL) {
    WARN_FUNC("unsupported instruction at jump label",
    @@ -696,7 +705,16 @@ static int handle_jump_alt(struct objtoo
    return -1;
    }

    - *new_insn = list_next_entry(orig_insn, list);
    + /*
    + * Otherwise, orig_insn is a JMP and it will have orig_insn->jump_dest.
    + * In this case we'll effectively NOP the alt by pointing new_insn at
    + * next_insn.
    + */
    + orig_insn->jump_dest->static_jump_dest = true;
    + next_insn->static_jump_dest = true;
    +
    + *new_insn = next_insn;
    +
    return 0;
    }

    @@ -1067,6 +1085,50 @@ static int read_unwind_hints(struct objt
    return 0;
    }

    +static int assert_static_jumps(struct objtool_file *file)
    +{
    + struct section *sec, *relasec;
    + struct instruction *insn;
    + struct rela *rela;
    + int i;
    +
    + sec = find_section_by_name(file->elf, ".discard.jump_assert");
    + if (!sec)
    + return 0;
    +
    + relasec = sec->rela;
    + if (!relasec) {
    + WARN("missing .rela.discard.jump_assert section");
    + return -1;
    + }
    +
    + if (sec->len % sizeof(unsigned long)) {
    + WARN("jump_assert size mismatch: %d %ld", sec->len, sizeof(unsigned long));
    + return -1;
    + }
    +
    + for (i = 0; i < sec->len / sizeof(unsigned long); i++) {
    + rela = find_rela_by_dest(sec, i * sizeof(unsigned long));
    + if (!rela) {
    + WARN("can't find rela for jump_assert[%d]", i);
    + return -1;
    + }
    +
    + insn = find_insn(file, rela->sym->sec, rela->addend);
    + if (!insn) {
    + WARN("can't find insn for jump_assert[%d]", i);
    + return -1;
    + }
    +
    + if (!insn->static_jump_dest) {
    + WARN_FUNC("static assert FAIL", insn->sec, insn->offset);
    + return -1;
    + }
    + }
    +
    + return 0;
    +}
    +
    static int decode_sections(struct objtool_file *file)
    {
    int ret;
    @@ -2000,6 +2062,10 @@ int check(const char *_objname, bool _no
    goto out;
    warnings += ret;

    + ret = assert_static_jumps(&file);
    + if (ret < 0)
    + return ret;
    +
    if (list_empty(&file.insn_list))
    goto out;

    --- a/tools/objtool/check.h
    +++ b/tools/objtool/check.h
    @@ -45,6 +45,7 @@ struct instruction {
    unsigned char type;
    unsigned long immediate;
    bool alt_group, visited, dead_end, ignore, hint, save, restore, ignore_alts;
    + bool static_jump_dest;
    struct symbol *call_dest;
    struct instruction *jump_dest;
    struct list_head alts;

    \
     
     \ /
      Last update: 2018-01-18 16:05    [W:4.298 / U:0.020 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site