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 br_static=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 br_static 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: Josh Poimboeuf <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Borislav Petkov <[email protected]>
Signed-off-by: Peter Zijlstra (Intel) <[email protected]>
---
 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->br_static = true;
+               (*new_insn)->br_static = 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->br_static = true;
+       next_insn->br_static = true;
+
+       *new_insn = next_insn;
+
        return 0;
 }
 
@@ -1067,6 +1085,50 @@ static int read_unwind_hints(struct objt
        return 0;
 }
 
+static int read_jump_assertions(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->br_static) {
+                       WARN_FUNC("static assert FAIL", insn->sec, 
insn->offset);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
 static int decode_sections(struct objtool_file *file)
 {
        int ret;
@@ -1105,6 +1167,10 @@ static int decode_sections(struct objtoo
        if (ret)
                return ret;
 
+       ret = read_jump_assertions(file);
+       if (ret)
+               return ret;
+
        return 0;
 }
 
--- 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 br_static;
        struct symbol *call_dest;
        struct instruction *jump_dest;
        struct list_head alts;


Reply via email to