@@ -14,11 +14,29 @@
#include <xen/vm_event.h>
#include <xen/virtual_region.h>
+#include <asm/endbr.h>
#include <asm/fixmap.h>
#include <asm/nmi.h>
#include <asm/livepatch.h>
#include <asm/setup.h>
+/*
+ * CET hotpatching support: We may have functions starting with an ENDBR64
+ * instruction that MUST remain the first instruction of the function, hence
+ * we need to move any hotpatch trampoline further into the function. For that
+ * we need to keep track of the patching offset used for any loaded hotpatch
+ * (to avoid racing against other fixups adding/removing ENDBR64 or similar
+ * instructions).
+ *
+ * We do so by making use of the existing opaque metadata area. We use its
+ * first 4 bytes to track the offset into the function used for patching and
+ * the remainder of the data to store overwritten code bytes.
+ */
+struct x86_livepatch_meta {
+ uint8_t patch_offset;
+ uint8_t instruction[LIVEPATCH_OPAQUE_SIZE - sizeof(uint8_t)];
+};
+
static bool has_active_waitqueue(const struct vm_event_domain *ved)
{
/* ved may be xzalloc()'d without INIT_LIST_HEAD() yet. */
@@ -104,18 +122,34 @@ void noinline arch_livepatch_revive(void)
int arch_livepatch_verify_func(const struct livepatch_func *func)
{
+ BUILD_BUG_ON(sizeof(struct x86_livepatch_meta) != LIVEPATCH_OPAQUE_SIZE);
+
/* If NOPing.. */
if ( !func->new_addr )
{
/* Only do up to maximum amount we can put in the ->opaque. */
- if ( func->new_size > sizeof(func->opaque) )
+ if ( func->new_size > sizeof_field(struct x86_livepatch_meta,
+ instruction) )
return -EOPNOTSUPP;
if ( func->old_size < func->new_size )
return -EINVAL;
}
- else if ( func->old_size < ARCH_PATCH_INSN_SIZE )
- return -EINVAL;
+ else
+ {
+ /*
+ * Space needed now depends on whether the target function
+ * starts with an ENDBR64 instruction.
+ */
+ uint8_t needed;
+
+ needed = ARCH_PATCH_INSN_SIZE;
+ if ( is_endbr64(func->old_addr) || was_endbr64(func->old_addr) )
+ needed += ENDBR64_LEN;
+
+ if ( func->old_size < needed )
+ return -EINVAL;
+ }
return 0;
}
@@ -127,15 +161,21 @@ int arch_livepatch_verify_func(const struct livepatch_func *func)
void noinline arch_livepatch_apply(struct livepatch_func *func)
{
uint8_t *old_ptr;
- uint8_t insn[sizeof(func->opaque)];
+ struct x86_livepatch_meta *lp;
+ uint8_t insn[sizeof(lp->instruction)];
unsigned int len;
+ lp = (struct x86_livepatch_meta *)func->opaque;
+ lp->patch_offset = 0;
old_ptr = func->old_addr;
len = livepatch_insn_len(func);
if ( !len )
return;
- memcpy(func->opaque, old_ptr, len);
+ if ( is_endbr64(old_ptr) )
+ lp->patch_offset += ENDBR64_LEN;
+
+ memcpy(lp->instruction, old_ptr + lp->patch_offset, len);
if ( func->new_addr )
{
int32_t val;
@@ -143,14 +183,15 @@ void noinline arch_livepatch_apply(struct livepatch_func *func)
BUILD_BUG_ON(ARCH_PATCH_INSN_SIZE != (1 + sizeof(val)));
insn[0] = 0xe9; /* Relative jump. */
- val = func->new_addr - func->old_addr - ARCH_PATCH_INSN_SIZE;
+ val = func->new_addr - (func->old_addr + lp->patch_offset
+ + ARCH_PATCH_INSN_SIZE);
memcpy(&insn[1], &val, sizeof(val));
}
else
add_nops(insn, len);
- memcpy(old_ptr, insn, len);
+ memcpy(old_ptr + lp->patch_offset, insn, len);
}
/*
@@ -159,7 +200,11 @@ void noinline arch_livepatch_apply(struct livepatch_func *func)
*/
void noinline arch_livepatch_revert(const struct livepatch_func *func)
{
- memcpy(func->old_addr, func->opaque, livepatch_insn_len(func));
+ const struct x86_livepatch_meta *lp;
+
+ lp = (struct x86_livepatch_meta *)func->opaque;
+
+ memcpy(func->old_addr + lp->patch_offset, lp->instruction, livepatch_insn_len(func));
}
/*