From patchwork Tue Nov 26 10:07:57 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Wieczorkiewicz, Pawel" X-Patchwork-Id: 11261853 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 817A71393 for ; Tue, 26 Nov 2019 10:10:30 +0000 (UTC) Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 5300E2089D for ; Tue, 26 Nov 2019 10:10:30 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=amazon.de header.i=@amazon.de header.b="r2E6fWIF" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 5300E2089D Authentication-Results: mail.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=amazon.de Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=xen-devel-bounces@lists.xenproject.org Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.89) (envelope-from ) id 1iZXmr-00081p-W1; Tue, 26 Nov 2019 10:09:37 +0000 Received: from us1-rack-iad1.inumbo.com ([172.99.69.81]) by lists.xenproject.org with esmtp (Exim 4.89) (envelope-from ) id 1iZXmq-00080k-Jo for xen-devel@lists.xenproject.org; Tue, 26 Nov 2019 10:09:36 +0000 X-Inumbo-ID: cf8affe8-1034-11ea-a55d-bc764e2007e4 Received: from smtp-fw-9102.amazon.com (unknown [207.171.184.29]) by us1-rack-iad1.inumbo.com (Halon) with ESMTPS id cf8affe8-1034-11ea-a55d-bc764e2007e4; Tue, 26 Nov 2019 10:09:19 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=amazon.de; i=@amazon.de; q=dns/txt; s=amazon201209; t=1574762960; x=1606298960; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version; bh=fNjYgBAw/uHzP4xLJY5R/p/zMZawSGY7b1wg203bKTc=; b=r2E6fWIFpcsezqOHMkIGGL5SPm8ISOkG+Je5pFUSbhVYhgOP0duCjgx8 W2Mb4/rPwMzmxW6uDDPYIj9jDfsQGL9tjwgmMA23DBeSydYw4pk8cy6eD Y3oR46tOyXBgudAxiL6BOkh2EWkN2sVL9AaxFy8MN82Vf2AibwdyH8I13 M=; IronPort-SDR: e6JZVUf9CAP171SjTSC3uCImgAkbir8GOwqRL9QYROMdTo5z3Ob1hfFQihpY59u9IvXyfKP7yz ByiravbJqXtA== X-IronPort-AV: E=Sophos;i="5.69,245,1571702400"; d="scan'208";a="9921143" Received: from sea32-co-svc-lb4-vlan3.sea.corp.amazon.com (HELO email-inbound-relay-1e-27fb8269.us-east-1.amazon.com) ([10.47.23.38]) by smtp-border-fw-out-9102.sea19.amazon.com with ESMTP; 26 Nov 2019 10:08:50 +0000 Received: from EX13MTAUEA001.ant.amazon.com (iad55-ws-svc-p15-lb9-vlan3.iad.amazon.com [10.40.159.166]) by email-inbound-relay-1e-27fb8269.us-east-1.amazon.com (Postfix) with ESMTPS id 419ABA2955; Tue, 26 Nov 2019 10:08:46 +0000 (UTC) Received: from EX13D05EUB003.ant.amazon.com (10.43.166.253) by EX13MTAUEA001.ant.amazon.com (10.43.61.82) with Microsoft SMTP Server (TLS) id 15.0.1367.3; Tue, 26 Nov 2019 10:08:33 +0000 Received: from EX13MTAUEB001.ant.amazon.com (10.43.60.96) by EX13D05EUB003.ant.amazon.com (10.43.166.253) with Microsoft SMTP Server (TLS) id 15.0.1367.3; Tue, 26 Nov 2019 10:08:32 +0000 Received: from dev-dsk-wipawel-1a-0c4e6d58.eu-west-1.amazon.com (10.4.134.33) by mail-relay.amazon.com (10.43.60.129) with Microsoft SMTP Server id 15.0.1367.3 via Frontend Transport; Tue, 26 Nov 2019 10:08:30 +0000 From: Pawel Wieczorkiewicz To: Date: Tue, 26 Nov 2019 10:07:57 +0000 Message-ID: <20191126100801.124844-9-wipawel@amazon.de> X-Mailer: git-send-email 2.16.5 In-Reply-To: <20191126100801.124844-1-wipawel@amazon.de> References: <20191126100801.124844-1-wipawel@amazon.de> MIME-Version: 1.0 Precedence: Bulk Subject: [Xen-devel] [PATCH v6 08/12] livepatch: Add support for inline asm livepatching expectations X-BeenThere: xen-devel@lists.xenproject.org X-Mailman-Version: 2.1.23 List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Cc: Stefano Stabellini , Julien Grall , Wei Liu , Konrad Rzeszutek Wilk , George Dunlap , Andrew Cooper , Ross Lagerwall , Ian Jackson , mpohlack@amazon.com, Pawel Wieczorkiewicz , Jan Beulich Errors-To: xen-devel-bounces@lists.xenproject.org Sender: "Xen-devel" This is the initial implementation of the expectations enhancement to improve inline asm livepatching. Expectations are designed as optional feature, since the main use of them is planned for inline asm livepatching. The flag enabled allows to control the expectation state. Each expectation has data and len fields that describe the data that is expected to be found at a given patching (old_addr) location. The len must not exceed the data array size. The data array size follows the size of the opaque array, since the opaque array holds the original data and therefore must match what is specified in the expectation (if enabled). The payload structure is modified as each expectation structure is part of the livepatch_func structure and hence extends the payload. Each expectation is checked prior to the apply action (i.e. as late as possible to check against the most current state of the code). For the replace action a new payload's expectations are checked AFTER all applied payloads are successfully reverted, but BEFORE new payload is applied. That breaks the replace action's atomicity and in case of an expectation check failure would leave a system with all payloads reverted. That is obviously insecure. Use it with caution and act upon replace errors! Signed-off-by: Pawel Wieczorkiewicz Reviewed-by: Andra-Irina Paraschiv Reviewed-by: Martin Pohlack Reviewed-by: Norbert Manthey Signed-off-by: Konrad Rzeszutek Wilk Reviewed-by: Ross Lagerwall --- Changed since v3: * Fix for test's automatic expectation generation Changed since v2: * Add rsv in the field. Update the size of the structure. (Konrad) * Fix expectation test to work also on Arm Changed since v1: * added corresponding documentation * added tests --- .gitignore | 1 + docs/misc/livepatch.pandoc | 35 +++++++++++++- xen/common/livepatch.c | 74 ++++++++++++++++++++++++++++++ xen/include/public/sysctl.h | 16 ++++++- xen/test/livepatch/Makefile | 27 ++++++++++- xen/test/livepatch/xen_expectations.c | 41 +++++++++++++++++ xen/test/livepatch/xen_expectations_fail.c | 42 +++++++++++++++++ 7 files changed, 231 insertions(+), 5 deletions(-) create mode 100644 xen/test/livepatch/xen_expectations.c create mode 100644 xen/test/livepatch/xen_expectations_fail.c diff --git a/.gitignore b/.gitignore index ab765a5cdd..017856c93e 100644 --- a/.gitignore +++ b/.gitignore @@ -308,6 +308,7 @@ xen/include/xen/acm_policy.h xen/include/xen/compile.h xen/include/xen/lib/x86/cpuid-autogen.h xen/test/livepatch/config.h +xen/test/livepatch/expect_config.h xen/test/livepatch/*.livepatch xen/tools/kconfig/.tmp_gtkcheck xen/tools/kconfig/.tmp_qtcheck diff --git a/docs/misc/livepatch.pandoc b/docs/misc/livepatch.pandoc index 02348dd49e..4f9238d235 100644 --- a/docs/misc/livepatch.pandoc +++ b/docs/misc/livepatch.pandoc @@ -300,10 +300,11 @@ which describe the functions to be patched: /* Added to livepatch payload version 2: */ uint8_t applied; uint8_t _pad[7]; + livepatch_expectation_t expect; }; -The size of the structure is 64 bytes on 64-bit hypervisors. It will be -52 on 32-bit hypervisors. +The size of the structure is 104 bytes on 64-bit hypervisors. It will be +92 on 32-bit hypervisors. The version 2 of the payload adds additional 8 bytes to the structure size. * `name` is the symbol name of the old function. Only used if `old_addr` is @@ -336,6 +337,28 @@ The version 2 of the payload adds the following fields to the structure: * `applied` tracks function's applied/reverted state. It has a boolean type either LIVEPATCH_FUNC_NOT_APPLIED or LIVEPATCH_FUNC_APPLIED. * `_pad[7]` adds padding to align to 8 bytes. + * `expect` is an optional structure containing expected to-be-replaced data + (mostly for inline asm patching). The `expect` structure format is: + + struct livepatch_expectation { + uint8_t enabled : 1; + uint8_t len : 5; + uint8_t rsv: 2; + uint8_t data[LIVEPATCH_OPAQUE_SIZE]; /* Same size as opaque[] buffer of + struct livepatch_func. This is the + max number of bytes to be patched */ + }; + typedef struct livepatch_expectation livepatch_expectation_t; + + * `enabled` allows to enable the expectation check for given function. + Default state is disabled. + * `len` specifies the number of valid bytes in `data` array. 5 bits is + enough to specify values up to 32 (of bytes), which is above the array + size. + * `rsv` reserved bitfields. **MUST** be zero. + * `data` contains expected bytes of content to be replaced. Same size as + `opaque` buffer of `struct livepatch_func` (max number of bytes to be + patched). The size of the `livepatch_func` array is determined from the ELF section size. @@ -391,6 +414,7 @@ A simple example of what a payload file can be: /* Added to livepatch payload version 2: */ uint8_t applied; uint8_t _pad[7]; + livepatch_expectation_t expect; }; /* Our replacement function for xen_extra_version. */ @@ -408,6 +432,13 @@ A simple example of what a payload file can be: .old_addr = (void *)0xffff82d08013963c, /* Extracted from xen-syms. */ .new_size = 13, /* To be be computed by scripts. */ .old_size = 13, /* -----------""--------------- */ + /* Added to livepatch payload version 2: */ + .expect = { /* All fields to be filled manually */ + .enabled = 1, + .len = 5, + .rsv = 0, + .data = { 0x48, 0x8d, 0x05, 0x33, 0x1C } + }, } __attribute__((__section__(".livepatch.funcs"))); Code must be compiled with `-fPIC`. diff --git a/xen/common/livepatch.c b/xen/common/livepatch.c index 81e1b474cd..9923df9ed2 100644 --- a/xen/common/livepatch.c +++ b/xen/common/livepatch.c @@ -560,6 +560,61 @@ static int check_patching_sections(const struct livepatch_elf *elf) return 0; } +static inline int livepatch_verify_expectation_fn(const struct livepatch_func *func) +{ + const livepatch_expectation_t *exp = &func->expect; + + /* Ignore disabled expectations. */ + if ( !exp->enabled ) + return 0; + + /* There is nothing to expect */ + if ( !func->old_addr ) + return -EFAULT; + + if ( exp->len > sizeof(exp->data)) + return -EOVERFLOW; + + if ( exp->rsv ) + return -EINVAL; + + /* Incorrect expectation */ + if ( func->old_size < exp->len ) + return -ERANGE; + + if ( memcmp(func->old_addr, exp->data, exp->len) ) + { + printk(XENLOG_ERR LIVEPATCH "%s: expectation failed: expected:%*phN, actual:%*phN\n", + func->name, exp->len, exp->data, exp->len, func->old_addr); + return -EINVAL; + } + + return 0; +} + +static inline int livepatch_check_expectations(const struct payload *payload) +{ + int i, rc; + + printk(XENLOG_INFO LIVEPATCH "%s: Verifying enabled expectations for all functions\n", + payload->name); + + for ( i = 0; i < payload->nfuncs; i++ ) + { + const struct livepatch_func *func = &(payload->funcs[i]); + + rc = livepatch_verify_expectation_fn(func); + if ( rc ) + { + printk(XENLOG_ERR LIVEPATCH "%s: expectations of %s failed (rc=%d), aborting!\n", + payload->name, func->name ?: "unknown", rc); + return rc; + } + } + + return 0; +} + /* * Lookup specified section and when exists assign its address to a specified hook. * Perform section pointer and size validation: single hook sections must contain a @@ -1352,6 +1407,20 @@ static void livepatch_do_action(void) if ( rc == 0 ) { + /* + * Make sure all expectation requirements are met. + * Beware all the payloads are reverted at this point. + * If expectations are not met the system is left in a + * completely UNPATCHED state! + */ + rc = livepatch_check_expectations(data); + if ( rc ) + { + printk(XENLOG_ERR LIVEPATCH "%s: SYSTEM MIGHT BE INSECURE: " + "Replace action has been aborted after reverting ALL payloads!\n", data->name); + break; + } + if ( is_hook_enabled(data->hooks.apply.action) ) { printk(XENLOG_INFO LIVEPATCH "%s: Calling apply action hook function\n", data->name); @@ -1808,6 +1877,11 @@ static int livepatch_action(struct xen_sysctl_livepatch_action *action) break; } + /* Make sure all expectation requirements are met. */ + rc = livepatch_check_expectations(data); + if ( rc ) + break; + if ( is_hook_enabled(data->hooks.apply.pre) ) { printk(XENLOG_INFO LIVEPATCH "%s: Calling pre-apply hook function\n", data->name); diff --git a/xen/include/public/sysctl.h b/xen/include/public/sysctl.h index 4f52ad581f..ec6f16f0e6 100644 --- a/xen/include/public/sysctl.h +++ b/xen/include/public/sysctl.h @@ -840,6 +840,19 @@ struct xen_sysctl_cpu_featureset { * We guard this with __XEN__ as toolstacks SHOULD not use it. */ #ifdef __XEN__ +#define LIVEPATCH_OPAQUE_SIZE 31 + +struct livepatch_expectation { + uint8_t enabled : 1; + uint8_t len : 5; /* Length of data up to LIVEPATCH_OPAQUE_SIZE + (5 bits is enough for now) */ + uint8_t rsv : 2; /* Reserved. Zero value */ + uint8_t data[LIVEPATCH_OPAQUE_SIZE]; /* Same size as opaque[] buffer of + struct livepatch_func. This is the + max number of bytes to be patched */ +}; +typedef struct livepatch_expectation livepatch_expectation_t; + typedef enum livepatch_func_state { LIVEPATCH_FUNC_NOT_APPLIED, LIVEPATCH_FUNC_APPLIED @@ -852,9 +865,10 @@ struct livepatch_func { uint32_t new_size; uint32_t old_size; uint8_t version; /* MUST be LIVEPATCH_PAYLOAD_VERSION. */ - uint8_t opaque[31]; + uint8_t opaque[LIVEPATCH_OPAQUE_SIZE]; uint8_t applied; uint8_t _pad[7]; + livepatch_expectation_t expect; }; typedef struct livepatch_func livepatch_func_t; #endif diff --git a/xen/test/livepatch/Makefile b/xen/test/livepatch/Makefile index 23113d3418..cebd3eb49c 100644 --- a/xen/test/livepatch/Makefile +++ b/xen/test/livepatch/Makefile @@ -27,6 +27,8 @@ LIVEPATCH_ACTION_HOOKS_NOFUNC := xen_action_hooks_nofunc.livepatch LIVEPATCH_ACTION_HOOKS_MARKER:= xen_action_hooks_marker.livepatch LIVEPATCH_ACTION_HOOKS_NOAPPLY:= xen_action_hooks_noapply.livepatch LIVEPATCH_ACTION_HOOKS_NOREVERT:= xen_action_hooks_norevert.livepatch +LIVEPATCH_EXPECTATIONS:= xen_expectations.livepatch +LIVEPATCH_EXPECTATIONS_FAIL:= xen_expectations_fail.livepatch LIVEPATCHES += $(LIVEPATCH) LIVEPATCHES += $(LIVEPATCH_BYE) @@ -40,6 +42,8 @@ LIVEPATCHES += $(LIVEPATCH_ACTION_HOOKS_NOFUNC) LIVEPATCHES += $(LIVEPATCH_ACTION_HOOKS_MARKER) LIVEPATCHES += $(LIVEPATCH_ACTION_HOOKS_NOAPPLY) LIVEPATCHES += $(LIVEPATCH_ACTION_HOOKS_NOREVERT) +LIVEPATCHES += $(LIVEPATCH_EXPECTATIONS) +LIVEPATCHES += $(LIVEPATCH_EXPECTATIONS_FAIL) LIVEPATCH_DEBUG_DIR ?= $(DEBUG_DIR)/xen-livepatch @@ -54,7 +58,7 @@ uninstall: .PHONY: clean clean:: - rm -f *.o .*.o.d *.livepatch config.h + rm -f *.o .*.o.d *.livepatch config.h expect_config.h # # To compute these values we need the binary files: xen-syms @@ -182,8 +186,27 @@ xen_actions_hooks_norevert.o: config.h $(LIVEPATCH_ACTION_HOOKS_NOREVERT): xen_action_hooks_marker.o xen_hello_world_func.o note.o xen_note.o $(LD) $(LDFLAGS) $(build_id_linker) -r -o $(LIVEPATCH_ACTION_HOOKS_NOREVERT) $^ +EXPECT_BYTES_COUNT := 8 +CODE_GET_EXPECT=$(shell $(OBJDUMP) -d --insn-width=1 $(1) | sed -n -e '/<'$(2)'>:$$/,/^$$/ p' | tail -n +2 | head -n $(EXPECT_BYTES_COUNT) | awk '{$$0=$$2; printf "%s", substr($$0,length-1)}' | sed 's/.\{2\}/0x&,/g' | sed 's/^/{/;s/,$$/}/g') +.PHONY: expect_config.h +expect_config.h: EXPECT_BYTES=$(call CODE_GET_EXPECT,$(BASEDIR)/xen-syms,xen_extra_version) +expect_config.h: xen_expectations.o + (set -e; \ + echo "#define EXPECT_BYTES $(EXPECT_BYTES)"; \ + echo "#define EXPECT_BYTES_COUNT $(EXPECT_BYTES_COUNT)") > $@ + +xen_expectations.o: expect_config.h + +.PHONY: $(LIVEPATCH_EXPECTATIONS) +$(LIVEPATCH_EXPECTATIONS): xen_expectations.o xen_hello_world_func.o note.o xen_note.o + $(LD) $(LDFLAGS) $(build_id_linker) -r -o $(LIVEPATCH_EXPECTATIONS) $^ + +.PHONY: $(LIVEPATCH_EXPECTATIONS_FAIL) +$(LIVEPATCH_EXPECTATIONS_FAIL): xen_expectations_fail.o xen_hello_world_func.o note.o xen_note.o + $(LD) $(LDFLAGS) $(build_id_linker) -r -o $(LIVEPATCH_EXPECTATIONS_FAIL) $^ + .PHONY: livepatch livepatch: $(LIVEPATCH) $(LIVEPATCH_BYE) $(LIVEPATCH_REPLACE) $(LIVEPATCH_NOP) $(LIVEPATCH_NO_XEN_BUILDID) \ $(LIVEPATCH_PREPOST_HOOKS) $(LIVEPATCH_PREPOST_HOOKS_FAIL) $(LIVEPATCH_ACTION_HOOKS) \ $(LIVEPATCH_ACTION_HOOKS_NOFUNC) $(LIVEPATCH_ACTION_HOOKS_MARKER) $(LIVEPATCH_ACTION_HOOKS_NOAPPLY) \ - $(LIVEPATCH_ACTION_HOOKS_NOREVERT) + $(LIVEPATCH_ACTION_HOOKS_NOREVERT) $(LIVEPATCH_EXPECTATIONS) $(LIVEPATCH_EXPECTATIONS_FAIL) diff --git a/xen/test/livepatch/xen_expectations.c b/xen/test/livepatch/xen_expectations.c new file mode 100644 index 0000000000..c8175a458b --- /dev/null +++ b/xen/test/livepatch/xen_expectations.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Amazon.com, Inc. or its affiliates. All rights reserved. + * + */ + +#include "expect_config.h" +#include +#include +#include +#include +#include + +#include + +static const char livepatch_exceptions_str[] = "xen_extra_version"; +extern const char *xen_hello_world(void); + +struct livepatch_func __section(".livepatch.funcs") livepatch_exceptions = { + .version = LIVEPATCH_PAYLOAD_VERSION, + .name = livepatch_exceptions_str, + .new_addr = xen_hello_world, + .old_addr = xen_extra_version, + .new_size = EXPECT_BYTES_COUNT, + .old_size = EXPECT_BYTES_COUNT, + .expect = { + .enabled = 1, + .len = EXPECT_BYTES_COUNT, + .data = EXPECT_BYTES + }, + +}; + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/test/livepatch/xen_expectations_fail.c b/xen/test/livepatch/xen_expectations_fail.c new file mode 100644 index 0000000000..36a110286f --- /dev/null +++ b/xen/test/livepatch/xen_expectations_fail.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019 Amazon.com, Inc. or its affiliates. All rights reserved. + * + */ + +#include +#include +#include +#include +#include + +#include + +static const char livepatch_exceptions_str[] = "xen_extra_version"; +extern const char *xen_hello_world(void); + +#define EXPECT_BYTES_COUNT 6 + +struct livepatch_func __section(".livepatch.funcs") livepatch_exceptions = { + .version = LIVEPATCH_PAYLOAD_VERSION, + .name = livepatch_exceptions_str, + .new_addr = xen_hello_world, + .old_addr = xen_extra_version, + .new_size = EXPECT_BYTES_COUNT, + .old_size = EXPECT_BYTES_COUNT, + .expect = { + .enabled = 1, + .len = EXPECT_BYTES_COUNT, + .data = { 0xDE, 0xAD, 0xC0, 0xDE, 0xBA, 0xBE } + }, + +}; + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */