lkml.org 
[lkml]   [2018]   [Jun]   [4]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.9 01/29] objtool: Improve detection of BUG() and other dead ends
    Date
    4.9-stable review patch.  If anyone has any objections, please let me know.

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

    From: Josh Poimboeuf <jpoimboe@redhat.com>

    commit d1091c7fa3d52ebce4dd3f15d04155b3469b2f90 upstream.

    The BUG() macro's use of __builtin_unreachable() via the unreachable()
    macro tells gcc that the instruction is a dead end, and that it's safe
    to assume the current code path will not execute past the previous
    instruction.

    On x86, the BUG() macro is implemented with the 'ud2' instruction. When
    objtool's branch analysis sees that instruction, it knows the current
    code path has come to a dead end.

    Peter Zijlstra has been working on a patch to change the WARN macros to
    use 'ud2'. That patch will break objtool's assumption that 'ud2' is
    always a dead end.

    Generally it's best for objtool to avoid making those kinds of
    assumptions anyway. The more ignorant it is of kernel code internals,
    the better.

    So create a more generic way for objtool to detect dead ends by adding
    an annotation to the unreachable() macro. The annotation stores a
    pointer to the end of the unreachable code path in an '__unreachable'
    section. Objtool can read that section to find the dead ends.

    Tested-by: Peter Zijlstra (Intel) <peterz@infradead.org>
    Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
    Cc: Linus Torvalds <torvalds@linux-foundation.org>
    Cc: Peter Zijlstra <peterz@infradead.org>
    Cc: Thomas Gleixner <tglx@linutronix.de>
    Link: http://lkml.kernel.org/r/41a6d33971462ebd944a1c60ad4bf5be86c17b77.1487712920.git.jpoimboe@redhat.com
    Signed-off-by: Ingo Molnar <mingo@kernel.org>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

    ---
    arch/x86/kernel/vmlinux.lds.S | 1
    include/linux/compiler-gcc.h | 13 ++++++++
    tools/objtool/arch.h | 5 +--
    tools/objtool/arch/x86/decode.c | 3 --
    tools/objtool/builtin-check.c | 60 +++++++++++++++++++++++++++++++++++++---
    5 files changed, 71 insertions(+), 11 deletions(-)

    --- a/arch/x86/kernel/vmlinux.lds.S
    +++ b/arch/x86/kernel/vmlinux.lds.S
    @@ -353,6 +353,7 @@ SECTIONS
    /DISCARD/ : {
    *(.eh_frame)
    *(__func_stack_frame_non_standard)
    + *(__unreachable)
    }
    }

    --- a/include/linux/compiler-gcc.h
    +++ b/include/linux/compiler-gcc.h
    @@ -203,6 +203,17 @@
    #endif
    #endif

    +#ifdef CONFIG_STACK_VALIDATION
    +#define annotate_unreachable() ({ \
    + asm("1:\t\n" \
    + ".pushsection __unreachable, \"a\"\t\n" \
    + ".long 1b\t\n" \
    + ".popsection\t\n"); \
    +})
    +#else
    +#define annotate_unreachable()
    +#endif
    +
    /*
    * Mark a position in code as unreachable. This can be used to
    * suppress control flow warnings after asm blocks that transfer
    @@ -212,7 +223,7 @@
    * this in the preprocessor, but we can live with this because they're
    * unreleased. Really, we need to have autoconf for the kernel.
    */
    -#define unreachable() __builtin_unreachable()
    +#define unreachable() annotate_unreachable(); __builtin_unreachable()

    /* Mark a function definition as prohibited from being cloned. */
    #define __noclone __attribute__((__noclone__, __optimize__("no-tracer")))
    --- a/tools/objtool/arch.h
    +++ b/tools/objtool/arch.h
    @@ -31,9 +31,8 @@
    #define INSN_CALL_DYNAMIC 8
    #define INSN_RETURN 9
    #define INSN_CONTEXT_SWITCH 10
    -#define INSN_BUG 11
    -#define INSN_NOP 12
    -#define INSN_OTHER 13
    +#define INSN_NOP 11
    +#define INSN_OTHER 12
    #define INSN_LAST INSN_OTHER

    int arch_decode_instruction(struct elf *elf, struct section *sec,
    --- a/tools/objtool/arch/x86/decode.c
    +++ b/tools/objtool/arch/x86/decode.c
    @@ -118,9 +118,6 @@ int arch_decode_instruction(struct elf *
    op2 == 0x35)
    /* sysenter, sysret */
    *type = INSN_CONTEXT_SWITCH;
    - else if (op2 == 0x0b || op2 == 0xb9)
    - /* ud2 */
    - *type = INSN_BUG;
    else if (op2 == 0x0d || op2 == 0x1f)
    /* nopl/nopw */
    *type = INSN_NOP;
    --- a/tools/objtool/builtin-check.c
    +++ b/tools/objtool/builtin-check.c
    @@ -51,7 +51,7 @@ struct instruction {
    unsigned int len, state;
    unsigned char type;
    unsigned long immediate;
    - bool alt_group, visited, ignore_alts;
    + bool alt_group, visited, dead_end, ignore_alts;
    struct symbol *call_dest;
    struct instruction *jump_dest;
    struct list_head alts;
    @@ -330,6 +330,54 @@ static int decode_instructions(struct ob
    }

    /*
    + * Find all uses of the unreachable() macro, which are code path dead ends.
    + */
    +static int add_dead_ends(struct objtool_file *file)
    +{
    + struct section *sec;
    + struct rela *rela;
    + struct instruction *insn;
    + bool found;
    +
    + sec = find_section_by_name(file->elf, ".rela__unreachable");
    + if (!sec)
    + return 0;
    +
    + list_for_each_entry(rela, &sec->rela_list, list) {
    + if (rela->sym->type != STT_SECTION) {
    + WARN("unexpected relocation symbol type in .rela__unreachable");
    + return -1;
    + }
    + insn = find_insn(file, rela->sym->sec, rela->addend);
    + if (insn)
    + insn = list_prev_entry(insn, list);
    + else if (rela->addend == rela->sym->sec->len) {
    + found = false;
    + list_for_each_entry_reverse(insn, &file->insn_list, list) {
    + if (insn->sec == rela->sym->sec) {
    + found = true;
    + break;
    + }
    + }
    +
    + if (!found) {
    + WARN("can't find unreachable insn at %s+0x%x",
    + rela->sym->sec->name, rela->addend);
    + return -1;
    + }
    + } else {
    + WARN("can't find unreachable insn at %s+0x%x",
    + rela->sym->sec->name, rela->addend);
    + return -1;
    + }
    +
    + insn->dead_end = true;
    + }
    +
    + return 0;
    +}
    +
    +/*
    * Warnings shouldn't be reported for ignored functions.
    */
    static void add_ignores(struct objtool_file *file)
    @@ -905,6 +953,10 @@ static int decode_sections(struct objtoo
    if (ret)
    return ret;

    + ret = add_dead_ends(file);
    + if (ret)
    + return ret;
    +
    add_ignores(file);

    ret = add_nospec_ignores(file);
    @@ -1103,13 +1155,13 @@ static int validate_branch(struct objtoo

    return 0;

    - case INSN_BUG:
    - return 0;
    -
    default:
    break;
    }

    + if (insn->dead_end)
    + return 0;
    +
    insn = next_insn_same_sec(file, insn);
    if (!insn) {
    WARN("%s: unexpected end of section", sec->name);

    \
     
     \ /
      Last update: 2018-06-04 08:59    [W:4.181 / U:0.108 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site