diff mbox series

[v3,07/12] livepatch: Add per-function applied/reverted state tracking marker

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

Commit Message

Wieczorkiewicz, Pawel Sept. 16, 2019, 10:59 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.

[And also update the top of the design document]

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>
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Acked-by: Julien Grall <julien.grall@arm.com>
---
Changed since v2:
  * Documentation fixes

Changed since v1:
  * support the feature for all arch (add handling for Arm)
  * add common is_func_applied() and is_func_reverted() to be
    used by all arch
  * remove explicit enum values from enum livepatch_func_state
  * added corresponding documentation
  * added tests

 docs/misc/livepatch.pandoc                     |  17 ++-
 xen/arch/arm/arm32/livepatch.c                 |  12 ++-
 xen/arch/arm/arm64/livepatch.c                 |  12 ++-
 xen/arch/arm/livepatch.c                       |  10 +-
 xen/arch/x86/livepatch.c                       |  22 +++-
 xen/common/livepatch.c                         |  35 ++++++
 xen/include/public/sysctl.h                    |   9 +-
 xen/include/xen/livepatch.h                    |  27 ++++-
 xen/test/livepatch/Makefile                    |  27 ++++-
 xen/test/livepatch/xen_action_hooks.c          |   2 +
 xen/test/livepatch/xen_action_hooks_marker.c   | 112 +++++++++++++++++++
 xen/test/livepatch/xen_action_hooks_noapply.c  | 136 +++++++++++++++++++++++
 xen/test/livepatch/xen_action_hooks_norevert.c | 143 +++++++++++++++++++++++++
 13 files changed, 554 insertions(+), 10 deletions(-)
 create mode 100644 xen/test/livepatch/xen_action_hooks_marker.c
 create mode 100644 xen/test/livepatch/xen_action_hooks_noapply.c
 create mode 100644 xen/test/livepatch/xen_action_hooks_norevert.c

Comments

Ross Lagerwall Sept. 19, 2019, 3:18 p.m. UTC | #1
On 9/16/19 11:59 AM, Pawel Wieczorkiewicz wrote:
> 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.
> 
> [And also update the top of the design document]
> 
snip> @@ -834,6 +839,8 @@ struct livepatch_func {
>       uint32_t old_size;
>       uint8_t version;        /* MUST be LIVEPATCH_PAYLOAD_VERSION. */
>       uint8_t opaque[31];
> +    uint8_t applied;
> +    uint8_t _pad[7];
>   };
>   typedef struct livepatch_func livepatch_func_t;
>   #endif
> diff --git a/xen/include/xen/livepatch.h b/xen/include/xen/livepatch.h
> index 2aec532ee2..28f9536776 100644
> --- a/xen/include/xen/livepatch.h
> +++ b/xen/include/xen/livepatch.h
> @@ -109,6 +109,31 @@ static inline int livepatch_verify_distance(const struct livepatch_func *func)
>   
>       return 0;
>   }
> +
> +static inline bool_t is_func_applied(const struct livepatch_func *func)

Use bool rather than bool_t (throughout the patch).

> +{
> +    if ( func->applied == LIVEPATCH_FUNC_APPLIED )
> +    {
> +        printk(XENLOG_WARNING LIVEPATCH "%s: %s has been already applied before\n",
> +                __func__, func->name);

How about dropping this function and having a wrapper function like this:

common_livepatch_apply() {
     if (func->applied == LIVEPATCH_FUNC_APPLIED) {
         WARN(...)
         return
     }

     arch_livepatch_apply()

     func->applied = LIVEPATCH_FUNC_APPLIED
}

This could be used by the normal apply code and any apply hooks.

This avoids having duplicate code in each of the architectures that is 
not arch specific and also avoids having a state querying function emit 
a warning which seems odd to me.

> +        return true;
> +    }
> +
> +    return false;
> +}
> +
> +static inline bool_t is_func_reverted(const struct livepatch_func *func)
> +{
> +    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 true;
> +    }
> +
> +    return false;
> +}
> +
>   /*
>    * These functions are called around the critical region patching live code,
>    * for an architecture to take make appropratie global state adjustments.
> @@ -117,7 +142,7 @@ int arch_livepatch_quiesce(void);
>   void arch_livepatch_revive(void);
>
Wieczorkiewicz, Pawel Sept. 20, 2019, 12:47 p.m. UTC | #2
> On 19. Sep 2019, at 17:18, Ross Lagerwall <ross.lagerwall@citrix.com> wrote:
> 
> On 9/16/19 11:59 AM, Pawel Wieczorkiewicz wrote:
>> 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.
>> [And also update the top of the design document]
> snip> @@ -834,6 +839,8 @@ struct livepatch_func {
>>      uint32_t old_size;
>>      uint8_t version;        /* MUST be LIVEPATCH_PAYLOAD_VERSION. */
>>      uint8_t opaque[31];
>> +    uint8_t applied;
>> +    uint8_t _pad[7];
>>  };
>>  typedef struct livepatch_func livepatch_func_t;
>>  #endif
>> diff --git a/xen/include/xen/livepatch.h b/xen/include/xen/livepatch.h
>> index 2aec532ee2..28f9536776 100644
>> --- a/xen/include/xen/livepatch.h
>> +++ b/xen/include/xen/livepatch.h
>> @@ -109,6 +109,31 @@ static inline int livepatch_verify_distance(const struct livepatch_func *func)
>>        return 0;
>>  }
>> +
>> +static inline bool_t is_func_applied(const struct livepatch_func *func)
> 
> Use bool rather than bool_t (throughout the patch).
> 

ACK.

>> +{
>> +    if ( func->applied == LIVEPATCH_FUNC_APPLIED )
>> +    {
>> +        printk(XENLOG_WARNING LIVEPATCH "%s: %s has been already applied before\n",
>> +                __func__, func->name);
> 
> How about dropping this function and having a wrapper function like this:
> 
> common_livepatch_apply() {
>    if (func->applied == LIVEPATCH_FUNC_APPLIED) {
>        WARN(...)
>        return
>    }
> 
>    arch_livepatch_apply()
> 
>    func->applied = LIVEPATCH_FUNC_APPLIED
> }
> 
> This could be used by the normal apply code and any apply hooks.
> 
> This avoids having duplicate code in each of the architectures that is not arch specific and also avoids having a state querying function emit a warning which seems odd to me.
> 

Yes. That makes a lot of sense. Let me do that.

Thanks.

>> +        return true;
>> +    }
>> +
>> +    return false;
>> +}
>> +
>> +static inline bool_t is_func_reverted(const struct livepatch_func *func)
>> +{
>> +    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 true;
>> +    }
>> +
>> +    return false;
>> +}
>> +
>>  /*
>>   * These functions are called around the critical region patching live code,
>>   * for an architecture to take make appropratie global state adjustments.
>> @@ -117,7 +142,7 @@ int arch_livepatch_quiesce(void);
>>  void arch_livepatch_revive(void);
>>  
> -- 
> Ross Lagerwall

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/docs/misc/livepatch.pandoc b/docs/misc/livepatch.pandoc
index 6fafb9e4b1..383a988ba2 100644
--- a/docs/misc/livepatch.pandoc
+++ b/docs/misc/livepatch.pandoc
@@ -1,4 +1,4 @@ 
-# Xen Live Patching Design v1
+# Xen Live Patching Design v2
 
 ## Rationale
 
@@ -297,10 +297,14 @@  which describe the functions to be patched:
         uint32_t old_size;
         uint8_t version;
         uint8_t opaque[31];
+        /* Added to livepatch payload version 2: */
+        uint8_t applied;
+        uint8_t _pad[7];
     };
 
 The size of the structure is 64 bytes on 64-bit hypervisors. It will be
 52 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
    zero, otherwise will be used during dynamic linking (when hypervisor loads
@@ -324,9 +328,15 @@  The size of the structure is 64 bytes on 64-bit hypervisors. It will be
    * If the value of `new_addr` is zero then `new_size` determines how many
     instruction bytes to NOP (up to opaque size modulo smallest platform
     instruction - 1 byte x86 and 4 bytes on ARM).
- * `version` is to be one.
+ * `version` indicates version of the generated payload.
  * `opaque` **MUST** be zero.
 
+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.
+
 The size of the `livepatch_func` array is determined from the ELF section
 size.
 
@@ -378,6 +388,9 @@  A simple example of what a payload file can be:
         uint32_t old_size;
         uint8_t version;
         uint8_t pad[31];
+        /* Added to livepatch payload version 2: */
+        uint8_t applied;
+        uint8_t _pad[7];
     };
 
     /* Our replacement function for xen_extra_version. */
diff --git a/xen/arch/arm/arm32/livepatch.c b/xen/arch/arm/arm32/livepatch.c
index 41378a54ae..76780d69bf 100644
--- a/xen/arch/arm/arm32/livepatch.c
+++ b/xen/arch/arm/arm32/livepatch.c
@@ -22,9 +22,16 @@  void arch_livepatch_apply(struct livepatch_func *func)
 
     ASSERT(vmap_of_xen_text);
 
+    /*
+     * If the apply action has been already executed
+     * on this function, do nothing...
+     */
+    if ( is_func_applied(func) )
+        return;
+
     len = livepatch_insn_len(func);
     if ( !len )
-        return;
+        goto applied;
 
     /* Save old ones. */
     memcpy(func->opaque, func->old_addr, len);
@@ -73,6 +80,9 @@  void arch_livepatch_apply(struct livepatch_func *func)
     if ( func->new_addr )
         clean_and_invalidate_dcache_va_range(func->new_addr, func->new_size);
     clean_and_invalidate_dcache_va_range(new_ptr, sizeof (*new_ptr) * len);
+
+applied:
+    func->applied = LIVEPATCH_FUNC_APPLIED;
 }
 
 /* arch_livepatch_revert shared with ARM 32/ARM 64. */
diff --git a/xen/arch/arm/arm64/livepatch.c b/xen/arch/arm/arm64/livepatch.c
index 5c75779284..61a764816c 100644
--- a/xen/arch/arm/arm64/livepatch.c
+++ b/xen/arch/arm/arm64/livepatch.c
@@ -26,9 +26,16 @@  void arch_livepatch_apply(struct livepatch_func *func)
 
     ASSERT(vmap_of_xen_text);
 
+    /*
+     * If the apply action has been already executed
+     * on this function, do nothing...
+     */
+    if ( is_func_applied(func) )
+         return;
+
     len = livepatch_insn_len(func);
     if ( !len )
-        return;
+        goto applied;
 
     /* Save old ones. */
     memcpy(func->opaque, func->old_addr, len);
@@ -60,6 +67,9 @@  void arch_livepatch_apply(struct livepatch_func *func)
     if ( func->new_addr )
         clean_and_invalidate_dcache_va_range(func->new_addr, func->new_size);
     clean_and_invalidate_dcache_va_range(new_ptr, sizeof (*new_ptr) * len);
+
+applied:
+    func->applied = LIVEPATCH_FUNC_APPLIED;
 }
 
 /* arch_livepatch_revert shared with ARM 32/ARM 64. */
diff --git a/xen/arch/arm/livepatch.c b/xen/arch/arm/livepatch.c
index 279d52cc6c..022402d55f 100644
--- a/xen/arch/arm/livepatch.c
+++ b/xen/arch/arm/livepatch.c
@@ -73,17 +73,25 @@  int arch_livepatch_verify_func(const struct livepatch_func *func)
     return 0;
 }
 
-void arch_livepatch_revert(const struct livepatch_func *func)
+void arch_livepatch_revert(struct livepatch_func *func)
 {
     uint32_t *new_ptr;
     unsigned int len;
 
+    /*
+     * If the apply action hasn't been executed
+     * on this function, do nothing...
+     */
+    if ( is_func_reverted(func) )
+        return;
+
     new_ptr = func->old_addr - (void *)_start + vmap_of_xen_text;
 
     len = livepatch_insn_len(func);
     memcpy(new_ptr, func->opaque, len);
 
     clean_and_invalidate_dcache_va_range(new_ptr, len);
+    func->applied = LIVEPATCH_FUNC_NOT_APPLIED;
 }
 
 void arch_livepatch_post_action(void)
diff --git a/xen/arch/x86/livepatch.c b/xen/arch/x86/livepatch.c
index c82cf53b9e..5701c90e48 100644
--- a/xen/arch/x86/livepatch.c
+++ b/xen/arch/x86/livepatch.c
@@ -56,10 +56,17 @@  void noinline arch_livepatch_apply(struct livepatch_func *func)
     uint8_t insn[sizeof(func->opaque)];
     unsigned int len;
 
+    /*
+     * If the apply action has been already executed
+     * on this function, do nothing...
+     */
+    if ( is_func_applied(func) )
+         return;
+
     old_ptr = func->old_addr;
     len = livepatch_insn_len(func);
     if ( !len )
-        return;
+        goto applied;
 
     memcpy(func->opaque, old_ptr, len);
     if ( func->new_addr )
@@ -77,15 +84,26 @@  void noinline arch_livepatch_apply(struct livepatch_func *func)
         add_nops(insn, len);
 
     memcpy(old_ptr, insn, len);
+
+applied:
+    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 ( is_func_reverted(func) )
+        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 705b5b8151..d76619844c 100644
--- a/xen/common/livepatch.c
+++ b/xen/common/livepatch.c
@@ -1240,6 +1240,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.
@@ -1266,6 +1289,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;
@@ -1280,6 +1306,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;
@@ -1302,6 +1331,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
@@ -1322,6 +1354,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 1b2b165a6d..3bcb892ce1 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,
+    LIVEPATCH_FUNC_APPLIED
+} livepatch_func_state_t;
+
 struct livepatch_func {
     const char *name;       /* Name of function to be patched. */
     void *new_addr;
@@ -834,6 +839,8 @@  struct livepatch_func {
     uint32_t old_size;
     uint8_t version;        /* MUST be LIVEPATCH_PAYLOAD_VERSION. */
     uint8_t opaque[31];
+    uint8_t applied;
+    uint8_t _pad[7];
 };
 typedef struct livepatch_func livepatch_func_t;
 #endif
diff --git a/xen/include/xen/livepatch.h b/xen/include/xen/livepatch.h
index 2aec532ee2..28f9536776 100644
--- a/xen/include/xen/livepatch.h
+++ b/xen/include/xen/livepatch.h
@@ -109,6 +109,31 @@  static inline int livepatch_verify_distance(const struct livepatch_func *func)
 
     return 0;
 }
+
+static inline bool_t is_func_applied(const struct livepatch_func *func)
+{
+    if ( func->applied == LIVEPATCH_FUNC_APPLIED )
+    {
+        printk(XENLOG_WARNING LIVEPATCH "%s: %s has been already applied before\n",
+                __func__, func->name);
+        return true;
+    }
+
+    return false;
+}
+
+static inline bool_t is_func_reverted(const struct livepatch_func *func)
+{
+    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 true;
+    }
+
+    return false;
+}
+
 /*
  * These functions are called around the critical region patching live code,
  * for an architecture to take make appropratie global state adjustments.
@@ -117,7 +142,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);
diff --git a/xen/test/livepatch/Makefile b/xen/test/livepatch/Makefile
index bbc6bdaf64..23113d3418 100644
--- a/xen/test/livepatch/Makefile
+++ b/xen/test/livepatch/Makefile
@@ -24,6 +24,9 @@  LIVEPATCH_PREPOST_HOOKS := xen_prepost_hooks.livepatch
 LIVEPATCH_PREPOST_HOOKS_FAIL := xen_prepost_hooks_fail.livepatch
 LIVEPATCH_ACTION_HOOKS := xen_action_hooks.livepatch
 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
 
 LIVEPATCHES += $(LIVEPATCH)
 LIVEPATCHES += $(LIVEPATCH_BYE)
@@ -34,6 +37,9 @@  LIVEPATCHES += $(LIVEPATCH_PREPOST_HOOKS)
 LIVEPATCHES += $(LIVEPATCH_PREPOST_HOOKS_FAIL)
 LIVEPATCHES += $(LIVEPATCH_ACTION_HOOKS)
 LIVEPATCHES += $(LIVEPATCH_ACTION_HOOKS_NOFUNC)
+LIVEPATCHES += $(LIVEPATCH_ACTION_HOOKS_MARKER)
+LIVEPATCHES += $(LIVEPATCH_ACTION_HOOKS_NOAPPLY)
+LIVEPATCHES += $(LIVEPATCH_ACTION_HOOKS_NOREVERT)
 
 LIVEPATCH_DEBUG_DIR ?= $(DEBUG_DIR)/xen-livepatch
 
@@ -158,7 +164,26 @@  $(LIVEPATCH_ACTION_HOOKS): xen_action_hooks.o xen_hello_world_func.o note.o xen_
 $(LIVEPATCH_ACTION_HOOKS_NOFUNC): xen_action_hooks_nofunc.o note.o xen_note.o
 	$(LD) $(LDFLAGS) $(build_id_linker) -r -o $(LIVEPATCH_ACTION_HOOKS_NOFUNC) $^
 
+xen_actions_hooks_marker.o: config.h
+
+.PHONY: $(LIVEPATCH_ACTION_HOOKS_MARKER)
+$(LIVEPATCH_ACTION_HOOKS_MARKER): 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_MARKER) $^
+
+xen_actions_hooks_noapply.o: config.h
+
+.PHONY: $(LIVEPATCH_ACTION_HOOKS_NOAPPLY)
+$(LIVEPATCH_ACTION_HOOKS_NOAPPLY): 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_NOAPPLY) $^
+
+xen_actions_hooks_norevert.o: config.h
+
+.PHONY: $(LIVEPATCH_ACTION_HOOKS_NOREVERT)
+$(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) $^
+
 .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_NOFUNC) $(LIVEPATCH_ACTION_HOOKS_MARKER) $(LIVEPATCH_ACTION_HOOKS_NOAPPLY) \
+           $(LIVEPATCH_ACTION_HOOKS_NOREVERT)
diff --git a/xen/test/livepatch/xen_action_hooks.c b/xen/test/livepatch/xen_action_hooks.c
index a947afc41f..39b5313027 100644
--- a/xen/test/livepatch/xen_action_hooks.c
+++ b/xen/test/livepatch/xen_action_hooks.c
@@ -28,6 +28,7 @@  static int apply_hook(livepatch_payload_t *payload)
     {
         struct livepatch_func *func = &payload->funcs[i];
 
+        func->applied = LIVEPATCH_FUNC_APPLIED;
         apply_cnt++;
 
         printk(KERN_DEBUG "%s: applying: %s\n", __func__, func->name);
@@ -48,6 +49,7 @@  static int revert_hook(livepatch_payload_t *payload)
     {
         struct livepatch_func *func = &payload->funcs[i];
 
+        func->applied = LIVEPATCH_FUNC_NOT_APPLIED;
         revert_cnt++;
 
         printk(KERN_DEBUG "%s: reverting: %s\n", __func__, func->name);
diff --git a/xen/test/livepatch/xen_action_hooks_marker.c b/xen/test/livepatch/xen_action_hooks_marker.c
new file mode 100644
index 0000000000..4f807a577f
--- /dev/null
+++ b/xen/test/livepatch/xen_action_hooks_marker.c
@@ -0,0 +1,112 @@ 
+/*
+ * Copyright (c) 2019 Amazon.com, Inc. or its affiliates. All rights reserved.
+ *
+ */
+
+#include "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 hello_world_patch_this_fnc[] = "xen_extra_version";
+extern const char *xen_hello_world(void);
+
+static int pre_apply_hook(livepatch_payload_t *payload)
+{
+    int i;
+
+    printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
+
+    for (i = 0; i < payload->nfuncs; i++)
+    {
+        struct livepatch_func *func = &payload->funcs[i];
+
+        BUG_ON(func->applied == LIVEPATCH_FUNC_APPLIED);
+        printk(KERN_DEBUG "%s: pre applied: %s\n", __func__, func->name);
+    }
+
+    printk(KERN_DEBUG "%s: Hook done.\n", __func__);
+
+    return 0;
+}
+
+static void post_apply_hook(livepatch_payload_t *payload)
+{
+    int i;
+
+    printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
+
+    for (i = 0; i < payload->nfuncs; i++)
+    {
+        struct livepatch_func *func = &payload->funcs[i];
+
+        BUG_ON(func->applied != LIVEPATCH_FUNC_APPLIED);
+        printk(KERN_DEBUG "%s: post applied: %s\n", __func__, func->name);
+    }
+
+    printk(KERN_DEBUG "%s: Hook done.\n", __func__);
+}
+
+static int pre_revert_hook(livepatch_payload_t *payload)
+{
+    int i;
+
+    printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
+
+    for (i = 0; i < payload->nfuncs; i++)
+    {
+        struct livepatch_func *func = &payload->funcs[i];
+
+        BUG_ON(func->applied != LIVEPATCH_FUNC_APPLIED);
+        printk(KERN_DEBUG "%s: pre reverted: %s\n", __func__, func->name);
+    }
+
+    printk(KERN_DEBUG "%s: Hook done.\n", __func__);
+
+    return 0;
+}
+
+static void post_revert_hook(livepatch_payload_t *payload)
+{
+    int i;
+
+    printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
+
+    for (i = 0; i < payload->nfuncs; i++)
+    {
+        struct livepatch_func *func = &payload->funcs[i];
+
+        BUG_ON(func->applied == LIVEPATCH_FUNC_APPLIED);
+        printk(KERN_DEBUG "%s: post reverted: %s\n", __func__, func->name);
+    }
+
+    printk(KERN_DEBUG "%s: Hook done.\n", __func__);
+}
+
+LIVEPATCH_PREAPPLY_HOOK(pre_apply_hook);
+LIVEPATCH_POSTAPPLY_HOOK(post_apply_hook);
+LIVEPATCH_PREREVERT_HOOK(pre_revert_hook);
+LIVEPATCH_POSTREVERT_HOOK(post_revert_hook);
+
+struct livepatch_func __section(".livepatch.funcs") livepatch_xen_hello_world = {
+    .version = LIVEPATCH_PAYLOAD_VERSION,
+    .name = hello_world_patch_this_fnc,
+    .new_addr = xen_hello_world,
+    .old_addr = xen_extra_version,
+    .new_size = NEW_CODE_SZ,
+    .old_size = OLD_CODE_SZ,
+};
+
+/*
+ * 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_action_hooks_noapply.c b/xen/test/livepatch/xen_action_hooks_noapply.c
new file mode 100644
index 0000000000..4c55c156a6
--- /dev/null
+++ b/xen/test/livepatch/xen_action_hooks_noapply.c
@@ -0,0 +1,136 @@ 
+/*
+ * Copyright (c) 2019 Amazon.com, Inc. or its affiliates. All rights reserved.
+ *
+ */
+
+#include "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 hello_world_patch_this_fnc[] = "xen_extra_version";
+extern const char *xen_hello_world(void);
+
+static unsigned int apply_cnt;
+
+static int pre_apply_hook(livepatch_payload_t *payload)
+{
+    int i;
+
+    printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
+
+    for (i = 0; i < payload->nfuncs; i++)
+    {
+        struct livepatch_func *func = &payload->funcs[i];
+
+        BUG_ON(func->applied == LIVEPATCH_FUNC_APPLIED);
+        printk(KERN_DEBUG "%s: pre applied: %s\n", __func__, func->name);
+    }
+
+    printk(KERN_DEBUG "%s: Hook done.\n", __func__);
+
+    return 0;
+}
+
+static int apply_hook(livepatch_payload_t *payload)
+{
+    int i;
+
+    printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
+
+    for (i = 0; i < payload->nfuncs; i++)
+    {
+        struct livepatch_func *func = &payload->funcs[i];
+
+        apply_cnt++;
+        printk(KERN_DEBUG "%s: applying: %s\n", __func__, func->name);
+    }
+
+    printk(KERN_DEBUG "%s: Hook done.\n", __func__);
+
+    return -EINVAL; /* Mark action as inconsistent */
+}
+
+static void post_apply_hook(livepatch_payload_t *payload)
+{
+    int i;
+
+    printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
+
+    for (i = 0; i < payload->nfuncs; i++)
+    {
+        struct livepatch_func *func = &payload->funcs[i];
+
+        BUG_ON(apply_cnt != 1);
+        BUG_ON(func->applied == LIVEPATCH_FUNC_APPLIED);
+        printk(KERN_DEBUG "%s: post applied: %s\n", __func__, func->name);
+    }
+
+    printk(KERN_DEBUG "%s: Hook done.\n", __func__);
+}
+
+static int pre_revert_hook(livepatch_payload_t *payload)
+{
+    int i;
+
+    printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
+
+    for (i = 0; i < payload->nfuncs; i++)
+    {
+        struct livepatch_func *func = &payload->funcs[i];
+
+        BUG_ON(func->applied == LIVEPATCH_FUNC_APPLIED);
+        printk(KERN_DEBUG "%s: pre reverted: %s\n", __func__, func->name);
+    }
+
+    printk(KERN_DEBUG "%s: Hook done.\n", __func__);
+
+    return 0;
+}
+
+static void post_revert_hook(livepatch_payload_t *payload)
+{
+    int i;
+
+    printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
+
+    for (i = 0; i < payload->nfuncs; i++)
+    {
+        struct livepatch_func *func = &payload->funcs[i];
+
+        BUG_ON(func->applied == LIVEPATCH_FUNC_APPLIED);
+        printk(KERN_DEBUG "%s: post reverted: %s\n", __func__, func->name);
+    }
+
+    printk(KERN_DEBUG "%s: Hook done.\n", __func__);
+}
+
+LIVEPATCH_APPLY_HOOK(apply_hook);
+
+LIVEPATCH_PREAPPLY_HOOK(pre_apply_hook);
+LIVEPATCH_POSTAPPLY_HOOK(post_apply_hook);
+LIVEPATCH_PREREVERT_HOOK(pre_revert_hook);
+LIVEPATCH_POSTREVERT_HOOK(post_revert_hook);
+
+struct livepatch_func __section(".livepatch.funcs") livepatch_xen_hello_world = {
+    .version = LIVEPATCH_PAYLOAD_VERSION,
+    .name = hello_world_patch_this_fnc,
+    .new_addr = xen_hello_world,
+    .old_addr = xen_extra_version,
+    .new_size = NEW_CODE_SZ,
+    .old_size = OLD_CODE_SZ,
+};
+
+/*
+ * 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_action_hooks_norevert.c b/xen/test/livepatch/xen_action_hooks_norevert.c
new file mode 100644
index 0000000000..4408166f47
--- /dev/null
+++ b/xen/test/livepatch/xen_action_hooks_norevert.c
@@ -0,0 +1,143 @@ 
+/*
+ * Copyright (c) 2019 Amazon.com, Inc. or its affiliates. All rights reserved.
+ *
+ */
+
+#include "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 hello_world_patch_this_fnc[] = "xen_extra_version";
+extern const char *xen_hello_world(void);
+
+static unsigned int revert_cnt;
+
+static int pre_apply_hook(livepatch_payload_t *payload)
+{
+    int i;
+
+    printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
+
+    for (i = 0; i < payload->nfuncs; i++)
+    {
+        struct livepatch_func *func = &payload->funcs[i];
+
+        BUG_ON(func->applied == LIVEPATCH_FUNC_APPLIED);
+        printk(KERN_DEBUG "%s: pre applied: %s\n", __func__, func->name);
+    }
+
+    printk(KERN_DEBUG "%s: Hook done.\n", __func__);
+
+    return 0;
+}
+
+static void post_apply_hook(livepatch_payload_t *payload)
+{
+    int i;
+
+    printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
+
+    for (i = 0; i < payload->nfuncs; i++)
+    {
+        struct livepatch_func *func = &payload->funcs[i];
+
+        BUG_ON(func->applied != LIVEPATCH_FUNC_APPLIED);
+        printk(KERN_DEBUG "%s: post applied: %s\n", __func__, func->name);
+    }
+
+    printk(KERN_DEBUG "%s: Hook done.\n", __func__);
+}
+
+static int pre_revert_hook(livepatch_payload_t *payload)
+{
+    int i;
+
+    printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
+
+    for (i = 0; i < payload->nfuncs; i++)
+    {
+        struct livepatch_func *func = &payload->funcs[i];
+
+        BUG_ON(func->applied != LIVEPATCH_FUNC_APPLIED);
+        printk(KERN_DEBUG "%s: pre reverted: %s\n", __func__, func->name);
+    }
+
+    printk(KERN_DEBUG "%s: Hook done.\n", __func__);
+
+    return 0;
+}
+
+static int revert_hook(livepatch_payload_t *payload)
+{
+    int i;
+
+    printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
+
+    for (i = 0; i < payload->nfuncs; i++)
+    {
+        struct livepatch_func *func = &payload->funcs[i];
+
+        revert_cnt++;
+        printk(KERN_DEBUG "%s: reverting: %s\n", __func__, func->name);
+    }
+
+    printk(KERN_DEBUG "%s: Hook done.\n", __func__);
+
+    return -EINVAL; /* Mark action as inconsistent */
+}
+
+static void post_revert_hook(livepatch_payload_t *payload)
+{
+    int i;
+
+    printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
+
+    for (i = 0; i < payload->nfuncs; i++)
+    {
+        struct livepatch_func *func = &payload->funcs[i];
+
+        BUG_ON(revert_cnt != 1);
+        BUG_ON(func->applied != LIVEPATCH_FUNC_APPLIED);
+
+        /* Outside of quiesce zone: MAY TRIGGER HOST CRASH/UNDEFINED BEHAVIOR */
+        arch_livepatch_quiesce();
+        arch_livepatch_revert(payload);
+        arch_livepatch_revive();
+        BUG_ON(func->applied == LIVEPATCH_FUNC_APPLIED);
+
+        printk(KERN_DEBUG "%s: post reverted: %s\n", __func__, func->name);
+    }
+
+    printk(KERN_DEBUG "%s: Hook done.\n", __func__);
+}
+
+LIVEPATCH_APPLY_HOOK(revert_hook);
+
+LIVEPATCH_PREAPPLY_HOOK(pre_apply_hook);
+LIVEPATCH_POSTAPPLY_HOOK(post_apply_hook);
+LIVEPATCH_PREREVERT_HOOK(pre_revert_hook);
+LIVEPATCH_POSTREVERT_HOOK(post_revert_hook);
+
+struct livepatch_func __section(".livepatch.funcs") livepatch_xen_hello_world = {
+    .version = LIVEPATCH_PAYLOAD_VERSION,
+    .name = hello_world_patch_this_fnc,
+    .new_addr = xen_hello_world,
+    .old_addr = xen_extra_version,
+    .new_size = NEW_CODE_SZ,
+    .old_size = OLD_CODE_SZ,
+};
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */