diff mbox series

[v3,3/4] livepatch: refuse to resolve symbols that belong to init sections

Message ID 20240423131249.29825-4-roger.pau@citrix.com (mailing list archive)
State Superseded
Headers show
Series livepatch: minor bug fixes and improvements | expand

Commit Message

Roger Pau Monné April 23, 2024, 1:12 p.m. UTC
Livepatch payloads containing symbols that belong to init sections can only
lead to page faults later on, as by the time the livepatch is loaded init
sections have already been freed.

Refuse to resolve such symbols and return an error instead.

Note such resolutions are only relevant for symbols that point to undefined
sections (SHN_UNDEF), as that implies the symbol is not in the current payload
and hence must either be a Xen or a different livepatch payload symbol.

Do not allow to resolve symbols that point to __init_begin, as that address is
also unmapped.  On the other hand, __init_end is not unmapped, and hence allow
resolutions against it.

Since __init_begin can alias other symbols (like _erodata for example)
allow the force flag to override the check and resolve the symbol anyway.

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
---
Changes since v2:
 - Allow bypassing added check with the force flag.

Changes since v1:
 - Fix off-by-one in range checking.
---
 xen/common/livepatch.c          | 13 ++++++++-----
 xen/common/livepatch_elf.c      | 18 +++++++++++++++++-
 xen/include/xen/livepatch_elf.h |  2 +-
 3 files changed, 26 insertions(+), 7 deletions(-)

Comments

Jan Beulich April 23, 2024, 1:44 p.m. UTC | #1
On 23.04.2024 15:12, Roger Pau Monne wrote:
> Livepatch payloads containing symbols that belong to init sections can only
> lead to page faults later on, as by the time the livepatch is loaded init
> sections have already been freed.
> 
> Refuse to resolve such symbols and return an error instead.
> 
> Note such resolutions are only relevant for symbols that point to undefined
> sections (SHN_UNDEF), as that implies the symbol is not in the current payload
> and hence must either be a Xen or a different livepatch payload symbol.
> 
> Do not allow to resolve symbols that point to __init_begin, as that address is
> also unmapped.  On the other hand, __init_end is not unmapped, and hence allow
> resolutions against it.
> 
> Since __init_begin can alias other symbols (like _erodata for example)
> allow the force flag to override the check and resolve the symbol anyway.
> 
> Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>

In principle, as promised (and just to indicate earlier concerns were
addressed, as this is meaningless for other purposes)
Acked-by: Jan Beulich <jbeulich@suse.com>
However, ...

> @@ -310,6 +311,21 @@ int livepatch_elf_resolve_symbols(struct livepatch_elf *elf)
>                      break;
>                  }
>              }
> +
> +            /*
> +             * Ensure not an init symbol.  Only applicable to Xen symbols, as
> +             * livepatch payloads don't have init sections or equivalent.
> +             */
> +            else if ( st_value >= (uintptr_t)&__init_begin &&
> +                      st_value <  (uintptr_t)&__init_end && !force )
> +            {
> +                printk(XENLOG_ERR LIVEPATCH
> +                       "%s: symbol %s is in init section, not resolving\n",
> +                       elf->name, elf->sym[i].name);
> +                rc = -ENXIO;
> +                break;
> +            }

... wouldn't it make sense to still warn in this case when "force" is set?

Jan
Roger Pau Monné April 23, 2024, 2:26 p.m. UTC | #2
On Tue, Apr 23, 2024 at 03:44:42PM +0200, Jan Beulich wrote:
> On 23.04.2024 15:12, Roger Pau Monne wrote:
> > Livepatch payloads containing symbols that belong to init sections can only
> > lead to page faults later on, as by the time the livepatch is loaded init
> > sections have already been freed.
> > 
> > Refuse to resolve such symbols and return an error instead.
> > 
> > Note such resolutions are only relevant for symbols that point to undefined
> > sections (SHN_UNDEF), as that implies the symbol is not in the current payload
> > and hence must either be a Xen or a different livepatch payload symbol.
> > 
> > Do not allow to resolve symbols that point to __init_begin, as that address is
> > also unmapped.  On the other hand, __init_end is not unmapped, and hence allow
> > resolutions against it.
> > 
> > Since __init_begin can alias other symbols (like _erodata for example)
> > allow the force flag to override the check and resolve the symbol anyway.
> > 
> > Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
> 
> In principle, as promised (and just to indicate earlier concerns were
> addressed, as this is meaningless for other purposes)
> Acked-by: Jan Beulich <jbeulich@suse.com>
> However, ...
> 
> > @@ -310,6 +311,21 @@ int livepatch_elf_resolve_symbols(struct livepatch_elf *elf)
> >                      break;
> >                  }
> >              }
> > +
> > +            /*
> > +             * Ensure not an init symbol.  Only applicable to Xen symbols, as
> > +             * livepatch payloads don't have init sections or equivalent.
> > +             */
> > +            else if ( st_value >= (uintptr_t)&__init_begin &&
> > +                      st_value <  (uintptr_t)&__init_end && !force )
> > +            {
> > +                printk(XENLOG_ERR LIVEPATCH
> > +                       "%s: symbol %s is in init section, not resolving\n",
> > +                       elf->name, elf->sym[i].name);
> > +                rc = -ENXIO;
> > +                break;
> > +            }
> 
> ... wouldn't it make sense to still warn in this case when "force" is set?

Pondered it, I was thinking that a user would first run without
--force, and use the option as a result of seeing the first failure.

However if there is more than one check that's bypassed, further ones
won't be noticed, so:

            else if ( st_value >= (uintptr_t)&__init_begin &&
                      st_value <  (uintptr_t)&__init_end )
            {
                printk(XENLOG_ERR LIVEPATCH
                       "%s: symbol %s is in init section, not resolving\n",
                       elf->name, elf->sym[i].name);
                if ( !force )
                {
                    rc = -ENXIO;
                    break;
                }
            }

Would be OK then?

Thanks, Roger.
Jan Beulich April 23, 2024, 2:28 p.m. UTC | #3
On 23.04.2024 16:26, Roger Pau Monné wrote:
> On Tue, Apr 23, 2024 at 03:44:42PM +0200, Jan Beulich wrote:
>> On 23.04.2024 15:12, Roger Pau Monne wrote:
>>> Livepatch payloads containing symbols that belong to init sections can only
>>> lead to page faults later on, as by the time the livepatch is loaded init
>>> sections have already been freed.
>>>
>>> Refuse to resolve such symbols and return an error instead.
>>>
>>> Note such resolutions are only relevant for symbols that point to undefined
>>> sections (SHN_UNDEF), as that implies the symbol is not in the current payload
>>> and hence must either be a Xen or a different livepatch payload symbol.
>>>
>>> Do not allow to resolve symbols that point to __init_begin, as that address is
>>> also unmapped.  On the other hand, __init_end is not unmapped, and hence allow
>>> resolutions against it.
>>>
>>> Since __init_begin can alias other symbols (like _erodata for example)
>>> allow the force flag to override the check and resolve the symbol anyway.
>>>
>>> Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
>>
>> In principle, as promised (and just to indicate earlier concerns were
>> addressed, as this is meaningless for other purposes)
>> Acked-by: Jan Beulich <jbeulich@suse.com>
>> However, ...
>>
>>> @@ -310,6 +311,21 @@ int livepatch_elf_resolve_symbols(struct livepatch_elf *elf)
>>>                      break;
>>>                  }
>>>              }
>>> +
>>> +            /*
>>> +             * Ensure not an init symbol.  Only applicable to Xen symbols, as
>>> +             * livepatch payloads don't have init sections or equivalent.
>>> +             */
>>> +            else if ( st_value >= (uintptr_t)&__init_begin &&
>>> +                      st_value <  (uintptr_t)&__init_end && !force )
>>> +            {
>>> +                printk(XENLOG_ERR LIVEPATCH
>>> +                       "%s: symbol %s is in init section, not resolving\n",
>>> +                       elf->name, elf->sym[i].name);
>>> +                rc = -ENXIO;
>>> +                break;
>>> +            }
>>
>> ... wouldn't it make sense to still warn in this case when "force" is set?
> 
> Pondered it, I was thinking that a user would first run without
> --force, and use the option as a result of seeing the first failure.
> 
> However if there is more than one check that's bypassed, further ones
> won't be noticed, so:
> 
>             else if ( st_value >= (uintptr_t)&__init_begin &&
>                       st_value <  (uintptr_t)&__init_end )
>             {
>                 printk(XENLOG_ERR LIVEPATCH
>                        "%s: symbol %s is in init section, not resolving\n",
>                        elf->name, elf->sym[i].name);
>                 if ( !force )
>                 {
>                     rc = -ENXIO;
>                     break;
>                 }
>             }
> 
> Would be OK then?

Perhaps. "not resolving" isn't quite true when "force" is true, and warnings
would also better not be issued with XENLOG_ERR.

Jan
Roger Pau Monné April 23, 2024, 3:03 p.m. UTC | #4
On Tue, Apr 23, 2024 at 04:28:59PM +0200, Jan Beulich wrote:
> On 23.04.2024 16:26, Roger Pau Monné wrote:
> > On Tue, Apr 23, 2024 at 03:44:42PM +0200, Jan Beulich wrote:
> >> On 23.04.2024 15:12, Roger Pau Monne wrote:
> >>> Livepatch payloads containing symbols that belong to init sections can only
> >>> lead to page faults later on, as by the time the livepatch is loaded init
> >>> sections have already been freed.
> >>>
> >>> Refuse to resolve such symbols and return an error instead.
> >>>
> >>> Note such resolutions are only relevant for symbols that point to undefined
> >>> sections (SHN_UNDEF), as that implies the symbol is not in the current payload
> >>> and hence must either be a Xen or a different livepatch payload symbol.
> >>>
> >>> Do not allow to resolve symbols that point to __init_begin, as that address is
> >>> also unmapped.  On the other hand, __init_end is not unmapped, and hence allow
> >>> resolutions against it.
> >>>
> >>> Since __init_begin can alias other symbols (like _erodata for example)
> >>> allow the force flag to override the check and resolve the symbol anyway.
> >>>
> >>> Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
> >>
> >> In principle, as promised (and just to indicate earlier concerns were
> >> addressed, as this is meaningless for other purposes)
> >> Acked-by: Jan Beulich <jbeulich@suse.com>
> >> However, ...
> >>
> >>> @@ -310,6 +311,21 @@ int livepatch_elf_resolve_symbols(struct livepatch_elf *elf)
> >>>                      break;
> >>>                  }
> >>>              }
> >>> +
> >>> +            /*
> >>> +             * Ensure not an init symbol.  Only applicable to Xen symbols, as
> >>> +             * livepatch payloads don't have init sections or equivalent.
> >>> +             */
> >>> +            else if ( st_value >= (uintptr_t)&__init_begin &&
> >>> +                      st_value <  (uintptr_t)&__init_end && !force )
> >>> +            {
> >>> +                printk(XENLOG_ERR LIVEPATCH
> >>> +                       "%s: symbol %s is in init section, not resolving\n",
> >>> +                       elf->name, elf->sym[i].name);
> >>> +                rc = -ENXIO;
> >>> +                break;
> >>> +            }
> >>
> >> ... wouldn't it make sense to still warn in this case when "force" is set?
> > 
> > Pondered it, I was thinking that a user would first run without
> > --force, and use the option as a result of seeing the first failure.
> > 
> > However if there is more than one check that's bypassed, further ones
> > won't be noticed, so:
> > 
> >             else if ( st_value >= (uintptr_t)&__init_begin &&
> >                       st_value <  (uintptr_t)&__init_end )
> >             {
> >                 printk(XENLOG_ERR LIVEPATCH
> >                        "%s: symbol %s is in init section, not resolving\n",
> >                        elf->name, elf->sym[i].name);
> >                 if ( !force )
> >                 {
> >                     rc = -ENXIO;
> >                     break;
> >                 }
> >             }
> > 
> > Would be OK then?
> 
> Perhaps. "not resolving" isn't quite true when "force" is true, and warnings
> would also better not be issued with XENLOG_ERR.

I was assuming that printing as XENLOG_ERR level would still be OK -
even if bypassed because of the usage of --force.  The "not resolving"
part should indeed go away. Maybe this is better:

            else if ( st_value >= (uintptr_t)&__init_begin &&
                      st_value <  (uintptr_t)&__init_end )
            {
                printk("%s" LIVEPATCH "%s: symbol %s is in init section%s\n",
                       force ? XENLOG_WARNING : XENLOG_ERR,
                       elf->name, elf->sym[i].name,
                       force ? "" : ", not resolving");
                if ( !force )
                {
                    rc = -ENXIO;
                    break;
                }
            }

Thanks, Roger.
Jan Beulich April 23, 2024, 4:07 p.m. UTC | #5
On 23.04.2024 17:03, Roger Pau Monné wrote:
> On Tue, Apr 23, 2024 at 04:28:59PM +0200, Jan Beulich wrote:
>> On 23.04.2024 16:26, Roger Pau Monné wrote:
>>> On Tue, Apr 23, 2024 at 03:44:42PM +0200, Jan Beulich wrote:
>>>> On 23.04.2024 15:12, Roger Pau Monne wrote:
>>>>> Livepatch payloads containing symbols that belong to init sections can only
>>>>> lead to page faults later on, as by the time the livepatch is loaded init
>>>>> sections have already been freed.
>>>>>
>>>>> Refuse to resolve such symbols and return an error instead.
>>>>>
>>>>> Note such resolutions are only relevant for symbols that point to undefined
>>>>> sections (SHN_UNDEF), as that implies the symbol is not in the current payload
>>>>> and hence must either be a Xen or a different livepatch payload symbol.
>>>>>
>>>>> Do not allow to resolve symbols that point to __init_begin, as that address is
>>>>> also unmapped.  On the other hand, __init_end is not unmapped, and hence allow
>>>>> resolutions against it.
>>>>>
>>>>> Since __init_begin can alias other symbols (like _erodata for example)
>>>>> allow the force flag to override the check and resolve the symbol anyway.
>>>>>
>>>>> Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
>>>>
>>>> In principle, as promised (and just to indicate earlier concerns were
>>>> addressed, as this is meaningless for other purposes)
>>>> Acked-by: Jan Beulich <jbeulich@suse.com>
>>>> However, ...
>>>>
>>>>> @@ -310,6 +311,21 @@ int livepatch_elf_resolve_symbols(struct livepatch_elf *elf)
>>>>>                      break;
>>>>>                  }
>>>>>              }
>>>>> +
>>>>> +            /*
>>>>> +             * Ensure not an init symbol.  Only applicable to Xen symbols, as
>>>>> +             * livepatch payloads don't have init sections or equivalent.
>>>>> +             */
>>>>> +            else if ( st_value >= (uintptr_t)&__init_begin &&
>>>>> +                      st_value <  (uintptr_t)&__init_end && !force )
>>>>> +            {
>>>>> +                printk(XENLOG_ERR LIVEPATCH
>>>>> +                       "%s: symbol %s is in init section, not resolving\n",
>>>>> +                       elf->name, elf->sym[i].name);
>>>>> +                rc = -ENXIO;
>>>>> +                break;
>>>>> +            }
>>>>
>>>> ... wouldn't it make sense to still warn in this case when "force" is set?
>>>
>>> Pondered it, I was thinking that a user would first run without
>>> --force, and use the option as a result of seeing the first failure.
>>>
>>> However if there is more than one check that's bypassed, further ones
>>> won't be noticed, so:
>>>
>>>             else if ( st_value >= (uintptr_t)&__init_begin &&
>>>                       st_value <  (uintptr_t)&__init_end )
>>>             {
>>>                 printk(XENLOG_ERR LIVEPATCH
>>>                        "%s: symbol %s is in init section, not resolving\n",
>>>                        elf->name, elf->sym[i].name);
>>>                 if ( !force )
>>>                 {
>>>                     rc = -ENXIO;
>>>                     break;
>>>                 }
>>>             }
>>>
>>> Would be OK then?
>>
>> Perhaps. "not resolving" isn't quite true when "force" is true, and warnings
>> would also better not be issued with XENLOG_ERR.
> 
> I was assuming that printing as XENLOG_ERR level would still be OK -
> even if bypassed because of the usage of --force.  The "not resolving"
> part should indeed go away. Maybe this is better:
> 
>             else if ( st_value >= (uintptr_t)&__init_begin &&
>                       st_value <  (uintptr_t)&__init_end )
>             {
>                 printk("%s" LIVEPATCH "%s: symbol %s is in init section%s\n",
>                        force ? XENLOG_WARNING : XENLOG_ERR,
>                        elf->name, elf->sym[i].name,
>                        force ? "" : ", not resolving");
>                 if ( !force )
>                 {
>                     rc = -ENXIO;
>                     break;
>                 }
>             }

I'd be okay with this; the livepatch maintainers will have the ultimate say.

Jan
diff mbox series

Patch

diff --git a/xen/common/livepatch.c b/xen/common/livepatch.c
index 1503a84457e4..36cf4bee8b8a 100644
--- a/xen/common/livepatch.c
+++ b/xen/common/livepatch.c
@@ -1080,7 +1080,8 @@  static void free_payload(struct payload *data)
     xfree(data);
 }
 
-static int load_payload_data(struct payload *payload, void *raw, size_t len)
+static int load_payload_data(struct payload *payload, void *raw, size_t len,
+                             bool force)
 {
     struct livepatch_elf elf = { .name = payload->name, .len = len };
     int rc = 0;
@@ -1093,7 +1094,7 @@  static int load_payload_data(struct payload *payload, void *raw, size_t len)
     if ( rc )
         goto out;
 
-    rc = livepatch_elf_resolve_symbols(&elf);
+    rc = livepatch_elf_resolve_symbols(&elf, force);
     if ( rc )
         goto out;
 
@@ -1133,7 +1134,8 @@  static int load_payload_data(struct payload *payload, void *raw, size_t len)
     return rc;
 }
 
-static int livepatch_upload(struct xen_sysctl_livepatch_upload *upload)
+static int livepatch_upload(struct xen_sysctl_livepatch_upload *upload,
+                            bool force)
 {
     struct payload *data, *found;
     char n[XEN_LIVEPATCH_NAME_SIZE];
@@ -1162,7 +1164,7 @@  static int livepatch_upload(struct xen_sysctl_livepatch_upload *upload)
     {
         memcpy(data->name, n, strlen(n));
 
-        rc = load_payload_data(data, raw_data, upload->size);
+        rc = load_payload_data(data, raw_data, upload->size, force);
         if ( rc )
             goto out;
 
@@ -2132,7 +2134,8 @@  int livepatch_op(struct xen_sysctl_livepatch_op *livepatch)
     switch ( livepatch->cmd )
     {
     case XEN_SYSCTL_LIVEPATCH_UPLOAD:
-        rc = livepatch_upload(&livepatch->u.upload);
+        rc = livepatch_upload(&livepatch->u.upload,
+                              livepatch->flags & LIVEPATCH_FLAG_FORCE);
         break;
 
     case XEN_SYSCTL_LIVEPATCH_GET:
diff --git a/xen/common/livepatch_elf.c b/xen/common/livepatch_elf.c
index 45d73912a3cd..0436f2d5fcbd 100644
--- a/xen/common/livepatch_elf.c
+++ b/xen/common/livepatch_elf.c
@@ -4,6 +4,7 @@ 
 
 #include <xen/errno.h>
 #include <xen/lib.h>
+#include <xen/sections.h>
 #include <xen/symbols.h>
 #include <xen/livepatch_elf.h>
 #include <xen/livepatch.h>
@@ -276,7 +277,7 @@  static int elf_get_sym(struct livepatch_elf *elf, const void *data)
     return 0;
 }
 
-int livepatch_elf_resolve_symbols(struct livepatch_elf *elf)
+int livepatch_elf_resolve_symbols(struct livepatch_elf *elf, bool force)
 {
     unsigned int i;
     int rc = 0;
@@ -310,6 +311,21 @@  int livepatch_elf_resolve_symbols(struct livepatch_elf *elf)
                     break;
                 }
             }
+
+            /*
+             * Ensure not an init symbol.  Only applicable to Xen symbols, as
+             * livepatch payloads don't have init sections or equivalent.
+             */
+            else if ( st_value >= (uintptr_t)&__init_begin &&
+                      st_value <  (uintptr_t)&__init_end && !force )
+            {
+                printk(XENLOG_ERR LIVEPATCH
+                       "%s: symbol %s is in init section, not resolving\n",
+                       elf->name, elf->sym[i].name);
+                rc = -ENXIO;
+                break;
+            }
+
             dprintk(XENLOG_DEBUG, LIVEPATCH "%s: Undefined symbol resolved: %s => %#"PRIxElfAddr"\n",
                     elf->name, elf->sym[i].name, st_value);
             break;
diff --git a/xen/include/xen/livepatch_elf.h b/xen/include/xen/livepatch_elf.h
index 7116deaddc28..84e9c5eb7be5 100644
--- a/xen/include/xen/livepatch_elf.h
+++ b/xen/include/xen/livepatch_elf.h
@@ -44,7 +44,7 @@  livepatch_elf_sec_by_name(const struct livepatch_elf *elf,
 int livepatch_elf_load(struct livepatch_elf *elf, const void *data);
 void livepatch_elf_free(struct livepatch_elf *elf);
 
-int livepatch_elf_resolve_symbols(struct livepatch_elf *elf);
+int livepatch_elf_resolve_symbols(struct livepatch_elf *elf, bool force);
 int livepatch_elf_perform_relocs(struct livepatch_elf *elf);
 
 static inline bool livepatch_elf_ignore_section(const Elf_Shdr *sec)