[RFC,v2,12/20] objtool: Warn about non __ro_after_init static key usage in .noinstr

Message ID 20230720163056.2564824-13-vschneid@redhat.com
State New
Headers
Series context_tracking,x86: Defer some IPIs until a user->kernel transition |

Commit Message

Valentin Schneider July 20, 2023, 4:30 p.m. UTC
  Later commits will depend on having no runtime-mutable text in early entry
code. (ab)use the .noinstr section as a marker of early entry code and warn
about static keys used in it that can be flipped at runtime.

Suggested-by: Josh Poimboeuf <jpoimboe@redhat.com>
Signed-off-by: Valentin Schneider <vschneid@redhat.com>
---
 tools/objtool/check.c                   | 20 ++++++++++++++++++++
 tools/objtool/include/objtool/check.h   |  1 +
 tools/objtool/include/objtool/special.h |  2 ++
 tools/objtool/special.c                 |  3 +++
 4 files changed, 26 insertions(+)
  

Comments

Josh Poimboeuf July 28, 2023, 3:35 p.m. UTC | #1
On Thu, Jul 20, 2023 at 05:30:48PM +0100, Valentin Schneider wrote:
> +static int validate_static_key(struct instruction *insn, struct insn_state *state)
> +{
> +	if (state->noinstr && state->instr <= 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",

For consistency with other warnings, this should start with a lowercase
"n" and the string literal should be on the same line as the WARN_INSN,
like

			WARN_INSN(insn, "non __ro_after_init static key \"%s\" in .noinstr section",
				  ...

> diff --git a/tools/objtool/special.c b/tools/objtool/special.c
> index 91b1950f5bd8a..1f76cfd815bf3 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 & ~2);

Bits 0 and 1 can both store data, should be ~3?
  
Josh Poimboeuf July 28, 2023, 4:02 p.m. UTC | #2
On Thu, Jul 20, 2023 at 05:30:48PM +0100, Valentin Schneider wrote:
> Later commits will depend on having no runtime-mutable text in early entry
> code. (ab)use the .noinstr section as a marker of early entry code and warn
> about static keys used in it that can be flipped at runtime.

Similar to my comment on patch 13, this could also use a short
justification for adding the feature, i.e. why runtime-mutable text
isn't going to be allowed in .noinstr.

Also, please add a short description of the warning (and why it exists)
to tools/objtool/Documentation/objtool.txt.
  
Valentin Schneider July 31, 2023, 11:18 a.m. UTC | #3
On 28/07/23 11:02, Josh Poimboeuf wrote:
> On Thu, Jul 20, 2023 at 05:30:48PM +0100, Valentin Schneider wrote:
>> Later commits will depend on having no runtime-mutable text in early entry
>> code. (ab)use the .noinstr section as a marker of early entry code and warn
>> about static keys used in it that can be flipped at runtime.
>
> Similar to my comment on patch 13, this could also use a short
> justification for adding the feature, i.e. why runtime-mutable text
> isn't going to be allowed in .noinstr.
>
> Also, please add a short description of the warning (and why it exists)
> to tools/objtool/Documentation/objtool.txt.
>

I had missed this, thanks for the pointer.

> --
> Josh
  
Valentin Schneider July 31, 2023, 11:18 a.m. UTC | #4
On 28/07/23 10:35, Josh Poimboeuf wrote:
> On Thu, Jul 20, 2023 at 05:30:48PM +0100, Valentin Schneider wrote:
>> +static int validate_static_key(struct instruction *insn, struct insn_state *state)
>> +{
>> +	if (state->noinstr && state->instr <= 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",
>
> For consistency with other warnings, this should start with a lowercase
> "n" and the string literal should be on the same line as the WARN_INSN,
> like
>
>                       WARN_INSN(insn, "non __ro_after_init static key \"%s\" in .noinstr section",
>                                 ...
>
>> diff --git a/tools/objtool/special.c b/tools/objtool/special.c
>> index 91b1950f5bd8a..1f76cfd815bf3 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 & ~2);
>
> Bits 0 and 1 can both store data, should be ~3?
>

Quite so, that needs to be the same as jump_entry_key().

> --
> Josh
  

Patch

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index d308330f2910e..d973bb4df4341 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -1968,6 +1968,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);
 	}
@@ -3476,6 +3479,20 @@  static int validate_return(struct symbol *func, struct instruction *insn, struct
 	return 0;
 }
 
+static int validate_static_key(struct instruction *insn, struct insn_state *state)
+{
+	if (state->noinstr && state->instr <= 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)
 {
@@ -3625,6 +3642,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 91b1950f5bd8a..1f76cfd815bf3 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 & ~2);
 	}
 
 	return 0;