diff mbox

[1/8] libelf: loop safety: Introduce elf_iter_ok and elf_strcmp_safe

Message ID 1481298289-13546-2-git-send-email-ian.jackson@eu.citrix.com (mailing list archive)
State New, archived
Headers show

Commit Message

Ian Jackson Dec. 9, 2016, 3:44 p.m. UTC
This will allow us to keep track of the total amount of work we are
doing.  When it becomes excessive, we mark the ELF broken, and stop
processing.

This is a more robust way of preventing DoS problems by bad images
than attempting to prove, for each of the (sometimes rather deeply
nested) loops, that the total work is "reasonable".  We bound the
notional work by 4x the image size (plus 1M).

Also introduce elf_strcmp_safe, which unconditionally does the work,
but increments the count so any outer loop may be aborted if
necessary.

Currently there are no callers, so no functional change.

Signed-off-by: Ian Jackson <Ian.Jackson@eu.citrix.com>
---
 xen/common/libelf/libelf-loader.c | 14 ++++++++++++++
 xen/include/xen/libelf.h          | 21 +++++++++++++++++++++
 2 files changed, 35 insertions(+)

Comments

Jan Beulich Dec. 12, 2016, 3:02 p.m. UTC | #1
>>> On 09.12.16 at 16:44, <ian.jackson@eu.citrix.com> wrote:
> This will allow us to keep track of the total amount of work we are
> doing.  When it becomes excessive, we mark the ELF broken, and stop
> processing.
> 
> This is a more robust way of preventing DoS problems by bad images
> than attempting to prove, for each of the (sometimes rather deeply
> nested) loops, that the total work is "reasonable".  We bound the
> notional work by 4x the image size (plus 1M).

The 4x has been selected arbitrarily, I suppose?

> --- a/xen/common/libelf/libelf-loader.c
> +++ b/xen/common/libelf/libelf-loader.c
> @@ -38,6 +38,7 @@ elf_errorstatus elf_init(struct elf_binary *elf, const char 
> *image_input, size_t
>      ELF_HANDLE_DECL(elf_shdr) shdr;
>      unsigned i, count, section, link;
>      uint64_t offset;
> +    const uint64_t max_size_for_deacc = (1UL << 63)/ELF_MAX_ITERATION_FACTOR;

This will need adjustment for 32-bit tool stack builds - did you
perhaps mean UINT64_C(1), considering the type of the variable?

Also please add blanks around the / .

> @@ -546,6 +551,15 @@ uint64_t elf_lookup_addr(struct elf_binary * elf, const char *symbol)
>      return value;
>  }
>  
> +bool elf_iter_ok_counted(struct elf_binary *elf, uint64_t maxcopysz) {
> +    if (maxcopysz > elf->iteration_deaccumulator)
> +        elf_mark_broken(elf, "excessive iteration - too much work to parse");
> +    if (elf->broken)
> +        return false;
> +    elf->iteration_deaccumulator -= maxcopysz;
> +    return true;
> +}

Hypervisor coding style here please (missing lots of blanks, and the
opening brace needs to go on its own line). I think there are more
such style problems further down.

And then - would it perhaps make sense to have most of this
function's body in #ifndef __XEN__, as there's nothing to guard
against when loading Dom0?

Jan
Jan Beulich Dec. 12, 2016, 3:15 p.m. UTC | #2
>>> On 09.12.16 at 16:44, <ian.jackson@eu.citrix.com> wrote:
> +static inline bool elf_iter_ok(struct elf_binary *elf)
> +    { return elf_iter_ok_counted(elf,1); }

Having seen first uses of this - why is this 1 instead of 0? Once it is,
calling elf_iter_ok_counted() here would be rather pointless, and
checking just elf_broken() here would allow the parameter to become
const.

Jan
Ian Jackson Dec. 12, 2016, 3:23 p.m. UTC | #3
Jan Beulich writes ("Re: [PATCH 1/8] libelf: loop safety: Introduce elf_iter_ok and elf_strcmp_safe"):
> On 09.12.16 at 16:44, <ian.jackson@eu.citrix.com> wrote:
> > This is a more robust way of preventing DoS problems by bad images
> > than attempting to prove, for each of the (sometimes rather deeply
> > nested) loops, that the total work is "reasonable".  We bound the
> > notional work by 4x the image size (plus 1M).
> 
> The 4x has been selected arbitrarily, I suppose?

Yes.

> >      uint64_t offset;
> > +    const uint64_t max_size_for_deacc = (1UL << 63)/ELF_MAX_ITERATION_FACTOR;
> 
> This will need adjustment for 32-bit tool stack builds - did you
> perhaps mean UINT64_C(1), considering the type of the variable?

Oops, yes.  Although it has to be (uint64_t)1 since the tools do not
have UINT64_C.

> Also please add blanks around the / .

Done.

> Hypervisor coding style here please (missing lots of blanks, and the
> opening brace needs to go on its own line). I think there are more
> such style problems further down.

Sorry, I will (try to) fix the style in all the patches.

> And then - would it perhaps make sense to have most of this
> function's body in #ifndef __XEN__, as there's nothing to guard
> against when loading Dom0?

This is a good idea.  If it's OK with you I'll leave the variable
initialisation etc., and simply stub out body of elf_iter_ok_counted.
If you want a more comprehensive approach I can add more #ifdefs.

Thanks,
Ian.
Jan Beulich Dec. 12, 2016, 3:51 p.m. UTC | #4
>>> On 09.12.16 at 16:44, <ian.jackson@eu.citrix.com> wrote:
> --- a/xen/common/libelf/libelf-loader.c
> +++ b/xen/common/libelf/libelf-loader.c
> @@ -38,6 +38,7 @@ elf_errorstatus elf_init(struct elf_binary *elf, const char *image_input, size_t
>      ELF_HANDLE_DECL(elf_shdr) shdr;
>      unsigned i, count, section, link;
>      uint64_t offset;
> +    const uint64_t max_size_for_deacc = (1UL << 63)/ELF_MAX_ITERATION_FACTOR;
>  
>      if ( !elf_is_elfbinary(image_input, size) )
>      {
> @@ -52,6 +53,10 @@ elf_errorstatus elf_init(struct elf_binary *elf, const char *image_input, size_t
>      elf->class = elf_uval_3264(elf, elf->ehdr, e32.e_ident[EI_CLASS]);
>      elf->data = elf_uval_3264(elf, elf->ehdr, e32.e_ident[EI_DATA]);
>  
> +    elf->iteration_deaccumulator = 1024*1024 +
> +        (size > max_size_for_deacc ? max_size_for_deacc : size)
> +        * ELF_MAX_ITERATION_FACTOR;        

One more question here: Is this useful at all? You're allowing
for approximately 2**63 accounted operations - how big does
an image need to be to actually break this limit? XSA-25 already
limited image sizes to 1Gb (but I do understand that the
guarding here is also against e.g. redundant loading of the
same bits through multiple program header table entries).

And how long will it take you to reach that limit (and to cause
elf->broken to be set)? With 1ns per accounted operation,
that'll be on the order of 270 years. Am I missing something
here?

Jan
Ian Jackson Dec. 12, 2016, 4 p.m. UTC | #5
Jan Beulich writes ("Re: [PATCH 1/8] libelf: loop safety: Introduce elf_iter_ok and elf_strcmp_safe"):
> On 09.12.16 at 16:44, <ian.jackson@eu.citrix.com> wrote:
> > +    const uint64_t max_size_for_deacc = (1UL << 63)/ELF_MAX_ITERATION_FACTOR;
...
> > +    elf->iteration_deaccumulator = 1024*1024 +
> > +        (size > max_size_for_deacc ? max_size_for_deacc : size)
> > +        * ELF_MAX_ITERATION_FACTOR;        
> 
> One more question here: Is this useful at all? You're allowing
> for approximately 2**63 accounted operations - how big does
> an image need to be to actually break this limit? XSA-25 already
> limited image sizes to 1Gb (but I do understand that the
> guarding here is also against e.g. redundant loading of the
> same bits through multiple program header table entries).
> 
> And how long will it take you to reach that limit (and to cause
> elf->broken to be set)? With 1ns per accounted operation,
> that'll be on the order of 270 years. Am I missing something
> here?

I'm not sure of your point.

Mostly I allow for 4x the size of the image, plus a fixed constant of
1M operations.

The max_size_for_deacc part is necessary because otherwise the
calculation "size * ELF_MAX_ITERATION_FACTOR" might overflow.  It
seems unreasonable to simply allow that overflow to occur.  But if it
is causing confusion we could do that.  The result would be a low
value for iteration_deaccumulator.

In practice the 1Gby image size limit will prevent this limit from
being reached, but it is distant from this check.

Ian.
Jan Beulich Dec. 12, 2016, 4:16 p.m. UTC | #6
>>> On 12.12.16 at 17:00, <ian.jackson@eu.citrix.com> wrote:
> Jan Beulich writes ("Re: [PATCH 1/8] libelf: loop safety: Introduce 
> elf_iter_ok and elf_strcmp_safe"):
>> On 09.12.16 at 16:44, <ian.jackson@eu.citrix.com> wrote:
>> > +    const uint64_t max_size_for_deacc = (1UL << 63)/ELF_MAX_ITERATION_FACTOR;
> ...
>> > +    elf->iteration_deaccumulator = 1024*1024 +
>> > +        (size > max_size_for_deacc ? max_size_for_deacc : size)
>> > +        * ELF_MAX_ITERATION_FACTOR;        
>> 
>> One more question here: Is this useful at all? You're allowing
>> for approximately 2**63 accounted operations - how big does
>> an image need to be to actually break this limit? XSA-25 already
>> limited image sizes to 1Gb (but I do understand that the
>> guarding here is also against e.g. redundant loading of the
>> same bits through multiple program header table entries).
>> 
>> And how long will it take you to reach that limit (and to cause
>> elf->broken to be set)? With 1ns per accounted operation,
>> that'll be on the order of 270 years. Am I missing something
>> here?
> 
> I'm not sure of your point.
> 
> Mostly I allow for 4x the size of the image, plus a fixed constant of
> 1M operations.

Well, I have to confess that I've read the above as max() when
in fact it is min().

> The max_size_for_deacc part is necessary because otherwise the
> calculation "size * ELF_MAX_ITERATION_FACTOR" might overflow.  It
> seems unreasonable to simply allow that overflow to occur.  But if it
> is causing confusion we could do that.  The result would be a low
> value for iteration_deaccumulator.

Considering that overflow here will actually result in a comparably
smaller upper limit, I think this may help overall readability. But with
the above I won't insist on this in any way.

Jan
Ian Jackson Dec. 12, 2016, 4:56 p.m. UTC | #7
Jan Beulich writes ("Re: [PATCH 1/8] libelf: loop safety: Introduce elf_iter_ok and elf_strcmp_safe"):
> Well, I have to confess that I've read the above as max() when
> in fact it is min().

Sadly we can't use min() and max() here it seems.

> On 12.12.16 at 17:00, <ian.jackson@eu.citrix.com> wrote:
> > The max_size_for_deacc part is necessary because otherwise the
> > calculation "size * ELF_MAX_ITERATION_FACTOR" might overflow.  It
> > seems unreasonable to simply allow that overflow to occur.  But if it
> > is causing confusion we could do that.  The result would be a low
> > value for iteration_deaccumulator.
> 
> Considering that overflow here will actually result in a comparably
> smaller upper limit, I think this may help overall readability. But with
> the above I won't insist on this in any way.

I have replaced the limit with a comment.  Now I have:

    elf->iteration_deaccumulator =
        1024*1024 + size * ELF_MAX_ITERATION_FACTOR;        
        /* overflow (from very big size, probably rejected earlier)
         * would just lead to small limit, which is safe */

Ian.
Jan Beulich Dec. 13, 2016, 7:24 a.m. UTC | #8
>>> On 12.12.16 at 17:56, <ian.jackson@eu.citrix.com> wrote:
> Jan Beulich writes ("Re: [PATCH 1/8] libelf: loop safety: Introduce 
> elf_iter_ok and elf_strcmp_safe"):
>> Well, I have to confess that I've read the above as max() when
>> in fact it is min().
> 
> Sadly we can't use min() and max() here it seems.

Sure, I understand that.

>> On 12.12.16 at 17:00, <ian.jackson@eu.citrix.com> wrote:
>> > The max_size_for_deacc part is necessary because otherwise the
>> > calculation "size * ELF_MAX_ITERATION_FACTOR" might overflow.  It
>> > seems unreasonable to simply allow that overflow to occur.  But if it
>> > is causing confusion we could do that.  The result would be a low
>> > value for iteration_deaccumulator.
>> 
>> Considering that overflow here will actually result in a comparably
>> smaller upper limit, I think this may help overall readability. But with
>> the above I won't insist on this in any way.
> 
> I have replaced the limit with a comment.  Now I have:
> 
>     elf->iteration_deaccumulator =
>         1024*1024 + size * ELF_MAX_ITERATION_FACTOR;        
>         /* overflow (from very big size, probably rejected earlier)
>          * would just lead to small limit, which is safe */

Thanks. May I ask that you then also use proper hypervisor
style for that comment?

Jan
Ian Jackson Dec. 13, 2016, 4:04 p.m. UTC | #9
Jan Beulich writes ("Re: [PATCH 1/8] libelf: loop safety: Introduce elf_iter_ok and elf_strcmp_safe"):
> On 12.12.16 at 17:56, <ian.jackson@eu.citrix.com> wrote:
> > I have replaced the limit with a comment.  Now I have:
> > 
> >     elf->iteration_deaccumulator =
> >         1024*1024 + size * ELF_MAX_ITERATION_FACTOR;        
> >         /* overflow (from very big size, probably rejected earlier)
> >          * would just lead to small limit, which is safe */
> 
> Thanks. May I ask that you then also use proper hypervisor
> style for that comment?

You mean like this ?

    elf->iteration_deaccumulator =
        1024*1024 + size * ELF_MAX_ITERATION_FACTOR;
        /*
         * overflow (from very big size, probably rejected earlier)
         * would just lead to small limit, which is safe
         */

Sure, whatever.  Done.

Ian.
Jan Beulich Dec. 13, 2016, 4:37 p.m. UTC | #10
>>> On 13.12.16 at 17:04, <ian.jackson@eu.citrix.com> wrote:
> Jan Beulich writes ("Re: [PATCH 1/8] libelf: loop safety: Introduce 
> elf_iter_ok and elf_strcmp_safe"):
>> On 12.12.16 at 17:56, <ian.jackson@eu.citrix.com> wrote:
>> > I have replaced the limit with a comment.  Now I have:
>> > 
>> >     elf->iteration_deaccumulator =
>> >         1024*1024 + size * ELF_MAX_ITERATION_FACTOR;        
>> >         /* overflow (from very big size, probably rejected earlier)
>> >          * would just lead to small limit, which is safe */
>> 
>> Thanks. May I ask that you then also use proper hypervisor
>> style for that comment?
> 
> You mean like this ?
> 
>     elf->iteration_deaccumulator =
>         1024*1024 + size * ELF_MAX_ITERATION_FACTOR;
>         /*
>          * overflow (from very big size, probably rejected earlier)
>          * would just lead to small limit, which is safe
>          */
> 
> Sure, whatever.  Done.

Almost: Comments are also supposed to start with an upper
case letter. But I'm fine either way.

Jan
diff mbox

Patch

diff --git a/xen/common/libelf/libelf-loader.c b/xen/common/libelf/libelf-loader.c
index a72cd8a..00479af 100644
--- a/xen/common/libelf/libelf-loader.c
+++ b/xen/common/libelf/libelf-loader.c
@@ -38,6 +38,7 @@  elf_errorstatus elf_init(struct elf_binary *elf, const char *image_input, size_t
     ELF_HANDLE_DECL(elf_shdr) shdr;
     unsigned i, count, section, link;
     uint64_t offset;
+    const uint64_t max_size_for_deacc = (1UL << 63)/ELF_MAX_ITERATION_FACTOR;
 
     if ( !elf_is_elfbinary(image_input, size) )
     {
@@ -52,6 +53,10 @@  elf_errorstatus elf_init(struct elf_binary *elf, const char *image_input, size_t
     elf->class = elf_uval_3264(elf, elf->ehdr, e32.e_ident[EI_CLASS]);
     elf->data = elf_uval_3264(elf, elf->ehdr, e32.e_ident[EI_DATA]);
 
+    elf->iteration_deaccumulator = 1024*1024 +
+        (size > max_size_for_deacc ? max_size_for_deacc : size)
+        * ELF_MAX_ITERATION_FACTOR;        
+
     /* Sanity check phdr. */
     offset = elf_uval(elf, elf->ehdr, e_phoff) +
         elf_uval(elf, elf->ehdr, e_phentsize) * elf_phdr_count(elf);
@@ -546,6 +551,15 @@  uint64_t elf_lookup_addr(struct elf_binary * elf, const char *symbol)
     return value;
 }
 
+bool elf_iter_ok_counted(struct elf_binary *elf, uint64_t maxcopysz) {
+    if (maxcopysz > elf->iteration_deaccumulator)
+        elf_mark_broken(elf, "excessive iteration - too much work to parse");
+    if (elf->broken)
+        return false;
+    elf->iteration_deaccumulator -= maxcopysz;
+    return true;
+}
+
 /*
  * Local variables:
  * mode: C
diff --git a/xen/include/xen/libelf.h b/xen/include/xen/libelf.h
index 1b763f3..294231a 100644
--- a/xen/include/xen/libelf.h
+++ b/xen/include/xen/libelf.h
@@ -56,6 +56,8 @@  typedef void elf_log_callback(struct elf_binary*, void *caller_data,
 #define ELF_MAX_STRING_LENGTH 4096
 #define ELF_MAX_TOTAL_NOTE_COUNT 65536
 
+#define ELF_MAX_ITERATION_FACTOR 4
+
 /* ------------------------------------------------------------------------ */
 
 /* Macros for accessing the input image and output area. */
@@ -201,6 +203,9 @@  struct elf_binary {
     uint64_t bsd_symtab_pstart;
     uint64_t bsd_symtab_pend;
 
+    /* private */
+    uint64_t iteration_deaccumulator;
+    
     /*
      * caller's other acceptable destination.
      * Set by elf_set_xdest.  Do not set these directly.
@@ -264,6 +269,14 @@  uint64_t elf_access_unsigned(struct elf_binary *elf, elf_ptrval ptr,
 
 uint64_t elf_round_up(struct elf_binary *elf, uint64_t addr);
 
+bool elf_iter_ok_counted(struct elf_binary *elf, uint64_t count);
+  /* It is OK for count to be out by a smallish constant factor.
+   * It is OK for count to be 0, as we clamp it to 1, so we
+   * can use lengths or sizes from the image. */
+
+static inline bool elf_iter_ok(struct elf_binary *elf)
+    { return elf_iter_ok_counted(elf,1); }
+
 const char *elf_strval(struct elf_binary *elf, elf_ptrval start);
   /* may return NULL if the string is out of range etc. */
 
@@ -463,6 +476,14 @@  static inline void *elf_memset_unchecked(void *s, int c, size_t n)
    * memcpy, memset and memmove to undefined MISTAKE things.
    */
 
+static inline int elf_strcmp_safe(struct elf_binary *elf,
+                                  const char *a, const char *b) {
+    elf_iter_ok_counted(elf, strlen(b));
+    return strcmp(a,b);
+}
+  /* Unlike other *_safe functions, elf_strcmp_safe is called on
+   * values already extracted from the image (eg by elf_strval),
+   * and fixed constant strings (typically, the latter is "b"). */
 
 /* Advances past amount bytes of the current destination area. */
 static inline void ELF_ADVANCE_DEST(struct elf_binary *elf, uint64_t amount)