[1/2] xen/livepatch: Add a return value to load hooks
diff mbox series

Message ID 20191105194317.16232-2-andrew.cooper3@citrix.com
State New
Headers show
Series
  • [1/2] xen/livepatch: Add a return value to load hooks
Related show

Commit Message

Andrew Cooper Nov. 5, 2019, 7:43 p.m. UTC
One use of load hooks is to perform a safety check of the system in its
quiesced state.  Any non-zero return value from a load hook will abort the
apply attempt.

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
---
CC: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
CC: Ross Lagerwall <ross.lagerwall@citrix.com>
CC: Juergen Gross <jgross@suse.com>

For several years, the following patch in the series has been shipped in every
XenServer livepatch, implemented as a void load hook which modifies its return
address to skip to the end of apply_payload().  This method is distinctly less
hacky.
---
 xen/common/livepatch.c               | 30 +++++++++++++++++++-----------
 xen/include/xen/livepatch_payload.h  |  2 +-
 xen/test/livepatch/xen_hello_world.c | 12 +++++++++---
 3 files changed, 29 insertions(+), 15 deletions(-)

Comments

Konrad Rzeszutek Wilk Nov. 6, 2019, 2:20 p.m. UTC | #1
On Tue, Nov 05, 2019 at 07:43:16PM +0000, Andrew Cooper wrote:
> One use of load hooks is to perform a safety check of the system in its
> quiesced state.  Any non-zero return value from a load hook will abort the
> apply attempt.
> 

Shouldn't you also update the documentation (design doc?)

Thanks.
> Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
> ---
> CC: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
> CC: Ross Lagerwall <ross.lagerwall@citrix.com>
> CC: Juergen Gross <jgross@suse.com>
> 
> For several years, the following patch in the series has been shipped in every
> XenServer livepatch, implemented as a void load hook which modifies its return
> address to skip to the end of apply_payload().  This method is distinctly less
> hacky.
> ---
>  xen/common/livepatch.c               | 30 +++++++++++++++++++-----------
>  xen/include/xen/livepatch_payload.h  |  2 +-
>  xen/test/livepatch/xen_hello_world.c | 12 +++++++++---
>  3 files changed, 29 insertions(+), 15 deletions(-)
> 
> diff --git a/xen/common/livepatch.c b/xen/common/livepatch.c
> index 7caa30c202..962647616a 100644
> --- a/xen/common/livepatch.c
> +++ b/xen/common/livepatch.c
> @@ -1076,25 +1076,33 @@ static int apply_payload(struct payload *data)
>       * temporarily disable the spin locks IRQ state checks.
>       */
>      spin_debug_disable();
> -    for ( i = 0; i < data->n_load_funcs; i++ )
> -        data->load_funcs[i]();
> +    for ( i = 0; !rc && i < data->n_load_funcs; i++ )
> +        rc = data->load_funcs[i]();
>      spin_debug_enable();
>  
> +    if ( rc )
> +        printk(XENLOG_ERR LIVEPATCH "%s: load_funcs[%u] failed: %d\n",
> +               data->name, i, rc);
> +
>      ASSERT(!local_irq_is_enabled());
>  
> -    for ( i = 0; i < data->nfuncs; i++ )
> -        arch_livepatch_apply(&data->funcs[i]);
> +    if ( !rc )
> +        for ( i = 0; i < data->nfuncs; i++ )
> +            arch_livepatch_apply(&data->funcs[i]);
>  
>      arch_livepatch_revive();
>  
> -    /*
> -     * We need RCU variant (which has barriers) in case we crash here.
> -     * The applied_list is iterated by the trap code.
> -     */
> -    list_add_tail_rcu(&data->applied_list, &applied_list);
> -    register_virtual_region(&data->region);
> +    if ( !rc )
> +    {
> +        /*
> +         * We need RCU variant (which has barriers) in case we crash here.
> +         * The applied_list is iterated by the trap code.
> +         */
> +        list_add_tail_rcu(&data->applied_list, &applied_list);
> +        register_virtual_region(&data->region);
> +    }
>  
> -    return 0;
> +    return rc;
>  }
>  
>  static int revert_payload(struct payload *data)
> diff --git a/xen/include/xen/livepatch_payload.h b/xen/include/xen/livepatch_payload.h
> index 4a1a96d054..3788b52ddf 100644
> --- a/xen/include/xen/livepatch_payload.h
> +++ b/xen/include/xen/livepatch_payload.h
> @@ -9,7 +9,7 @@
>   * The following definitions are to be used in patches. They are taken
>   * from kpatch.
>   */
> -typedef void livepatch_loadcall_t(void);
> +typedef int livepatch_loadcall_t(void);
>  typedef void livepatch_unloadcall_t(void);
>  
>  /*
> diff --git a/xen/test/livepatch/xen_hello_world.c b/xen/test/livepatch/xen_hello_world.c
> index 02f3f85dc0..d720001f07 100644
> --- a/xen/test/livepatch/xen_hello_world.c
> +++ b/xen/test/livepatch/xen_hello_world.c
> @@ -16,9 +16,11 @@ static const char hello_world_patch_this_fnc[] = "xen_extra_version";
>  extern const char *xen_hello_world(void);
>  static unsigned int cnt;
>  
> -static void apply_hook(void)
> +static int apply_hook(void)
>  {
>      printk(KERN_DEBUG "Hook executing.\n");
> +
> +    return 0;
>  }
>  
>  static void revert_hook(void)
> @@ -26,10 +28,14 @@ static void revert_hook(void)
>      printk(KERN_DEBUG "Hook unloaded.\n");
>  }
>  
> -static void  hi_func(void)
> +static int hi_func(void)
>  {
>      printk(KERN_DEBUG "%s: Hi! (called %u times)\n", __func__, ++cnt);
> +
> +    return 0;
>  };
> +/* Safe to cast away the return value for this trivial case. */
> +void (void_hi_func)(void) __attribute__((alias("hi_func")));
>  
>  static void check_fnc(void)
>  {
> @@ -43,7 +49,7 @@ LIVEPATCH_UNLOAD_HOOK(revert_hook);
>  /* Imbalance here. Two load and three unload. */
>  
>  LIVEPATCH_LOAD_HOOK(hi_func);
> -LIVEPATCH_UNLOAD_HOOK(hi_func);
> +LIVEPATCH_UNLOAD_HOOK(void_hi_func);
>  
>  LIVEPATCH_UNLOAD_HOOK(check_fnc);
>  
> -- 
> 2.11.0
>
Jan Beulich Nov. 6, 2019, 2:35 p.m. UTC | #2
On 05.11.2019 20:43, Andrew Cooper wrote:
> --- a/xen/common/livepatch.c
> +++ b/xen/common/livepatch.c
> @@ -1076,25 +1076,33 @@ static int apply_payload(struct payload *data)
>       * temporarily disable the spin locks IRQ state checks.
>       */
>      spin_debug_disable();
> -    for ( i = 0; i < data->n_load_funcs; i++ )
> -        data->load_funcs[i]();
> +    for ( i = 0; !rc && i < data->n_load_funcs; i++ )
> +        rc = data->load_funcs[i]();
>      spin_debug_enable();
>  
> +    if ( rc )
> +        printk(XENLOG_ERR LIVEPATCH "%s: load_funcs[%u] failed: %d\n",
> +               data->name, i, rc);

Is there a possible problem here if some of the load_funcs()
succeeded before one fails? Or are those required to not do
any state change to the system (which would need rolling back)?

Jan
Ross Lagerwall Nov. 8, 2019, 10:21 a.m. UTC | #3
On 11/5/19 7:43 PM, Andrew Cooper wrote:
> One use of load hooks is to perform a safety check of the system in its
> quiesced state.  Any non-zero return value from a load hook will abort the
> apply attempt.
> 
> Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>

Reviewed-by: Ross Lagerwall <ross.lagerwall@citrix.com>

Patch
diff mbox series

diff --git a/xen/common/livepatch.c b/xen/common/livepatch.c
index 7caa30c202..962647616a 100644
--- a/xen/common/livepatch.c
+++ b/xen/common/livepatch.c
@@ -1076,25 +1076,33 @@  static int apply_payload(struct payload *data)
      * temporarily disable the spin locks IRQ state checks.
      */
     spin_debug_disable();
-    for ( i = 0; i < data->n_load_funcs; i++ )
-        data->load_funcs[i]();
+    for ( i = 0; !rc && i < data->n_load_funcs; i++ )
+        rc = data->load_funcs[i]();
     spin_debug_enable();
 
+    if ( rc )
+        printk(XENLOG_ERR LIVEPATCH "%s: load_funcs[%u] failed: %d\n",
+               data->name, i, rc);
+
     ASSERT(!local_irq_is_enabled());
 
-    for ( i = 0; i < data->nfuncs; i++ )
-        arch_livepatch_apply(&data->funcs[i]);
+    if ( !rc )
+        for ( i = 0; i < data->nfuncs; i++ )
+            arch_livepatch_apply(&data->funcs[i]);
 
     arch_livepatch_revive();
 
-    /*
-     * We need RCU variant (which has barriers) in case we crash here.
-     * The applied_list is iterated by the trap code.
-     */
-    list_add_tail_rcu(&data->applied_list, &applied_list);
-    register_virtual_region(&data->region);
+    if ( !rc )
+    {
+        /*
+         * We need RCU variant (which has barriers) in case we crash here.
+         * The applied_list is iterated by the trap code.
+         */
+        list_add_tail_rcu(&data->applied_list, &applied_list);
+        register_virtual_region(&data->region);
+    }
 
-    return 0;
+    return rc;
 }
 
 static int revert_payload(struct payload *data)
diff --git a/xen/include/xen/livepatch_payload.h b/xen/include/xen/livepatch_payload.h
index 4a1a96d054..3788b52ddf 100644
--- a/xen/include/xen/livepatch_payload.h
+++ b/xen/include/xen/livepatch_payload.h
@@ -9,7 +9,7 @@ 
  * The following definitions are to be used in patches. They are taken
  * from kpatch.
  */
-typedef void livepatch_loadcall_t(void);
+typedef int livepatch_loadcall_t(void);
 typedef void livepatch_unloadcall_t(void);
 
 /*
diff --git a/xen/test/livepatch/xen_hello_world.c b/xen/test/livepatch/xen_hello_world.c
index 02f3f85dc0..d720001f07 100644
--- a/xen/test/livepatch/xen_hello_world.c
+++ b/xen/test/livepatch/xen_hello_world.c
@@ -16,9 +16,11 @@  static const char hello_world_patch_this_fnc[] = "xen_extra_version";
 extern const char *xen_hello_world(void);
 static unsigned int cnt;
 
-static void apply_hook(void)
+static int apply_hook(void)
 {
     printk(KERN_DEBUG "Hook executing.\n");
+
+    return 0;
 }
 
 static void revert_hook(void)
@@ -26,10 +28,14 @@  static void revert_hook(void)
     printk(KERN_DEBUG "Hook unloaded.\n");
 }
 
-static void  hi_func(void)
+static int hi_func(void)
 {
     printk(KERN_DEBUG "%s: Hi! (called %u times)\n", __func__, ++cnt);
+
+    return 0;
 };
+/* Safe to cast away the return value for this trivial case. */
+void (void_hi_func)(void) __attribute__((alias("hi_func")));
 
 static void check_fnc(void)
 {
@@ -43,7 +49,7 @@  LIVEPATCH_UNLOAD_HOOK(revert_hook);
 /* Imbalance here. Two load and three unload. */
 
 LIVEPATCH_LOAD_HOOK(hi_func);
-LIVEPATCH_UNLOAD_HOOK(hi_func);
+LIVEPATCH_UNLOAD_HOOK(void_hi_func);
 
 LIVEPATCH_UNLOAD_HOOK(check_fnc);