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 11/35] objtool: Add retpoline validation
    David requested a objtool validation pass for RETPOLINE enabled
    builds, where it validates no unannotated indirect jumps or calls are
    left.

    Add an additional .discard.retpoline_safe section to allow annotating
    the few indirect sites that are required and safe.

    Requested-by: David Woodhouse <dwmw2@infradead.org>
    Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
    ---
    scripts/Makefile.build | 6 ++
    tools/objtool/builtin-check.c | 5 +-
    tools/objtool/builtin-orc.c | 4 -
    tools/objtool/check.c | 92 +++++++++++++++++++++++++++++++++++++++---
    tools/objtool/check.h | 4 -
    5 files changed, 100 insertions(+), 11 deletions(-)

    --- a/scripts/Makefile.build
    +++ b/scripts/Makefile.build
    @@ -269,6 +269,10 @@ objtool_args += --no-unreachable
    else
    objtool_args += $(call cc-ifversion, -lt, 0405, --no-unreachable)
    endif
    +ifdef CONFIG_RETPOLINE
    + objtool_args += --retpoline
    +endif
    +

    ifdef CONFIG_MODVERSIONS
    objtool_o = $(@D)/.tmp_$(@F)
    --- a/tools/objtool/builtin-check.c
    +++ b/tools/objtool/builtin-check.c
    @@ -29,7 +29,7 @@
    #include "builtin.h"
    #include "check.h"

    -bool no_fp, no_unreachable;
    +bool no_fp, no_unreachable, retpoline;

    static const char * const check_usage[] = {
    "objtool check [<options>] file.o",
    @@ -39,6 +39,7 @@ static const char * const check_usage[]
    const struct option check_options[] = {
    OPT_BOOLEAN('f', "no-fp", &no_fp, "Skip frame pointer validation"),
    OPT_BOOLEAN('u', "no-unreachable", &no_unreachable, "Skip 'unreachable instruction' warnings"),
    + OPT_BOOLEAN('r', "retpoline", &retpoline, "Validate retpoline assumptions"),
    OPT_END(),
    };

    @@ -53,5 +54,5 @@ int cmd_check(int argc, const char **arg

    objname = argv[0];

    - return check(objname, no_fp, no_unreachable, false);
    + return check(objname, no_fp, no_unreachable, retpoline, false);
    }
    --- a/tools/objtool/builtin-orc.c
    +++ b/tools/objtool/builtin-orc.c
    @@ -37,7 +37,7 @@ static const char *orc_usage[] = {
    };

    extern const struct option check_options[];
    -extern bool no_fp, no_unreachable;
    +extern bool no_fp, no_unreachable, retpoline;

    int cmd_orc(int argc, const char **argv)
    {
    @@ -51,7 +51,7 @@ int cmd_orc(int argc, const char **argv)

    objname = argv[0];

    - return check(objname, no_fp, no_unreachable, true);
    + return check(objname, no_fp, no_unreachable, retpoline, true);

    }

    --- a/tools/objtool/check.c
    +++ b/tools/objtool/check.c
    @@ -33,7 +33,7 @@ struct alternative {
    };

    const char *objname;
    -static bool no_fp;
    +static bool no_fp, retpoline;
    struct cfi_state initial_func_cfi;

    struct instruction *find_insn(struct objtool_file *file,
    @@ -496,6 +496,7 @@ static int add_jump_destinations(struct
    * disguise, so convert them accordingly.
    */
    insn->type = INSN_JUMP_DYNAMIC;
    + insn->retpoline_safe = true;
    continue;
    } else {
    /* sibling call */
    @@ -548,13 +549,11 @@ static int add_call_destinations(struct
    * normal for a function to call within itself. So
    * disable this warning for now.
    */
    -#if 0
    - if (!insn->call_dest) {
    + if (!retpoline && !insn->call_dest) {
    WARN_FUNC("can't find call dest symbol at offset 0x%lx",
    insn->sec, insn->offset, dest_off);
    return -1;
    }
    -#endif
    } else if (rela->sym->type == STT_SECTION) {
    insn->call_dest = find_symbol_by_offset(rela->sym->sec,
    rela->addend+4);
    @@ -1158,6 +1157,54 @@ static int assert_static_jumps(struct ob
    return 0;
    }

    +static int read_retpoline_hints(struct objtool_file *file)
    +{
    + struct section *sec, *relasec;
    + struct instruction *insn;
    + struct rela *rela;
    + int i;
    +
    + sec = find_section_by_name(file->elf, ".discard.retpoline_safe");
    + if (!sec)
    + return 0;
    +
    + relasec = sec->rela;
    + if (!relasec) {
    + WARN("missing .rela.discard.retpoline_safe section");
    + return -1;
    + }
    +
    + if (sec->len % sizeof(unsigned long)) {
    + WARN("retpoline_safe 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 retpoline_safe[%d]", i);
    + return -1;
    + }
    +
    + insn = find_insn(file, rela->sym->sec, rela->addend);
    + if (!insn) {
    + WARN("can't find insn for retpoline_safe[%d]", i);
    + return -1;
    + }
    +
    + if (insn->type != INSN_JUMP_DYNAMIC &&
    + insn->type != INSN_CALL_DYNAMIC) {
    + WARN_FUNC("retpoline_safe hint not a indirect jump/call",
    + insn->sec, insn->offset);
    + return -1;
    + }
    +
    + insn->retpoline_safe = true;
    + }
    +
    + return 0;
    +}
    +
    static int decode_sections(struct objtool_file *file)
    {
    int ret;
    @@ -1196,6 +1243,10 @@ static int decode_sections(struct objtoo
    if (ret)
    return ret;

    + ret = read_retpoline_hints(file);
    + if (ret)
    + return ret;
    +
    return 0;
    }

    @@ -1939,6 +1990,29 @@ static int validate_unwind_hints(struct
    return warnings;
    }

    +static int validate_retpoline(struct objtool_file *file)
    +{
    + struct instruction *insn;
    + int warnings = 0;
    +
    + for_each_insn(file, insn) {
    + if (insn->type != INSN_JUMP_DYNAMIC &&
    + insn->type != INSN_CALL_DYNAMIC)
    + continue;
    +
    + if (insn->retpoline_safe)
    + continue;
    +
    + WARN_FUNC("indirect %s found in RETPOLINE build",
    + insn->sec, insn->offset,
    + insn->type == INSN_JUMP_DYNAMIC ? "jump" : "call");
    +
    + warnings++;
    + }
    +
    + return warnings;
    +}
    +
    static bool is_kasan_insn(struct instruction *insn)
    {
    return (insn->type == INSN_CALL &&
    @@ -2064,13 +2138,14 @@ static void cleanup(struct objtool_file
    elf_close(file->elf);
    }

    -int check(const char *_objname, bool _no_fp, bool no_unreachable, bool orc)
    +int check(const char *_objname, bool _no_fp, bool no_unreachable, bool _retpoline, bool orc)
    {
    struct objtool_file file;
    int ret, warnings = 0;

    objname = _objname;
    no_fp = _no_fp;
    + retpoline = _retpoline;

    file.elf = elf_open(objname, orc ? O_RDWR : O_RDONLY);
    if (!file.elf)
    @@ -2098,6 +2173,13 @@ int check(const char *_objname, bool _no
    if (list_empty(&file.insn_list))
    goto out;

    + if (retpoline) {
    + ret = validate_retpoline(&file);
    + if (ret < 0)
    + return ret;
    + warnings += ret;
    + }
    +
    ret = validate_functions(&file);
    if (ret < 0)
    goto out;
    --- a/tools/objtool/check.h
    +++ b/tools/objtool/check.h
    @@ -45,7 +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;
    + bool static_jump_dest, retpoline_safe;
    struct symbol *call_dest;
    struct instruction *jump_dest;
    struct list_head alts;
    @@ -63,7 +63,7 @@ struct objtool_file {
    bool ignore_unreachables, c_file, hints;
    };

    -int check(const char *objname, bool no_fp, bool no_unreachable, bool orc);
    +int check(const char *objname, bool no_fp, bool no_unreachable, bool retpoline, bool orc);

    struct instruction *find_insn(struct objtool_file *file,
    struct section *sec, unsigned long offset);

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