diff mbox series

[10/14] livepatch: Add support for inline asm hotpatching expectations

Message ID 20190821081931.90887-11-wipawel@amazon.de (mailing list archive)
State Superseded
Headers show
Series livepatch: new features and fixes | expand

Commit Message

Wieczorkiewicz, Pawel Aug. 21, 2019, 8:19 a.m. UTC
This is the initial implementation of the expectations enhancement
to improve inline asm hotpatching.

Expectations are designed as optional feature, since the main use of
them is planned for inline asm hotpatching. 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.
The payload version is bumped to 3 with this change to highlight the
ABI modification and enforce proper support.

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 <wipawel@amazon.de>
Reviewed-by: Andra-Irina Paraschiv <andraprs@amazon.com>
Reviewed-by: Martin Pohlack <mpohlack@amazon.de>
Reviewed-by: Norbert Manthey <nmanthey@amazon.de>
---
 xen/common/livepatch.c      | 71 +++++++++++++++++++++++++++++++++++++++++++++
 xen/include/public/sysctl.h | 17 +++++++++--
 2 files changed, 86 insertions(+), 2 deletions(-)

Comments

Konrad Rzeszutek Wilk Aug. 21, 2019, 6:30 p.m. UTC | #1
On 8/21/19 4:19 AM, Pawel Wieczorkiewicz wrote:
>   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;
>   };

Aaah, now I understand why you decide to create a new field _pad and 
applied field!

Any particular reason why the version can't be 2 since this can be part 
of this changeset? Also you would need to have a Documentation change.
Wieczorkiewicz, Pawel Aug. 21, 2019, 7:02 p.m. UTC | #2
> On 21. Aug 2019, at 20:30, Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> wrote:
> 
> On 8/21/19 4:19 AM, Pawel Wieczorkiewicz wrote:
>>  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;
>>  };
> 
> Aaah, now I understand why you decide to create a new field _pad and applied field!
> 

Yup, that was premeditated! :-)

> Any particular reason why the version can't be 2 since this can be part of this changeset? Also you would need to have a Documentation change.

It surely can be 2. I wasn’t sure before if the changes could go together. Will change to 2 for both

And, I will also update the doc. 

Best Regards,
Pawel Wieczorkiewicz



Amazon Development Center Germany GmbH
Krausenstr. 38
10117 Berlin
Geschaeftsfuehrung: Christian Schlaeger, Ralf Herbrich
Eingetragen am Amtsgericht Charlottenburg unter HRB 149173 B
Sitz: Berlin
Ust-ID: DE 289 237 879
Julien Grall Aug. 22, 2019, 10:31 a.m. UTC | #3
Hi Pawel,

On 21/08/2019 09:19, Pawel Wieczorkiewicz wrote:
> diff --git a/xen/include/public/sysctl.h b/xen/include/public/sysctl.h
> index b55ad6d050..e18322350d 100644
> --- a/xen/include/public/sysctl.h
> +++ b/xen/include/public/sysctl.h
> @@ -818,7 +818,7 @@ struct xen_sysctl_cpu_featureset {
>    *     If zero exit with success.
>    */
>   
> -#define LIVEPATCH_PAYLOAD_VERSION 2
> +#define LIVEPATCH_PAYLOAD_VERSION 3

We usually only bump the version once per release. So this is unnecessary as you 
already did it in the previous patch.

Cheers,
Wieczorkiewicz, Pawel Aug. 22, 2019, 11:03 a.m. UTC | #4
On 22. Aug 2019, at 12:31, Julien Grall <julien.grall@arm.com<mailto:julien.grall@arm.com>> wrote:

Hi Pawel,

On 21/08/2019 09:19, Pawel Wieczorkiewicz wrote:
diff --git a/xen/include/public/sysctl.h b/xen/include/public/sysctl.h
index b55ad6d050..e18322350d 100644
--- a/xen/include/public/sysctl.h
+++ b/xen/include/public/sysctl.h
@@ -818,7 +818,7 @@ struct xen_sysctl_cpu_featureset {
  *     If zero exit with success.
  */
 -#define LIVEPATCH_PAYLOAD_VERSION 2
+#define LIVEPATCH_PAYLOAD_VERSION 3

We usually only bump the version once per release. So this is unnecessary as you already did it in the previous patch.


Yes, I will keep all these changes under a single bump as promised to Konrad yesterday.

Cheers,

--
Julien Grall

Best Regards,
Pawel Wieczorkiewicz






Amazon Development Center Germany GmbH
Krausenstr. 38
10117 Berlin
Geschaeftsfuehrung: Christian Schlaeger, Ralf Herbrich
Eingetragen am Amtsgericht Charlottenburg unter HRB 149173 B
Sitz: Berlin
Ust-ID: DE 289 237 879
diff mbox series

Patch

diff --git a/xen/common/livepatch.c b/xen/common/livepatch.c
index 090a48977b..8aef2fd12e 100644
--- a/xen/common/livepatch.c
+++ b/xen/common/livepatch.c
@@ -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);
diff --git a/xen/include/public/sysctl.h b/xen/include/public/sysctl.h
index b55ad6d050..e18322350d 100644
--- a/xen/include/public/sysctl.h
+++ b/xen/include/public/sysctl.h
@@ -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