diff mbox series

[livepatch-hooks-2,4/4] livepatch: Add per-function applied/reverted state tracking marker

Message ID 20190814083911.89280-1-wipawel@amazon.de (mailing list archive)
State New, archived
Headers show
Series [livepatch-hooks-2,1/4] create-diff-object: Handle optional apply|revert hooks | expand

Commit Message

Wieczorkiewicz, Pawel Aug. 14, 2019, 8:39 a.m. UTC
Livepatch only tracks an entire payload applied/reverted state. But,
with an option to supply the apply_payload() and/or revert_payload()
functions as optional hooks, it becomes possible to intermix the
execution of the original apply_payload()/revert_payload() functions
with their dynamically supplied counterparts.
It is important then to track the current state of every function
being patched and prevent situations of unintentional double-apply
or unapplied revert.

To support that, it is necessary to extend public interface of the
livepatch. The struct livepatch_func gets additional field holding
the applied/reverted state marker.

To reflect the livepatch payload ABI change, bump the version flag
LIVEPATCH_PAYLOAD_VERSION up to 2.

The above solution only applies to x86 architecture for now.

Signed-off-by: Pawel Wieczorkiewicz <wipawel@amazon.de>
Reviewed-by: Andra-Irina Paraschiv <andraprs@amazon.com>
Reviewed-by: Bjoern Doebel <doebel@amazon.de>
Reviewed-by: Martin Pohlack <mpohlack@amazon.de>
---
 xen/arch/x86/livepatch.c    | 20 +++++++++++++++++++-
 xen/common/livepatch.c      | 35 +++++++++++++++++++++++++++++++++++
 xen/include/public/sysctl.h | 11 ++++++++++-
 xen/include/xen/livepatch.h |  2 +-
 4 files changed, 65 insertions(+), 3 deletions(-)

Comments

Konrad Rzeszutek Wilk Aug. 21, 2019, 6:12 p.m. UTC | #1
On 8/14/19 4:39 AM, Pawel Wieczorkiewicz wrote:
> #ifdef __XEN__
> +typedef enum livepatch_func_state {
> +    LIVEPATCH_FUNC_NOT_APPLIED = 0,
> +    LIVEPATCH_FUNC_APPLIED = 1
> +} livepatch_func_state_t;
> +
>   struct livepatch_func {
>       const char *name;       /* Name of function to be patched. */
>       void *new_addr;
> @@ -834,6 +839,10 @@ struct livepatch_func {
>       uint32_t old_size;
>       uint8_t version;        /* MUST be LIVEPATCH_PAYLOAD_VERSION. */
>       uint8_t opaque[31];
> +#if defined CONFIG_X86
> +    uint8_t applied;
> +    uint8_t _pad[7];
> +#endif
>   };

Three requests:
  - Why does it have to be X86 specific?
  - Can you also include the change in the documentation where the spec 
resides?
  - You are bumping the version to 2, but not making it backwards 
compatible. If so ,you also need to update the documentation to mention 
this.
Wieczorkiewicz, Pawel Aug. 21, 2019, 6:59 p.m. UTC | #2
> On 21. Aug 2019, at 20:12, Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> wrote:
> 
> On 8/14/19 4:39 AM, Pawel Wieczorkiewicz wrote:
>> #ifdef __XEN__
>> +typedef enum livepatch_func_state {
>> +    LIVEPATCH_FUNC_NOT_APPLIED = 0,
>> +    LIVEPATCH_FUNC_APPLIED = 1
>> +} livepatch_func_state_t;
>> +
>>  struct livepatch_func {
>>      const char *name;       /* Name of function to be patched. */
>>      void *new_addr;
>> @@ -834,6 +839,10 @@ struct livepatch_func {
>>      uint32_t old_size;
>>      uint8_t version;        /* MUST be LIVEPATCH_PAYLOAD_VERSION. */
>>      uint8_t opaque[31];
>> +#if defined CONFIG_X86
>> +    uint8_t applied;
>> +    uint8_t _pad[7];
>> +#endif
>>  };
> 
> Three requests:
>  - Why does it have to be X86 specific?

Mostly because I am unable to test that on ARM (ENODEV…).

>  - Can you also include the change in the documentation where the spec 
> resides?

Yes, will do.

>  - You are bumping the version to 2, but not making it backwards 
> compatible. If so ,you also need to update the documentation to mention 
> this.


Ok, I will update that as well.

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/arch/x86/livepatch.c b/xen/arch/x86/livepatch.c
index 436ee40fe1..76fa91a082 100644
--- a/xen/arch/x86/livepatch.c
+++ b/xen/arch/x86/livepatch.c
@@ -61,6 +61,14 @@  void noinline arch_livepatch_apply(struct livepatch_func *func)
     if ( !len )
         return;
 
+    /* If the apply action has been already executed on this function, do nothing... */
+    if ( func->applied == LIVEPATCH_FUNC_APPLIED )
+    {
+        printk(XENLOG_WARNING LIVEPATCH "%s: %s has been already applied before\n",
+                __func__, func->name);
+        return;
+    }
+
     memcpy(func->opaque, old_ptr, len);
     if ( func->new_addr )
     {
@@ -77,15 +85,25 @@  void noinline arch_livepatch_apply(struct livepatch_func *func)
         add_nops(insn, len);
 
     memcpy(old_ptr, insn, len);
+    func->applied = LIVEPATCH_FUNC_APPLIED;
 }
 
 /*
  * "noinline" to cause control flow change and thus invalidate I$ and
  * cause refetch after modification.
  */
-void noinline arch_livepatch_revert(const struct livepatch_func *func)
+void noinline arch_livepatch_revert(struct livepatch_func *func)
 {
+    /* If the apply action hasn't been executed on this function, do nothing... */
+    if ( !func->old_addr || func->applied == LIVEPATCH_FUNC_NOT_APPLIED )
+    {
+        printk(XENLOG_WARNING LIVEPATCH "%s: %s has not been applied before\n",
+                __func__, func->name);
+        return;
+    }
+
     memcpy(func->old_addr, func->opaque, livepatch_insn_len(func));
+    func->applied = LIVEPATCH_FUNC_NOT_APPLIED;
 }
 
 /*
diff --git a/xen/common/livepatch.c b/xen/common/livepatch.c
index 585ec9819a..090a48977b 100644
--- a/xen/common/livepatch.c
+++ b/xen/common/livepatch.c
@@ -1242,6 +1242,29 @@  static inline void revert_payload_tail(struct payload *data)
     data->state = LIVEPATCH_STATE_CHECKED;
 }
 
+/*
+ * Check if an action has applied the same state to all payload's functions consistently.
+ */
+static inline bool was_action_consistent(const struct payload *data, livepatch_func_state_t expected_state)
+{
+    int i;
+
+    for ( i = 0; i < data->nfuncs; i++ )
+    {
+        struct livepatch_func *f = &(data->funcs[i]);
+
+        if ( f->applied != expected_state )
+        {
+            printk(XENLOG_ERR LIVEPATCH "%s: Payload has a function: '%s' with inconsistent applied state.\n",
+                   data->name, f->name ?: "noname");
+
+            return false;
+        }
+    }
+
+    return true;
+}
+
 /*
  * This function is executed having all other CPUs with no deep stack (we may
  * have cpu_idle on it) and IRQs disabled.
@@ -1268,6 +1291,9 @@  static void livepatch_do_action(void)
         else
             rc = apply_payload(data);
 
+        if ( !was_action_consistent(data, rc ? LIVEPATCH_FUNC_NOT_APPLIED : LIVEPATCH_FUNC_APPLIED) )
+            panic("livepatch: partially applied payload '%s'!\n", data->name);
+
         if ( rc == 0 )
             apply_payload_tail(data);
         break;
@@ -1282,6 +1308,9 @@  static void livepatch_do_action(void)
         else
             rc = revert_payload(data);
 
+        if ( !was_action_consistent(data, rc ? LIVEPATCH_FUNC_APPLIED : LIVEPATCH_FUNC_NOT_APPLIED) )
+            panic("livepatch: partially reverted payload '%s'!\n", data->name);
+
         if ( rc == 0 )
             revert_payload_tail(data);
         break;
@@ -1304,6 +1333,9 @@  static void livepatch_do_action(void)
                 other->rc = revert_payload(other);
 
 
+            if ( !was_action_consistent(other, rc ? LIVEPATCH_FUNC_APPLIED : LIVEPATCH_FUNC_NOT_APPLIED) )
+                panic("livepatch: partially reverted payload '%s'!\n", other->name);
+
             if ( other->rc == 0 )
                 revert_payload_tail(other);
             else
@@ -1324,6 +1356,9 @@  static void livepatch_do_action(void)
             else
                 rc = apply_payload(data);
 
+            if ( !was_action_consistent(data, rc ? LIVEPATCH_FUNC_NOT_APPLIED : LIVEPATCH_FUNC_APPLIED) )
+                panic("livepatch: partially applied payload '%s'!\n", data->name);
+
             if ( rc == 0 )
                 apply_payload_tail(data);
         }
diff --git a/xen/include/public/sysctl.h b/xen/include/public/sysctl.h
index cfbb8760d4..98cdcc64bb 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 1
+#define LIVEPATCH_PAYLOAD_VERSION 2
 /*
  * .livepatch.funcs structure layout defined in the `Payload format`
  * section in the Live Patch design document.
@@ -826,6 +826,11 @@  struct xen_sysctl_cpu_featureset {
  * We guard this with __XEN__ as toolstacks SHOULD not use it.
  */
 #ifdef __XEN__
+typedef enum livepatch_func_state {
+    LIVEPATCH_FUNC_NOT_APPLIED = 0,
+    LIVEPATCH_FUNC_APPLIED = 1
+} livepatch_func_state_t;
+
 struct livepatch_func {
     const char *name;       /* Name of function to be patched. */
     void *new_addr;
@@ -834,6 +839,10 @@  struct livepatch_func {
     uint32_t old_size;
     uint8_t version;        /* MUST be LIVEPATCH_PAYLOAD_VERSION. */
     uint8_t opaque[31];
+#if defined CONFIG_X86
+    uint8_t applied;
+    uint8_t _pad[7];
+#endif
 };
 typedef struct livepatch_func livepatch_func_t;
 #endif
diff --git a/xen/include/xen/livepatch.h b/xen/include/xen/livepatch.h
index 2aec532ee2..a93126f631 100644
--- a/xen/include/xen/livepatch.h
+++ b/xen/include/xen/livepatch.h
@@ -117,7 +117,7 @@  int arch_livepatch_quiesce(void);
 void arch_livepatch_revive(void);
 
 void arch_livepatch_apply(struct livepatch_func *func);
-void arch_livepatch_revert(const struct livepatch_func *func);
+void arch_livepatch_revert(struct livepatch_func *func);
 void arch_livepatch_post_action(void);
 
 void arch_livepatch_mask(void);