|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [xen staging] x86/altcall: Introduce new simpler scheme
commit ac02ee9c82d115a824629edb10c14f260108ef56
Author: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
AuthorDate: Sat Apr 19 23:05:52 2025 +0100
Commit: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
CommitDate: Wed Apr 23 19:37:57 2025 +0100
x86/altcall: Introduce new simpler scheme
Encoding altcalls as regular alternatives leads to an unreasonable amount of
complexity in _apply_alternatives().
Introduce apply_alt_calls(), and an .alt_call_sites section which simply
tracks the source address (relative, to save on space). That's literally
all
that is needed in order to devirtualise the function pointers.
apply_alt_calls() is mostly as per _apply_alternatives(), except the size is
known to be 6 bytes. Drop the logic for JMP *RIPREL, as there's no support
for tailcall optimisations, nor a feasbile plan on how to introduce support.
Pad with a redundant prefix to avoid needing a separate NOP on the end.
Wire it up in nmi_apply_alternatives(), although the section is empty at
this
juncture so nothing happens in practice.
Signed-off-by: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
Reviewed-by: Jan Beulich <jbeulich@xxxxxxxx>
---
xen/arch/x86/alternative.c | 94 +++++++++++++++++++++++++++++
xen/arch/x86/include/asm/alternative-call.h | 7 +++
xen/arch/x86/xen.lds.S | 4 ++
3 files changed, 105 insertions(+)
diff --git a/xen/arch/x86/alternative.c b/xen/arch/x86/alternative.c
index 4b9f8d8601..f6594e21a1 100644
--- a/xen/arch/x86/alternative.c
+++ b/xen/arch/x86/alternative.c
@@ -388,6 +388,92 @@ static int init_or_livepatch _apply_alternatives(struct
alt_instr *start,
return 0;
}
+/*
+ * At build time, alternative calls are emitted as:
+ * ff 15 xx xx xx xx => call *disp32(%rip)
+ *
+ * During boot, we devirtualise by editing to:
+ * 2e e8 xx xx xx xx => cs call disp32
+ *
+ * or, if the function pointer is still NULL, poison to:
+ * 0f 0b 0f 0b 0f 0b => ud2a (x3)
+ */
+static int init_or_livepatch apply_alt_calls(
+ const struct alt_call *start, const struct alt_call *end)
+{
+ const struct alt_call *a;
+
+ for ( a = start; a < end; a++ )
+ {
+ const uint8_t *dest;
+ uint8_t buf[6], *orig = ALT_CALL_PTR(a);
+ long disp;
+
+ /* It's likely that this won't change, but check just to be safe. */
+ BUILD_BUG_ON(ALT_CALL_LEN(a) != 6);
+
+ if ( orig[0] != 0xff || orig[1] != 0x15 )
+ {
+ printk(XENLOG_ERR
+ "Altcall for %ps [%6ph] not CALL *RIPREL\n",
+ orig, orig);
+ return -EINVAL;
+ }
+
+ disp = *(int32_t *)(orig + 2);
+ dest = *(const void **)(orig + 6 + disp);
+
+ if ( dest )
+ {
+ /*
+ * When building for CET-IBT, all function pointer targets
+ * should have an endbr64 instruction.
+ *
+ * If this is not the case, leave a warning because
+ * something is probably wrong with the build. A CET-IBT
+ * enabled system might have exploded already.
+ *
+ * Otherwise, skip the endbr64 instruction. This is a
+ * marginal perf improvement which saves on instruction
+ * decode bandwidth.
+ */
+ if ( IS_ENABLED(CONFIG_XEN_IBT) )
+ {
+ if ( is_endbr64(dest) )
+ dest += ENDBR64_LEN;
+ else
+ printk(XENLOG_WARNING
+ "Altcall %ps dest %ps has no endbr64\n",
+ orig, dest);
+ }
+
+ disp = dest - (orig + 6);
+ ASSERT(disp == (int32_t)disp);
+
+ buf[0] = 0x2e;
+ buf[1] = 0xe8;
+ *(int32_t *)(buf + 2) = disp;
+ }
+ else
+ {
+ /*
+ * The function pointer is still NULL. Seal the whole call, as
+ * it's not used.
+ */
+ buf[0] = 0x0f;
+ buf[1] = 0x0b;
+ buf[2] = 0x0f;
+ buf[3] = 0x0b;
+ buf[4] = 0x0f;
+ buf[5] = 0x0b;
+ }
+
+ text_poke(orig, buf, sizeof(buf));
+ }
+
+ return 0;
+}
+
#ifdef CONFIG_LIVEPATCH
int apply_alternatives(struct alt_instr *start, struct alt_instr *end)
{
@@ -401,6 +487,7 @@ static unsigned int __initdata alt_todo;
static unsigned int __initdata alt_done;
extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
+extern struct alt_call __alt_call_sites_start[], __alt_call_sites_end[];
/*
* At boot time, we patch alternatives in NMI context. This means that the
@@ -435,6 +522,13 @@ static int __init cf_check nmi_apply_alternatives(
if ( rc )
panic("Unable to apply alternatives: %d\n", rc);
+ if ( alt_todo & ALT_CALLS )
+ {
+ rc = apply_alt_calls(__alt_call_sites_start, __alt_call_sites_end);
+ if ( rc )
+ panic("Unable to apply alternative calls: %d\n", rc);
+ }
+
/*
* Reinstate perms on .text to be RX. This also cleans out the dirty
* bits, which matters when CET Shstk is active.
diff --git a/xen/arch/x86/include/asm/alternative-call.h
b/xen/arch/x86/include/asm/alternative-call.h
index 828ea32a96..49a04a7cc4 100644
--- a/xen/arch/x86/include/asm/alternative-call.h
+++ b/xen/arch/x86/include/asm/alternative-call.h
@@ -4,6 +4,13 @@
#include <asm/alternative.h>
+/* Simply the relative position of the source call. */
+struct alt_call {
+ int32_t offset;
+};
+#define ALT_CALL_PTR(a) ((void *)&(a)->offset + (a)->offset)
+#define ALT_CALL_LEN(a) (6)
+
/*
* Machinery to allow converting indirect to direct calls, when the called
* function is determined once at boot and later never changed.
diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S
index d4dd6434c4..53bafc98a5 100644
--- a/xen/arch/x86/xen.lds.S
+++ b/xen/arch/x86/xen.lds.S
@@ -260,6 +260,10 @@ SECTIONS
__alt_instructions = .;
*(.altinstructions)
__alt_instructions_end = .;
+ . = ALIGN(4);
+ __alt_call_sites_start = .;
+ *(.alt_call_sites)
+ __alt_call_sites_end = .;
LOCK_PROFILE_DATA
--
generated by git-patchbot for /home/xen/git/xen.git#staging
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |