From patchwork Wed Aug 24 02:22:10 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Konrad Rzeszutek Wilk X-Patchwork-Id: 9296727 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 2C060607D0 for ; Wed, 24 Aug 2016 02:25:04 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 1D33F28D10 for ; Wed, 24 Aug 2016 02:25:04 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 11D3128DCB; Wed, 24 Aug 2016 02:25:04 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 83E4128D10 for ; Wed, 24 Aug 2016 02:25:03 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1bcNqA-00035D-5r; Wed, 24 Aug 2016 02:22:54 +0000 Received: from mail6.bemta6.messagelabs.com ([193.109.254.103]) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1bcNq8-00032T-At for xen-devel@lists.xenproject.org; Wed, 24 Aug 2016 02:22:52 +0000 Received: from [85.158.143.35] by server-1.bemta-6.messagelabs.com id D1/3D-21406-BF40DB75; Wed, 24 Aug 2016 02:22:51 +0000 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFvrNLMWRWlGSWpSXmKPExsXSO6nOVfcXy95 wg/eHZS2+b5nM5MDocfjDFZYAxijWzLyk/IoE1oylZx8yFrwwqGi+upixgbFDvYuRi0NIYDKT xNmNU1khnG+MEusWXWGDcDYySjSv+AflTGCUaFi6HMjh4GATMJF4s8qxi5GTQ0QgQqL35SkWE JtZoI9RYt9pZhBbWMBZYv2x1YwgNouAqkTP0+dsIDavgJvEwb2PmEHGSAjISzy7XQ8S5hRwlz hzeBbYGCGgkkWH37CD2BICxhLtby+yTWDkW8DIsIpRozi1qCy1SNfYWC+pKDM9oyQ3MTNH19D ATC83tbg4MT01JzGpWC85P3cTIzBQGIBgB+PO9YGHGCU5mJREec3e7gkX4kvKT6nMSCzOiC8q zUktPsQow8GhJMF7lnlvuJBgUWp6akVaZg4wZGHSEhw8SiK8YsCwFeItLkjMLc5Mh0idYlSUE odICIAkMkrz4NpgcXKJUVZKmJcR6BAhnoLUotzMElT5V4ziHIxKwryvQbbzZOaVwE1/BbSYCW hxy/3dIItLEhFSUg2MSumMpaeeHgmef5TX0l2EmVm5yPlmyEOZ4u+pDwLchVrFZQ+uYb+nv3m 6WKqW752yQ09e/9KaG7Mg5I8Hx0L7u9W1i/yZOjPEJx7WqYn+5v7i11nWr7Lsv80bJHc+Sztk 9OPPbb3z8Yzp7/+sO3uGb58my+aDj0obiye/1dKtXGMjnFwuIPFQiaU4I9FQi7moOBEApnO1n Y4CAAA= X-Env-Sender: konrad.wilk@oracle.com X-Msg-Ref: server-9.tower-21.messagelabs.com!1472005368!29743622!1 X-Originating-IP: [141.146.126.69] X-SpamReason: No, hits=0.0 required=7.0 tests=sa_preprocessor: VHJ1c3RlZCBJUDogMTQxLjE0Ni4xMjYuNjkgPT4gMjc3MjE4\n X-StarScan-Received: X-StarScan-Version: 8.84; banners=-,-,- X-VirusChecked: Checked Received: (qmail 50734 invoked from network); 24 Aug 2016 02:22:50 -0000 Received: from aserp1040.oracle.com (HELO aserp1040.oracle.com) (141.146.126.69) by server-9.tower-21.messagelabs.com with DHE-RSA-AES256-GCM-SHA384 encrypted SMTP; 24 Aug 2016 02:22:50 -0000 Received: from aserv0022.oracle.com (aserv0022.oracle.com [141.146.126.234]) by aserp1040.oracle.com (Sentrion-MTA-4.3.2/Sentrion-MTA-4.3.2) with ESMTP id u7O2Mjum000788 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Wed, 24 Aug 2016 02:22:45 GMT Received: from aserv0121.oracle.com (aserv0121.oracle.com [141.146.126.235]) by aserv0022.oracle.com (8.14.4/8.14.4) with ESMTP id u7O2MjUF017991 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Wed, 24 Aug 2016 02:22:45 GMT Received: from abhmp0014.oracle.com (abhmp0014.oracle.com [141.146.116.20]) by aserv0121.oracle.com (8.13.8/8.13.8) with ESMTP id u7O2MiMI000784; Wed, 24 Aug 2016 02:22:44 GMT Received: from localhost.localdomain.com (/209.6.196.81) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Tue, 23 Aug 2016 19:22:44 -0700 From: Konrad Rzeszutek Wilk To: xen-devel@lists.xenproject.org, konrad@kernel.org, ross.lagerwall@citrix.com Date: Tue, 23 Aug 2016 22:22:10 -0400 Message-Id: <1472005332-32207-8-git-send-email-konrad.wilk@oracle.com> X-Mailer: git-send-email 2.4.11 In-Reply-To: <1472005332-32207-1-git-send-email-konrad.wilk@oracle.com> References: <1472005332-32207-1-git-send-email-konrad.wilk@oracle.com> X-Source-IP: aserv0022.oracle.com [141.146.126.234] Cc: Andrew Cooper , Jan Beulich , Konrad Rzeszutek Wilk Subject: [Xen-devel] [PATCH v4 7/9] livepatch: NOP if func->new_[addr] is zero. X-BeenThere: xen-devel@lists.xen.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: xen-devel-bounces@lists.xen.org Sender: "Xen-devel" X-Virus-Scanned: ClamAV using ClamSMTP The NOP functionality will NOP any of the code at the 'old_addr' or at 'name' if the 'new_addr' is zero. The purpose of this is to NOP out calls, such as: e8 <4-bytes-offset> (5 byte insn), or on ARM a 4 byte insn for branching. But on x86 we could NOP instructions that are much shorter or longer (up to 15 bytes). We need the EIP of where we need to the NOP, and that can be provided via the `old_addr` or `name`. If the `old_addr` is provided we will NOP 'new_size' amount of bytes at that location. If `name` is provided with the 'symbol+0x --- Cc: Konrad Rzeszutek Wilk Cc: Ross Lagerwall Cc: Jan Beulich Cc: Andrew Cooper v3: First submission v4: Fix description - e9 -> e8 Remove the restriction of only doing 5 or 4 bytes. Redo the patching code to deal with variable size of new_size. --- docs/misc/livepatch.markdown | 7 +++++-- xen/arch/x86/alternative.c | 2 +- xen/arch/x86/livepatch.c | 44 ++++++++++++++++++++++++++++++--------- xen/common/livepatch.c | 3 ++- xen/include/asm-x86/alternative.h | 1 + 5 files changed, 43 insertions(+), 14 deletions(-) diff --git a/docs/misc/livepatch.markdown b/docs/misc/livepatch.markdown index 7e82047..ad0df26 100644 --- a/docs/misc/livepatch.markdown +++ b/docs/misc/livepatch.markdown @@ -320,10 +320,13 @@ The size of the structure is 64 bytes on 64-bit hypervisors. It will be * `new_addr` is the address of the function that is replacing the old function. The address is filled in during relocation. The value **MUST** be - the address of the new function in the file. + either the address of the new function in the file, or zero if we are NOPing out + at `old_addr` (and `new_size` **MUST** not be zero). * `old_size` and `new_size` contain the sizes of the respective functions in bytes. - The value of `old_size` **MUST** not be zero. + The value of `old_size` **MUST** not be zero. If the value of `new_addr` is + zero then `new_size` determines how many instruction bytes to NOP (up to + platform limitations). * `version` is to be one. diff --git a/xen/arch/x86/alternative.c b/xen/arch/x86/alternative.c index be40b13..fd8528e 100644 --- a/xen/arch/x86/alternative.c +++ b/xen/arch/x86/alternative.c @@ -101,7 +101,7 @@ static void __init arch_init_ideal_nops(void) } /* Use this to add nops to a buffer, then text_poke the whole buffer. */ -static void init_or_livepatch add_nops(void *insns, unsigned int len) +void init_or_livepatch add_nops(void *insns, unsigned int len) { while ( len > 0 ) { diff --git a/xen/arch/x86/livepatch.c b/xen/arch/x86/livepatch.c index 1023fab..952e897 100644 --- a/xen/arch/x86/livepatch.c +++ b/xen/arch/x86/livepatch.c @@ -14,6 +14,7 @@ #include #define PATCH_INSN_SIZE 5 +#define MAX_INSN_SIZE 15 void arch_livepatch_quiesce(void) { @@ -29,8 +30,8 @@ void arch_livepatch_revive(void) int arch_livepatch_verify_func(const struct livepatch_func *func) { - /* No NOP patching yet. */ - if ( !func->new_size ) + /* If NOPing only do up to maximum instruction size. */ + if ( !func->new_addr && func->new_size > MAX_INSN_SIZE ) return -EOPNOTSUPP; if ( func->old_size < PATCH_INSN_SIZE ) @@ -39,25 +40,48 @@ int arch_livepatch_verify_func(const struct livepatch_func *func) return 0; } +static size_t get_len(const struct livepatch_func *func) +{ + if ( !func->new_addr ) + return func->new_size; + + return PATCH_INSN_SIZE; +} + void arch_livepatch_apply_jmp(struct livepatch_func *func) { - int32_t val; uint8_t *old_ptr; + uint8_t insn[MAX_INSN_SIZE]; + size_t len; - BUILD_BUG_ON(PATCH_INSN_SIZE > sizeof(func->opaque)); - BUILD_BUG_ON(PATCH_INSN_SIZE != (1 + sizeof(val))); + BUILD_BUG_ON(MAX_INSN_SIZE > sizeof(func->opaque)); old_ptr = func->old_addr; - memcpy(func->opaque, old_ptr, PATCH_INSN_SIZE); + len = get_len(func); + if ( !len ) + return; + + memcpy(func->opaque, old_ptr, len); + if ( func->new_addr ) + { + int32_t val; + + BUILD_BUG_ON(PATCH_INSN_SIZE != (1 + sizeof(val))); + + insn[0] = 0xe9; + val = func->new_addr - func->old_addr - PATCH_INSN_SIZE; + + memcpy(&insn[1], &val, sizeof(val)); + } + else + add_nops(&insn, len); - *old_ptr++ = 0xe9; /* Relative jump */ - val = func->new_addr - func->old_addr - PATCH_INSN_SIZE; - memcpy(old_ptr, &val, sizeof(val)); + memcpy(old_ptr, insn, len); } void arch_livepatch_revert_jmp(const struct livepatch_func *func) { - memcpy(func->old_addr, func->opaque, PATCH_INSN_SIZE); + memcpy(func->old_addr, func->opaque, get_len(func)); } /* Serialise the CPU pipeline. */ diff --git a/xen/common/livepatch.c b/xen/common/livepatch.c index c234424..dd24a07 100644 --- a/xen/common/livepatch.c +++ b/xen/common/livepatch.c @@ -566,7 +566,8 @@ static int prepare_payload(struct payload *payload, return -EOPNOTSUPP; } - if ( !f->new_addr || !f->new_size ) + /* 'old_addr', 'new_addr', 'new_size' can all be zero. */ + if ( !f->old_size ) { dprintk(XENLOG_ERR, LIVEPATCH "%s: Address or size fields are zero!\n", elf->name); diff --git a/xen/include/asm-x86/alternative.h b/xen/include/asm-x86/alternative.h index bce959f..acaeded 100644 --- a/xen/include/asm-x86/alternative.h +++ b/xen/include/asm-x86/alternative.h @@ -23,6 +23,7 @@ struct alt_instr { u8 replacementlen; /* length of new instruction, <= instrlen */ }; +extern void add_nops(void *insns, unsigned int len); /* Similar to apply_alternatives except it can be run with IRQs enabled. */ extern void apply_alternatives_nocheck(struct alt_instr *start, struct alt_instr *end);