@@ -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
@@ -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`.
@@ -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
@@ -1344,6 +1399,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);
@@ -1800,6 +1869,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);
@@ -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
@@ -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)
new file mode 100644
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019 Amazon.com, Inc. or its affiliates. All rights reserved.
+ *
+ */
+
+#include "expect_config.h"
+#include <xen/lib.h>
+#include <xen/types.h>
+#include <xen/version.h>
+#include <xen/livepatch.h>
+#include <xen/livepatch_payload.h>
+
+#include <public/sysctl.h>
+
+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:
+ */
new file mode 100644
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2019 Amazon.com, Inc. or its affiliates. All rights reserved.
+ *
+ */
+
+#include <xen/lib.h>
+#include <xen/types.h>
+#include <xen/version.h>
+#include <xen/livepatch.h>
+#include <xen/livepatch_payload.h>
+
+#include <public/sysctl.h>
+
+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:
+ */