When self tests are active, walk all alternative replacements with
x86_decode_lite().
This checks that we can decode all instructions, and also lets us check that
disp8's don't leave the replacement block as such a case will definitely
malfunction.
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Jan Beulich <JBeulich@suse.com>
---
CC: Jan Beulich <JBeulich@suse.com>
CC: Roger Pau Monné <roger.pau@citrix.com>
v2:
* Rebase over API changes in patch 1
* Use +%lu and drop casts
* Swap to CONFIG_SELF_TESTS
---
xen/arch/x86/alternative.c | 52 ++++++++++++++++++++++++++++++++++++++
1 file changed, 52 insertions(+)
diff --git a/xen/arch/x86/alternative.c b/xen/arch/x86/alternative.c
index 1ba35cb9ede9..2dc759abd858 100644
--- a/xen/arch/x86/alternative.c
+++ b/xen/arch/x86/alternative.c
@@ -15,6 +15,7 @@
#include <asm/traps.h>
#include <asm/nmi.h>
#include <asm/nops.h>
+#include <asm/x86_emulate.h>
#include <xen/livepatch.h>
#define MAX_PATCH_LEN (255-1)
@@ -490,6 +491,57 @@ static void __init _alternative_instructions(bool force)
void __init alternative_instructions(void)
{
arch_init_ideal_nops();
+
+ /*
+ * Walk all replacement instructions with x86_decode_lite(). This checks
+ * both that we can decode all instructions within the replacement, and
+ * that any near branch with a disp8 stays within the alternative itself.
+ */
+ if ( IS_ENABLED(CONFIG_SELF_TESTS) )
+ {
+ struct alt_instr *a;
+
+ for ( a = __alt_instructions;
+ a < __alt_instructions_end; ++a )
+ {
+ void *repl = ALT_REPL_PTR(a);
+ void *ip = repl, *end = ip + a->repl_len;
+
+ if ( !a->repl_len )
+ continue;
+
+ for ( x86_decode_lite_t res; ip < end; ip += res.len )
+ {
+ const int8_t *d8;
+ const void *target;
+
+ res = x86_decode_lite(ip, end);
+
+ if ( res.len == 0 )
+ {
+ printk("Alt for %ps [%*ph]\n",
+ ALT_ORIG_PTR(a), a->repl_len, repl);
+ panic(" Unable to decode instruction at +%lu in alternative\n",
+ ip - repl);
+ }
+
+ if ( res.rel_sz != 1 )
+ continue;
+
+ d8 = res.rel;
+ target = ip + res.len + *d8;
+
+ if ( target < repl || target > end )
+ {
+ printk("Alt for %ps [%*ph]\n",
+ ALT_ORIG_PTR(a), a->repl_len, repl);
+ panic(" 'JMP/Jcc disp8' at +%lu leaves alternative block\n",
+ ip - repl);
+ }
+ }
+ }
+ }
+
_alternative_instructions(false);
}
--
2.39.5