diff --git a/xen/arch/x86/alternative.c b/xen/arch/x86/alternative.c index 7824053c9d..1ba35cb9ed 100644 --- a/xen/arch/x86/alternative.c +++ b/xen/arch/x86/alternative.c @@ -175,9 +175,9 @@ extern void *const __initdata_cf_clobber_end[]; * invocation, such that no CALLs/JMPs to NULL pointers will be left * around. See also the further comment below. */ -static void init_or_livepatch _apply_alternatives(struct alt_instr *start, - struct alt_instr *end, - bool force) +static int init_or_livepatch _apply_alternatives(struct alt_instr *start, + struct alt_instr *end, + bool force) { struct alt_instr *a, *base; @@ -198,9 +198,29 @@ static void init_or_livepatch _apply_alternatives(struct alt_instr *start, uint8_t buf[MAX_PATCH_LEN]; unsigned int total_len = a->orig_len + a->pad_len; - BUG_ON(a->repl_len > total_len); - BUG_ON(total_len > sizeof(buf)); - BUG_ON(a->cpuid >= NCAPINTS * 32); + if ( a->repl_len > total_len ) + { + printk(XENLOG_ERR + "Alt for %ps, replacement size %#x larger than origin %#x\n", + ALT_ORIG_PTR(a), a->repl_len, total_len); + return -ENOSPC; + } + + if ( total_len > sizeof(buf) ) + { + printk(XENLOG_ERR + "Alt for %ps, origin size %#x bigger than buffer %#zx\n", + ALT_ORIG_PTR(a), total_len, sizeof(buf)); + return -ENOSPC; + } + + if ( a->cpuid >= NCAPINTS * 32 ) + { + printk(XENLOG_ERR + "Alt for %ps, feature %#x outside of featureset range %#x\n", + ALT_ORIG_PTR(a), a->cpuid, NCAPINTS * 32); + return -ERANGE; + } /* * Detect sequences of alt_instr's patching the same origin site, and @@ -356,12 +376,14 @@ static void init_or_livepatch _apply_alternatives(struct alt_instr *start, printk("altcall: Optimised away %u endbr64 instructions\n", clobbered); } + + return 0; } #ifdef CONFIG_LIVEPATCH -void apply_alternatives(struct alt_instr *start, struct alt_instr *end) +int apply_alternatives(struct alt_instr *start, struct alt_instr *end) { - _apply_alternatives(start, end, true); + return _apply_alternatives(start, end, true); } #endif @@ -383,6 +405,8 @@ static int __init cf_check nmi_apply_alternatives( */ if ( !(alt_done & alt_todo) ) { + int rc; + /* * Relax perms on .text to be RWX, so we can modify them. * @@ -394,8 +418,10 @@ static int __init cf_check nmi_apply_alternatives( PAGE_HYPERVISOR_RWX); flush_local(FLUSH_TLB_GLOBAL); - _apply_alternatives(__alt_instructions, __alt_instructions_end, - alt_done); + rc = _apply_alternatives(__alt_instructions, __alt_instructions_end, + alt_done); + if ( rc ) + panic("Unable to apply alternatives: %d\n", rc); /* * Reinstate perms on .text to be RX. This also cleans out the dirty diff --git a/xen/arch/x86/include/asm/alternative.h b/xen/arch/x86/include/asm/alternative.h index a86eadfaec..69555d781e 100644 --- a/xen/arch/x86/include/asm/alternative.h +++ b/xen/arch/x86/include/asm/alternative.h @@ -24,7 +24,7 @@ struct __packed alt_instr { extern void add_nops(void *insns, unsigned int len); /* Similar to alternative_instructions except it can be run with IRQs enabled. */ -extern void apply_alternatives(struct alt_instr *start, struct alt_instr *end); +extern int apply_alternatives(struct alt_instr *start, struct alt_instr *end); extern void alternative_instructions(void); extern void alternative_branches(void); diff --git a/xen/common/livepatch.c b/xen/common/livepatch.c index c519f466df..86f4035f8c 100644 --- a/xen/common/livepatch.c +++ b/xen/common/livepatch.c @@ -901,7 +901,15 @@ static int prepare_payload(struct payload *payload, return -EINVAL; } } - apply_alternatives(start, end); + + rc = apply_alternatives(start, end); + if ( rc ) + { + printk(XENLOG_ERR LIVEPATCH "%s applying alternatives failed: %d\n", + elf->name, rc); + return rc; + } + alt_done:; #else printk(XENLOG_ERR LIVEPATCH "%s: We don't support alternative patching\n",