Skip to content

Commit dbe4058

Browse files
suryasaimadhuIngo Molnar
authored and
Ingo Molnar
committed
x86/alternatives: Fix ALTERNATIVE_2 padding generation properly
Quentin caught a corner case with the generation of instruction padding in the ALTERNATIVE_2 macro: if len(orig_insn) < len(alt1) < len(alt2), then not enough padding gets added and that is not good(tm) as we could overwrite the beginning of the next instruction. Luckily, at the time of this writing, we don't have ALTERNATIVE_2() invocations which have that problem and even if we did, a simple fix would be to prepend the instructions with enough prefixes so that that corner case doesn't happen. However, best it would be if we fixed it properly. See below for a simple, abstracted example of what we're doing. So what we ended up doing is, we compute the max(len(alt1), len(alt2)) - len(orig_insn) and feed that value to the .skip gas directive. The max() cannot have conditionals due to gas limitations, thus the fancy integer math. With this patch, all ALTERNATIVE_2 sites get padded correctly; generating obscure test cases pass too: #define alt_max_short(a, b) ((a) ^ (((a) ^ (b)) & -(-((a) < (b))))) #define gen_skip(orig, alt1, alt2, marker) \ .skip -((alt_max_short(alt1, alt2) - (orig)) > 0) * \ (alt_max_short(alt1, alt2) - (orig)),marker .pushsection .text, "ax" .globl main main: gen_skip(1, 2, 4, 0x09) gen_skip(4, 1, 2, 0x10) ... .popsection Thanks to Quentin for catching it and double-checking the fix! Reported-by: Quentin Casasnovas <[email protected]> Signed-off-by: Borislav Petkov <[email protected]> Cc: Andy Lutomirski <[email protected]> Cc: Borislav Petkov <[email protected]> Cc: Brian Gerst <[email protected]> Cc: Denys Vlasenko <[email protected]> Cc: H. Peter Anvin <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Oleg Nesterov <[email protected]> Cc: Thomas Gleixner <[email protected]> Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Ingo Molnar <[email protected]>
1 parent 6b51311 commit dbe4058

File tree

3 files changed

+26
-8
lines changed

3 files changed

+26
-8
lines changed

arch/x86/include/asm/alternative-asm.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,22 @@
4545
.popsection
4646
.endm
4747

48+
#define old_len 141b-140b
49+
#define new_len1 144f-143f
50+
#define new_len2 145f-144f
51+
52+
/*
53+
* max without conditionals. Idea adapted from:
54+
* http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
55+
*/
56+
#define alt_max_short(a, b) ((a) ^ (((a) ^ (b)) & -(-((a) < (b)))))
57+
4858
.macro ALTERNATIVE_2 oldinstr, newinstr1, feature1, newinstr2, feature2
4959
140:
5060
\oldinstr
5161
141:
52-
.skip -(((144f-143f)-(141b-140b)) > 0) * ((144f-143f)-(141b-140b)),0x90
53-
.skip -(((145f-144f)-(144f-143f)-(141b-140b)) > 0) * ((145f-144f)-(144f-143f)-(141b-140b)),0x90
62+
.skip -((alt_max_short(new_len1, new_len2) - (old_len)) > 0) * \
63+
(alt_max_short(new_len1, new_len2) - (old_len)),0x90
5464
142:
5565

5666
.pushsection .altinstructions,"a"

arch/x86/include/asm/alternative.h

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,22 @@ static inline int alternatives_text_reserved(void *start, void *end)
9595
__OLDINSTR(oldinstr, num) \
9696
alt_end_marker ":\n"
9797

98+
/*
99+
* max without conditionals. Idea adapted from:
100+
* http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
101+
*
102+
* The additional "-" is needed because gas works with s32s.
103+
*/
104+
#define alt_max_short(a, b) "((" a ") ^ (((" a ") ^ (" b ")) & -(-((" a ") - (" b ")))))"
105+
98106
/*
99107
* Pad the second replacement alternative with additional NOPs if it is
100108
* additionally longer than the first replacement alternative.
101109
*/
102-
#define OLDINSTR_2(oldinstr, num1, num2) \
103-
__OLDINSTR(oldinstr, num1) \
104-
".skip -(((" alt_rlen(num2) ")-(" alt_rlen(num1) ")-(662b-661b)) > 0) * " \
105-
"((" alt_rlen(num2) ")-(" alt_rlen(num1) ")-(662b-661b)),0x90\n" \
110+
#define OLDINSTR_2(oldinstr, num1, num2) \
111+
"661:\n\t" oldinstr "\n662:\n" \
112+
".skip -((" alt_max_short(alt_rlen(num1), alt_rlen(num2)) " - (" alt_slen ")) > 0) * " \
113+
"(" alt_max_short(alt_rlen(num1), alt_rlen(num2)) " - (" alt_slen ")), 0x90\n" \
106114
alt_end_marker ":\n"
107115

108116
#define ALTINSTR_ENTRY(feature, num) \

arch/x86/kernel/alternative.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -369,11 +369,11 @@ void __init_or_module apply_alternatives(struct alt_instr *start,
369369
continue;
370370
}
371371

372-
DPRINTK("feat: %d*32+%d, old: (%p, len: %d), repl: (%p, len: %d)",
372+
DPRINTK("feat: %d*32+%d, old: (%p, len: %d), repl: (%p, len: %d), pad: %d",
373373
a->cpuid >> 5,
374374
a->cpuid & 0x1f,
375375
instr, a->instrlen,
376-
replacement, a->replacementlen);
376+
replacement, a->replacementlen, a->padlen);
377377

378378
DUMP_BYTES(instr, a->instrlen, "%p: old_insn: ", instr);
379379
DUMP_BYTES(replacement, a->replacementlen, "%p: rpl_insn: ", replacement);

0 commit comments

Comments
 (0)