On Wed, Mar 30, 2022 at 01:05:31PM +0200, Jan Beulich wrote:
> While not triggered by the trivial xen_nop in-tree patch on
> staging/master, that patch exposes a problem on the stable trees, where
> all functions have ENDBR inserted. When NOP-ing out a range, we need to
> account for this. Handle this right in livepatch_insn_len().
> 
> This requires livepatch_insn_len() to be called _after_ ->patch_offset
> was set. Note however that the earlier call cannot be deleted. In fact
> its result should have been used to guard the is_endbr64() /
> is_endbr64_poison() invocations - add the missing check now. While
> making that adjustment, also use the local variable "old_ptr"
> consistently.
> 
> Fixes: 6974c75180f1 ("xen/x86: Livepatch: support patching CET-enhanced 
> functions")

I have to admit I'm confused as to why that commit carries a Tested-by
from Arm.  Did Arm test the commit on x86 hardware?  Because that
commit only touches x86 specific code.

> Signed-off-by: Jan Beulich <jbeul...@suse.com>

FWIW, on the original implementation, I think it would have been
clearer to advance old_ptr and adjust the length?

> ---
> v2: Re-issue livepatch_insn_len(). Fix buffer overrun.
> ---
> Only build tested, as I don't have a live patching environment available.
> 
> For Arm this assumes that the patch_offset field starts out as zero; I
> think we can make such an assumption, yet otoh on x86 explicit
> initialization was added by the cited commit.
> 
> Note that the other use of is_endbr64() / is_endbr64_poison() in
> arch_livepatch_verify_func() would need the extra check only for
> cosmetic reasons, because ARCH_PATCH_INSN_SIZE > ENDBR64_LEN (5 > 4).
> Hence I'm not altering the code there.
> 
> --- a/xen/arch/x86/livepatch.c
> +++ b/xen/arch/x86/livepatch.c
> @@ -157,9 +157,15 @@ void noinline arch_livepatch_apply(struc
>       * loaded hotpatch (to avoid racing against other fixups adding/removing
>       * ENDBR64 or similar instructions).
>       */
> -    if ( is_endbr64(old_ptr) || is_endbr64_poison(func->old_addr) )
> +    if ( len >= ENDBR64_LEN &&
> +         (is_endbr64(old_ptr) || is_endbr64_poison(old_ptr)) )
>          func->patch_offset += ENDBR64_LEN;
>  
> +    /* This call must be re-issued once ->patch_offset has its final value. 
> */
> +    len = livepatch_insn_len(func);
> +    if ( !len )
> +        return;
> +
>      memcpy(func->opaque, old_ptr + func->patch_offset, len);
>      if ( func->new_addr )
>      {
> --- a/xen/include/xen/livepatch.h
> +++ b/xen/include/xen/livepatch.h
> @@ -90,7 +90,7 @@ static inline
>  unsigned int livepatch_insn_len(const struct livepatch_func *func)
>  {
>      if ( !func->new_addr )
> -        return func->new_size;
> +        return func->new_size - func->patch_offset;

Seeing as func->patch_offset is explicitly initialized in
arch_livepatch_apply for x86, do we also need to do the same on Arm
now that the field will be used by common code?

Maybe the initialization done in arch_livepatch_apply for x86 is not
strictly required.

Thanks, Roger.

Reply via email to