@@ -560,6 +560,58 @@ 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;
+
+ /* 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
@@ -1347,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 +1866,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);
@@ -818,7 +818,7 @@ struct xen_sysctl_cpu_featureset {
* If zero exit with success.
*/
-#define LIVEPATCH_PAYLOAD_VERSION 2
+#define LIVEPATCH_PAYLOAD_VERSION 3
/*
* .livepatch.funcs structure layout defined in the `Payload format`
* section in the Live Patch design document.
@@ -826,6 +826,18 @@ 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 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 = 0,
LIVEPATCH_FUNC_APPLIED = 1
@@ -838,11 +850,12 @@ 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];
#if defined CONFIG_X86
uint8_t applied;
uint8_t _pad[7];
#endif
+ livepatch_expectation_t expect;
};
typedef struct livepatch_func livepatch_func_t;
#endif