On Sat, Feb 23, 2019 at 09:37:06AM +0100, Peter Zijlstra wrote:
> On Fri, Feb 22, 2019 at 03:55:25PM -0800, Andy Lutomirski wrote:
> > On Fri, Feb 22, 2019 at 2:26 PM Peter Zijlstra <pet...@infradead.org> wrote:
> 
> > >   arch/x86/entry/entry_64.o: warning: objtool: 
> > > .altinstr_replacement+0xb1: redundant CLAC
> > 
> > Does objtool understand altinstr?
> 
> Yep, otherwise it would've never found any of the STAC/CLAC instructions
> to begin with, they're all alternatives. The emitted code is all NOPs.
> 
> > If it understands that this is an
> > altinstr associated with a not-actually-a-fuction (i.e. END not
> > ENDPROC) piece of code, it could suppress this warning.
> 
> Using readelf on entry_64.o the symbol type appears to be STT_NOTYPE for
> these 'functions', so yes, I can try and ignore this warning for those.
> 
> > > +#define AC_SAFE(func) \
> > > +       static void __used __section(.discard.ac_safe) \
> > > +               *__func_ac_safe_##func = func
> > 
> > Are we okay with annotating function *declarations* in a way that
> > their addresses get taken whenever the declaration is processed?  It
> > would be nice if we could avoid this.
> > 
> > I'm wondering if we can just change the code that does getreg() and
> > load_gs_index() so it doesn't do it with AC set.  Also, what about
> > paravirt kernels?  They'll call into PV code for load_gs_index() with
> > AC set.
> 
> Fixing that code would be fine; but we need that annotation regardless,
> read Linus' email a little further back, he wants things like
> copy_{to,from}_user_unsafe().
> 
> Those really would need to be marked AC-safe, there's no inlining that.
> 
> That said, yes these annotations _suck_. But it's what we had and works,
> I'm just copying it (from STACK_FRAME_NON_STANDARD).
> 
> That said; maybe we can do something like:
> 
> #define AC_SAFE(func)                                         \
>       asm (".pushsection .discard.ac_safe_sym\n\t"            \
>            "999: .ascii \"" #func "\"\n\t"                    \
>            ".popsection\n\t"                                  \
>            ".pushsection .discard.ac_safe\n\t"                \
>            _ASM_PTR " 999b\n\t"                               \
>            ".popsection")
> 
> I just don't have a clue on how to read that in objtool; weak elf foo.
> I'll see if I can make it work.

Fixed all that. Should probably also convert that NON_STANDARD stuff to
this new shiny thing.

Retained the ptrace muck because it's a nice test case, your patch is
obviously a better fix there.

Haven't bothered looking at alternatives yet, this is a
defconfig+tracing build.

Weekend now.

---
 arch/x86/include/asm/special_insns.h |   2 +
 arch/x86/kernel/ptrace.c             |   3 +-
 include/linux/frame.h                |  38 ++++++++++++
 tools/objtool/Makefile               |   2 +-
 tools/objtool/arch.h                 |   6 +-
 tools/objtool/arch/x86/decode.c      |  22 ++++++-
 tools/objtool/check.c                | 117 ++++++++++++++++++++++++++++++++++-
 tools/objtool/check.h                |   2 +-
 tools/objtool/elf.h                  |   1 +
 9 files changed, 187 insertions(+), 6 deletions(-)

diff --git a/arch/x86/include/asm/special_insns.h 
b/arch/x86/include/asm/special_insns.h
index 43c029cdc3fe..cd31e4433f4c 100644
--- a/arch/x86/include/asm/special_insns.h
+++ b/arch/x86/include/asm/special_insns.h
@@ -5,6 +5,7 @@
 
 #ifdef __KERNEL__
 
+#include <linux/frame.h>
 #include <asm/nops.h>
 
 /*
@@ -135,6 +136,7 @@ static inline void native_wbinvd(void)
 }
 
 extern asmlinkage void native_load_gs_index(unsigned);
+AC_SAFE(native_load_gs_index);
 
 static inline unsigned long __read_cr4(void)
 {
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index 4b8ee05dd6ad..e278b4055a8b 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -420,7 +420,7 @@ static int putreg(struct task_struct *child,
        return 0;
 }
 
-static unsigned long getreg(struct task_struct *task, unsigned long offset)
+static notrace unsigned long getreg(struct task_struct *task, unsigned long 
offset)
 {
        switch (offset) {
        case offsetof(struct user_regs_struct, cs):
@@ -444,6 +444,7 @@ static unsigned long getreg(struct task_struct *task, 
unsigned long offset)
 
        return *pt_regs_access(task_pt_regs(task), offset);
 }
+AC_SAFE(getreg);
 
 static int genregs_get(struct task_struct *target,
                       const struct user_regset *regset,
diff --git a/include/linux/frame.h b/include/linux/frame.h
index 02d3ca2d9598..870a894bc586 100644
--- a/include/linux/frame.h
+++ b/include/linux/frame.h
@@ -21,4 +21,42 @@
 
 #endif /* CONFIG_STACK_VALIDATION */
 
+#if defined(CONFIG_STACK_VALIDATION)
+/*
+ * This macro marks functions as AC-safe, that is, it is safe to call from an
+ * EFLAGS.AC enabled region (typically user_access_begin() /
+ * user_access_end()).
+ *
+ * These functions in turn will only call AC-safe functions themselves (which
+ * precludes tracing, including __fentry__ and scheduling, including
+ * preempt_enable).
+ *
+ * AC-safe functions will obviously also not change EFLAGS.AC themselves.
+ */
+#define AC_SAFE(func)                                          \
+       asm (".pushsection .discard.ac_safe_sym, \"S\", @3\n\t" \
+            "999: .ascii \"" #func "\"\n\t"                    \
+            "     .byte 0\n\t"                                 \
+            ".popsection\n\t"                                  \
+            ".pushsection .discard.ac_safe\n\t"                \
+            ".long 999b - .\n\t"                               \
+            ".popsection")
+
+/*
+ * SHT_STRTAB sayeth:
+ *
+ * - The initial byte of a string table is \0. This allows an string offset
+ *   value of zero to denote the NULL string.
+ *
+ * Works just fine without this, but since we set the proper section type, lets
+ * not confuse anybody reading this.
+ */
+asm(".pushsection .discard.ac_safe_sym, \"S\", @3\n\t"
+    ".byte 0\n\t"
+    ".popsection\n\t");
+
+#else
+#define AC_SAFE(func)
+#endif
+
 #endif /* _LINUX_FRAME_H */
diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile
index c9d038f91af6..5a64e5fb9d7a 100644
--- a/tools/objtool/Makefile
+++ b/tools/objtool/Makefile
@@ -31,7 +31,7 @@ INCLUDES := -I$(srctree)/tools/include \
            -I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi \
            -I$(srctree)/tools/objtool/arch/$(ARCH)/include
 WARNINGS := $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -Wno-packed
-CFLAGS   += -Werror $(WARNINGS) $(KBUILD_HOSTCFLAGS) -g $(INCLUDES)
+CFLAGS   += -Werror $(WARNINGS) $(KBUILD_HOSTCFLAGS) -g $(INCLUDES) -ggdb3
 LDFLAGS  += -lelf $(LIBSUBCMD) $(KBUILD_HOSTLDFLAGS)
 
 # Allow old libelf to be used:
diff --git a/tools/objtool/arch.h b/tools/objtool/arch.h
index b0d7dc3d71b5..9795d83cbc01 100644
--- a/tools/objtool/arch.h
+++ b/tools/objtool/arch.h
@@ -33,7 +33,11 @@
 #define INSN_STACK             8
 #define INSN_BUG               9
 #define INSN_NOP               10
-#define INSN_OTHER             11
+#define INSN_STAC              11
+#define INSN_CLAC              12
+#define INSN_STD               13
+#define INSN_CLD               14
+#define INSN_OTHER             15
 #define INSN_LAST              INSN_OTHER
 
 enum op_dest_type {
diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c
index 540a209b78ab..bee32ad53ecf 100644
--- a/tools/objtool/arch/x86/decode.c
+++ b/tools/objtool/arch/x86/decode.c
@@ -369,7 +369,19 @@ int arch_decode_instruction(struct elf *elf, struct 
section *sec,
 
        case 0x0f:
 
-               if (op2 >= 0x80 && op2 <= 0x8f) {
+               if (op2 == 0x01) {
+
+                       if (modrm == 0xca) {
+
+                               *type = INSN_CLAC;
+
+                       } else if (modrm == 0xcb) {
+
+                               *type = INSN_STAC;
+
+                       }
+
+               } else if (op2 >= 0x80 && op2 <= 0x8f) {
 
                        *type = INSN_JUMP_CONDITIONAL;
 
@@ -444,6 +456,14 @@ int arch_decode_instruction(struct elf *elf, struct 
section *sec,
                *type = INSN_CALL;
                break;
 
+       case 0xfc:
+               *type = INSN_CLD;
+               break;
+
+       case 0xfd:
+               *type = INSN_STD;
+               break;
+
        case 0xff:
                if (modrm_reg == 2 || modrm_reg == 3)
 
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 0414a0d52262..3dedfa19cb09 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -451,6 +451,41 @@ static void add_ignores(struct objtool_file *file)
        }
 }
 
+static int add_ac_safe(struct objtool_file *file)
+{
+       struct section *sec_sym, *sec;
+       struct symbol *func;
+       struct rela *rela;
+       const char *name;
+
+       sec = find_section_by_name(file->elf, ".rela.discard.ac_safe");
+       if (!sec)
+               return 0;
+
+       sec_sym = find_section_by_name(file->elf, ".discard.ac_safe_sym");
+       if (!sec_sym) {
+               WARN("missing AC-safe symbols");
+               return -1;
+       }
+
+       list_for_each_entry(rela, &sec->rela_list, list) {
+               if (rela->sym->type != STT_SECTION) {
+                       WARN("unexpected relocation symbol type in %s", 
sec->name);
+                       return -1;
+               }
+
+               name = elf_strptr(file->elf->elf, sec_sym->idx, rela->addend);
+
+               func = find_symbol_by_name(file->elf, name);
+               if (!func)
+                       continue;
+
+               func->ac_safe = true;
+       }
+
+       return 0;
+}
+
 /*
  * FIXME: For now, just ignore any alternatives which add retpolines.  This is
  * a temporary hack, as it doesn't allow ORC to unwind from inside a retpoline.
@@ -695,6 +730,7 @@ static int handle_group_alt(struct objtool_file *file,
                last_new_insn = insn;
 
                insn->ignore = orig_insn->ignore_alts;
+               insn->func = orig_insn->func;
 
                if (insn->type != INSN_JUMP_CONDITIONAL &&
                    insn->type != INSN_JUMP_UNCONDITIONAL)
@@ -1239,6 +1275,10 @@ static int decode_sections(struct objtool_file *file)
 
        add_ignores(file);
 
+       ret = add_ac_safe(file);
+       if (ret)
+               return ret;
+
        ret = add_nospec_ignores(file);
        if (ret)
                return ret;
@@ -1902,6 +1942,15 @@ static int validate_branch(struct objtool_file *file, 
struct instruction *first,
                switch (insn->type) {
 
                case INSN_RETURN:
+                       if (state.ac) {
+                               WARN_FUNC("return with AC set", sec, 
insn->offset);
+                               return 1;
+                       }
+                       if (state.df) {
+                               WARN_FUNC("return with DF set", sec, 
insn->offset);
+                               return 1;
+                       }
+
                        if (func && has_modified_stack_frame(&state)) {
                                WARN_FUNC("return with modified stack frame",
                                          sec, insn->offset);
@@ -1917,6 +1966,17 @@ static int validate_branch(struct objtool_file *file, 
struct instruction *first,
                        return 0;
 
                case INSN_CALL:
+                       if ((state.ac_safe || state.ac) && 
!insn->call_dest->ac_safe) {
+                               WARN_FUNC("call to %s() with AC set", sec, 
insn->offset,
+                                               insn->call_dest->name);
+                               return 1;
+                       }
+                       if (state.df) {
+                               WARN_FUNC("call to %s() with DF set", sec, 
insn->offset,
+                                               insn->call_dest->name);
+                               return 1;
+                       }
+
                        if (is_fentry_call(insn))
                                break;
 
@@ -1926,8 +1986,25 @@ static int validate_branch(struct objtool_file *file, 
struct instruction *first,
                        if (ret == -1)
                                return 1;
 
-                       /* fallthrough */
+                       if (!no_fp && func && !has_valid_stack_frame(&state)) {
+                               WARN_FUNC("call without frame pointer 
save/setup",
+                                         sec, insn->offset);
+                               return 1;
+                       }
+                       break;
+
                case INSN_CALL_DYNAMIC:
+                       if ((state.ac_safe || state.ac) && 
!insn->call_dest->ac_safe) {
+                               WARN_FUNC("call to %s() with AC set", sec, 
insn->offset,
+                                               insn->call_dest->name);
+                               return 1;
+                       }
+                       if (state.df) {
+                               WARN_FUNC("call to %s() with DF set", sec, 
insn->offset,
+                                               insn->call_dest->name);
+                               return 1;
+                       }
+
                        if (!no_fp && func && !has_valid_stack_frame(&state)) {
                                WARN_FUNC("call without frame pointer 
save/setup",
                                          sec, insn->offset);
@@ -1980,6 +2057,42 @@ static int validate_branch(struct objtool_file *file, 
struct instruction *first,
 
                        break;
 
+               case INSN_STAC:
+                       if (state.ac_safe || state.ac) {
+                               WARN_FUNC("recursive STAC", sec, insn->offset);
+                               return 1;
+                       }
+                       state.ac = true;
+                       break;
+
+               case INSN_CLAC:
+                       if (!state.ac && insn->func) {
+                               WARN_FUNC("redundant CLAC", sec, insn->offset);
+                               return 1;
+                       }
+                       if (state.ac_safe) {
+                               WARN_FUNC("AC-safe clears AC", sec, 
insn->offset);
+                               return 1;
+                       }
+                       state.ac = false;
+                       break;
+
+               case INSN_STD:
+                       if (state.df) {
+                               WARN_FUNC("recursive STD", sec, insn->offset);
+                               return 1;
+                       }
+                       state.df = true;
+                       break;
+
+               case INSN_CLD:
+                       if (!state.df && insn->func) {
+                               WARN_FUNC("redundant CLD", sec, insn->offset);
+                               return 1;
+                       }
+                       state.df = false;
+                       break;
+
                default:
                        break;
                }
@@ -2141,6 +2254,8 @@ static int validate_functions(struct objtool_file *file)
                        if (!insn || insn->ignore)
                                continue;
 
+                       state.ac_safe = func->ac_safe;
+
                        ret = validate_branch(file, insn, state);
                        warnings += ret;
                }
diff --git a/tools/objtool/check.h b/tools/objtool/check.h
index e6e8a655b556..602634792151 100644
--- a/tools/objtool/check.h
+++ b/tools/objtool/check.h
@@ -31,7 +31,7 @@ struct insn_state {
        int stack_size;
        unsigned char type;
        bool bp_scratch;
-       bool drap, end;
+       bool drap, end, ac, ac_safe, df;
        int drap_reg, drap_offset;
        struct cfi_reg vals[CFI_NUM_REGS];
 };
diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h
index bc97ed86b9cd..064c3df31e40 100644
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -62,6 +62,7 @@ struct symbol {
        unsigned long offset;
        unsigned int len;
        struct symbol *pfunc, *cfunc;
+       bool ac_safe;
 };
 
 struct rela {

Reply via email to