Later commits will disallow runtime-mutable text in .noinstr sections in
order to safely defer instruction patching IPIs.

All static keys used in .noinstr sections have now been checked as being
either flagged as __ro_after_init, or as forceful static keys. Any
occurrence of this new warning would be the result of a code change that
will need looking at.

Suggested-by: Josh Poimboeuf <jpoim...@redhat.com>
Signed-off-by: Valentin Schneider <vschn...@redhat.com>
---
offset_of(static_key.type) and JUMP_TYPE_FORCEFUL would need to be shoved
into a somewhat standalone header file that could be included by objtool
itself.
---
 tools/objtool/Documentation/objtool.txt | 13 ++++++++
 tools/objtool/check.c                   | 41 +++++++++++++++++++++++++
 tools/objtool/include/objtool/check.h   |  1 +
 tools/objtool/include/objtool/special.h |  2 ++
 tools/objtool/special.c                 |  3 ++
 5 files changed, 60 insertions(+)

diff --git a/tools/objtool/Documentation/objtool.txt 
b/tools/objtool/Documentation/objtool.txt
index 7c3ee959b63c7..06fa285873387 100644
--- a/tools/objtool/Documentation/objtool.txt
+++ b/tools/objtool/Documentation/objtool.txt
@@ -447,6 +447,19 @@ the objtool maintainers.
    names and does not use module_init() / module_exit() macros to create
    them.
 
+13. file.o: warning: func()+0x2a: non __ro_after_init static key "key" in 
.noinstr section
+
+  This means that the noinstr function func() uses a static key that can be
+  modified at runtime. This is not allowed as noinstr functions rely on
+  containing stable instructions after init.
+
+  Check whether the static key in question can really be modified at runtime,
+  and if it is only enabled during init then mark it as __ro_after_init. If it
+  genuinely needs to be modified at runtime:
+
+  1) Directly rely on the underlying atomic count of they key in the noinstr
+     functions.
+  2) Mark the static key as forceful.
 
 If the error doesn't seem to make sense, it could be a bug in objtool.
 Feel free to ask the objtool maintainer for help.
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 00e25492f5065..c1fb02c326839 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -2056,6 +2056,9 @@ static int add_special_section_alts(struct objtool_file 
*file)
                alt->next = orig_insn->alts;
                orig_insn->alts = alt;
 
+               if (special_alt->key_sym)
+                       orig_insn->key_sym = special_alt->key_sym;
+
                list_del(&special_alt->list);
                free(special_alt);
        }
@@ -3605,6 +3608,41 @@ static int validate_return(struct symbol *func, struct 
instruction *insn, struct
        return 0;
 }
 
+static bool static_key_is_forceful(struct symbol *key)
+{
+       if (!strcmp(key->sec->name, ".data")) {
+               unsigned long data_offset = key->sec->data->d_off;
+               unsigned long key_offset = key->sym.st_value;
+               char* data = key->sec->data->d_buf;
+
+               /*
+                * offset_of(static_key.type)
+                *                        v
+                *                        v            JUMP_TYPE_FORCEFUL
+                *                        v                    v
+                */
+               return data[(key_offset + 8) - data_offset] & 0x4;
+       }
+
+       return false;
+}
+
+static int validate_static_key(struct instruction *insn, struct insn_state 
*state)
+{
+       if (state->noinstr && state->instr <= 0) {
+               if (static_key_is_forceful(insn->key_sym))
+                       return 0;
+
+               if ((strcmp(insn->key_sym->sec->name, ".data..ro_after_init"))) 
{
+                       WARN_INSN(insn, "non __ro_after_init static key \"%s\" 
in .noinstr section",
+                                 insn->key_sym->name);
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
 static struct instruction *next_insn_to_validate(struct objtool_file *file,
                                                 struct instruction *insn)
 {
@@ -3766,6 +3804,9 @@ static int validate_branch(struct objtool_file *file, 
struct symbol *func,
                if (handle_insn_ops(insn, next_insn, &state))
                        return 1;
 
+               if (insn->key_sym)
+                       validate_static_key(insn, &state);
+
                switch (insn->type) {
 
                case INSN_RETURN:
diff --git a/tools/objtool/include/objtool/check.h 
b/tools/objtool/include/objtool/check.h
index daa46f1f0965a..35dd21f8f41e1 100644
--- a/tools/objtool/include/objtool/check.h
+++ b/tools/objtool/include/objtool/check.h
@@ -77,6 +77,7 @@ struct instruction {
        struct symbol *sym;
        struct stack_op *stack_ops;
        struct cfi_state *cfi;
+       struct symbol *key_sym;
 };
 
 static inline struct symbol *insn_func(struct instruction *insn)
diff --git a/tools/objtool/include/objtool/special.h 
b/tools/objtool/include/objtool/special.h
index 86d4af9c5aa9d..0e61f34fe3a28 100644
--- a/tools/objtool/include/objtool/special.h
+++ b/tools/objtool/include/objtool/special.h
@@ -27,6 +27,8 @@ struct special_alt {
        struct section *new_sec;
        unsigned long new_off;
 
+       struct symbol *key_sym;
+
        unsigned int orig_len, new_len; /* group only */
 };
 
diff --git a/tools/objtool/special.c b/tools/objtool/special.c
index 097a69db82a0e..fefab2b471bf8 100644
--- a/tools/objtool/special.c
+++ b/tools/objtool/special.c
@@ -127,6 +127,9 @@ static int get_alt_entry(struct elf *elf, const struct 
special_entry *entry,
                        return -1;
                }
                alt->key_addend = reloc_addend(key_reloc);
+
+               reloc_to_sec_off(key_reloc, &sec, &offset);
+               alt->key_sym = find_symbol_by_offset(sec, offset & ~3);
        }
 
        return 0;
-- 
2.43.0


Reply via email to