On Fri, Jan 12, 2018 at 10:09:08AM +0000, David Woodhouse wrote:
> static_cpu_has() + asm-goto is NOT SUFFICIENT.
> 
> It's still *possible* for a missed optimisation in GCC to still leave
> us with a conditional branch around the wrmsr, letting the CPU
> speculate around it too.

OK, so GCC would have to be bloody retarded to mess this up; but would
something like the below work for you?

The usage is like:

  if (static_branch_unlikely(key)) {
        arch_static_assert();
        stuff();
  }

And then objtool will fail things if the first instruction into that
branch is not immediately after a NOP/JMP patch site (on either the NOP
or the JMP+disp side of things).

---
diff --git a/arch/x86/include/asm/jump_label.h 
b/arch/x86/include/asm/jump_label.h
index 8c0de4282659..6a1a893145ca 100644
--- a/arch/x86/include/asm/jump_label.h
+++ b/arch/x86/include/asm/jump_label.h
@@ -62,6 +62,15 @@ static __always_inline bool arch_static_branch_jump(struct 
static_key *key, bool
        return true;
 }
 
+static __always_inline void arch_static_assert(void)
+{
+       asm volatile ("1:\n\t"
+                     ".pushsection .discard.jump_assert, \"aw\" \n\t"
+                     _ASM_ALIGN  "\n\t"
+                     _ASM_PTR "1b \n\t"
+                     ".popsection \n\t");
+}
+
 #ifdef CONFIG_X86_64
 typedef u64 jump_label_t;
 #else
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index f40d46e24bcc..657bfc706bb6 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -687,8 +687,17 @@ static int handle_jump_alt(struct objtool_file *file,
                           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 objtool_file *file,
                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 objtool_file *file)
        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 objtool_file *file)
        if (ret)
                return ret;
 
+       ret = read_jump_assertions(file);
+       if (ret)
+               return ret;
+
        return 0;
 }
 
diff --git a/tools/objtool/check.h b/tools/objtool/check.h
index dbadb304a410..12e0a3cf0350 100644
--- a/tools/objtool/check.h
+++ b/tools/objtool/check.h
@@ -45,6 +46,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