diff --git a/xen/arch/x86/livepatch.c b/xen/arch/x86/livepatch.c index 49f0d902e5..e94ac9b228 100644 --- a/xen/arch/x86/livepatch.c +++ b/xen/arch/x86/livepatch.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -113,8 +114,20 @@ int arch_livepatch_verify_func(const struct livepatch_func *func) 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 = ARCH_PATCH_INSN_SIZE; + + if ( is_endbr64(func->old_addr) ) + needed += ENDBR64_LEN; + + if ( func->old_size < needed ) + return -EINVAL; + } return 0; } @@ -129,12 +142,24 @@ void noinline arch_livepatch_apply(struct livepatch_func *func) uint8_t insn[sizeof(func->opaque)]; unsigned int len; + func->patch_offset = 0; old_ptr = func->old_addr; len = livepatch_insn_len(func); if ( !len ) return; - memcpy(func->opaque, old_ptr, len); + /* + * 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). + */ + if ( is_endbr64(old_ptr) ) + func->patch_offset += ENDBR64_LEN; + + memcpy(func->opaque, old_ptr + func->patch_offset, len); if ( func->new_addr ) { int32_t val; @@ -142,14 +167,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 + func->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 + func->patch_offset, insn, len); } /* @@ -158,7 +184,8 @@ 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)); + memcpy(func->old_addr + func->patch_offset, func->opaque, + livepatch_insn_len(func)); } /* diff --git a/xen/include/public/sysctl.h b/xen/include/public/sysctl.h index a073647117..7439cc154f 100644 --- a/xen/include/public/sysctl.h +++ b/xen/include/public/sysctl.h @@ -869,7 +869,8 @@ struct livepatch_func { uint8_t version; /* MUST be LIVEPATCH_PAYLOAD_VERSION. */ uint8_t opaque[LIVEPATCH_OPAQUE_SIZE]; uint8_t applied; - uint8_t _pad[7]; + uint8_t patch_offset; + uint8_t _pad[6]; livepatch_expectation_t expect; }; typedef struct livepatch_func livepatch_func_t;