Skip to content

Commit 2b31e8e

Browse files
committed
x86/alternative: Optimize single-byte NOPs at an arbitrary position
Up until now the assumption was that an alternative patching site would have some instructions at the beginning and trailing single-byte NOPs (0x90) padding. Therefore, the patching machinery would go and optimize those single-byte NOPs into longer ones. However, this assumption is broken on 32-bit when code like hv_do_hypercall() in hyperv_init() would use the ratpoline speculation killer CALL_NOSPEC. The 32-bit version of that macro would align certain insns to 16 bytes, leading to the compiler issuing a one or more single-byte NOPs, depending on the holes it needs to fill for alignment. That would lead to the warning in optimize_nops() to fire: ------------[ cut here ]------------ Not a NOP at 0xc27fb598 WARNING: CPU: 0 PID: 0 at arch/x86/kernel/alternative.c:211 optimize_nops.isra.13 due to that function verifying whether all of the following bytes really are single-byte NOPs. Therefore, carve out the NOP padding into a separate function and call it for each NOP range beginning with a single-byte NOP. Fixes: 23c1ad5 ("x86/alternatives: Optimize optimize_nops()") Reported-by: Richard Narron <[email protected]> Signed-off-by: Borislav Petkov <[email protected]> Acked-by: Peter Zijlstra (Intel) <[email protected]> Link: https://bugzilla.kernel.org/show_bug.cgi?id=213301 Link: https://lkml.kernel.org/r/[email protected]
1 parent 9bfecd0 commit 2b31e8e

File tree

1 file changed

+46
-18
lines changed

1 file changed

+46
-18
lines changed

arch/x86/kernel/alternative.c

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -182,42 +182,70 @@ recompute_jump(struct alt_instr *a, u8 *orig_insn, u8 *repl_insn, u8 *insn_buff)
182182
n_dspl, (unsigned long)orig_insn + n_dspl + repl_len);
183183
}
184184

185+
/*
186+
* optimize_nops_range() - Optimize a sequence of single byte NOPs (0x90)
187+
*
188+
* @instr: instruction byte stream
189+
* @instrlen: length of the above
190+
* @off: offset within @instr where the first NOP has been detected
191+
*
192+
* Return: number of NOPs found (and replaced).
193+
*/
194+
static __always_inline int optimize_nops_range(u8 *instr, u8 instrlen, int off)
195+
{
196+
unsigned long flags;
197+
int i = off, nnops;
198+
199+
while (i < instrlen) {
200+
if (instr[i] != 0x90)
201+
break;
202+
203+
i++;
204+
}
205+
206+
nnops = i - off;
207+
208+
if (nnops <= 1)
209+
return nnops;
210+
211+
local_irq_save(flags);
212+
add_nops(instr + off, nnops);
213+
local_irq_restore(flags);
214+
215+
DUMP_BYTES(instr, instrlen, "%px: [%d:%d) optimized NOPs: ", instr, off, i);
216+
217+
return nnops;
218+
}
219+
185220
/*
186221
* "noinline" to cause control flow change and thus invalidate I$ and
187222
* cause refetch after modification.
188223
*/
189224
static void __init_or_module noinline optimize_nops(struct alt_instr *a, u8 *instr)
190225
{
191-
unsigned long flags;
192226
struct insn insn;
193-
int nop, i = 0;
227+
int i = 0;
194228

195229
/*
196-
* Jump over the non-NOP insns, the remaining bytes must be single-byte
197-
* NOPs, optimize them.
230+
* Jump over the non-NOP insns and optimize single-byte NOPs into bigger
231+
* ones.
198232
*/
199233
for (;;) {
200234
if (insn_decode_kernel(&insn, &instr[i]))
201235
return;
202236

237+
/*
238+
* See if this and any potentially following NOPs can be
239+
* optimized.
240+
*/
203241
if (insn.length == 1 && insn.opcode.bytes[0] == 0x90)
204-
break;
205-
206-
if ((i += insn.length) >= a->instrlen)
207-
return;
208-
}
242+
i += optimize_nops_range(instr, a->instrlen, i);
243+
else
244+
i += insn.length;
209245

210-
for (nop = i; i < a->instrlen; i++) {
211-
if (WARN_ONCE(instr[i] != 0x90, "Not a NOP at 0x%px\n", &instr[i]))
246+
if (i >= a->instrlen)
212247
return;
213248
}
214-
215-
local_irq_save(flags);
216-
add_nops(instr + nop, i - nop);
217-
local_irq_restore(flags);
218-
219-
DUMP_BYTES(instr, a->instrlen, "%px: [%d:%d) optimized NOPs: ",
220-
instr, nop, a->instrlen);
221249
}
222250

223251
/*

0 commit comments

Comments
 (0)