[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH] libelf: Fix div0 issues in elf_{shdr, phdr}_count()



Ian Jackson writes ("Re: [PATCH] libelf: Fix div0 issues in 
elf_{shdr,phdr}_count()"):
> Admittedly the rule about iteration count is not so easy to remember,
> and I had failed to anticipate that someone would introduce division.
> But both of those kind of bugs are denial of service, rather than
> privilege escalation.
> 
> Having stared at the commit message of a01b6d46 and at the
> pre-a01b6d46 code, I still don't understand what motivated the
> changes.

Jan and I had a conversation on irc.


Part of the motivation for a01b6d4 was the anomaly which is the
difference between the implementations of elf_phdr_size and
elf_shdr_size.

This was introduced in 39bf7b9d0ae5 "libelf: check loops for running
away".  That was part of the XSA-55 patchset.

Looking at the commit message I was concerned that the phdr and shdr
counts might be excessive, and that libelf might get stuck in a loop
with an unreasonably high iteration count.

I appear to have attempted to mitigate this by:

 (a) in the case of shdrs, by using elf_access_ok inside each
    loop, on the principle that an out of control loop count
    would walk off the end of the image and stop;

 (b) in the case of phdrs, making an explicit limit of
    (image size) / sizeof(phdr).  NB that sizeof(phdr) is not the
    actual size of phdrs; for a valid ELF it is a minimum.  This
    is fine because we're computing a backstop, which does not
    have to be entirely accurate.

I now see that (a) is ineffective because the image can specify its
own stride for the iteration (the header size).  If it specifies a
stride of zero, the maximum count is unbounded.

However, both count values are actually 16-bit.  So there is already a
limit.  The extra code in elf_phdr_size is not necessary.

IMO this near-miss demonstrates that a more robust approach is
required to bounding loops in libelf.

A possibility would be to introduce
  bool elf_iter_cont(struct elf_binary *elf, size_t copysz);
and using it in every for(;;) in libelf.  It would keep a counter (set
to zero in libelf) and abandon the processing if the total of all the
copysz values passed was "too large".  (copysz would be bounded below
by 1, so that it is safe to pass a size from the ELF.)

Then

-     for ( i = 0; i < elf_shdr_count(elf); i++ )
+     for ( i = 0; elf_iter_cont(elf, e_shentsize)
+          && i < elf_shdr_count(elf); i++ )

An alternative would be to decorate every loop with a comment
explaining why the loop does reasonably-bounded work.  I'm not sure
whether this, or my more sophisticated suggestion, will find favour.

In any case, the max calculation in elf_phdr_size is unnecessary for
security, and has no purpose related to functionality, and can be
deleted.


Another part of the motivation for a01b6d4 is to produce better
messages for certain malformed ELFs.  There has been no assertion that
any such ELFs (ie ELFs which would fail these new checks) actually
exist or have caused trouble for anyone.

I think that it is a bad idea to add unnecessary checking to libelf.

libelf is a format parser on a security boundary.  Despite attempts to
provide a safe dialect to write in, every piece of code in libelf
risks security problems.

If libelf "accepts" some malformed image, and constructs a mess in the
guest memory space, then that is unfortunate.  But it is not a big
problem.  The guest will probably crash or go wrong, but that is not a
problem for the host.

If someone is faced with fallout from a malformed ELF, they would
hopefully try use an object file inspector like objdump on the loaded
image.  That would reveal the problem.  (And of course ELFs are mostly
generated by a fairly small ecosystem of toolchain programs.)


Also, this has demonstrated that the design principles underlying the
safety of libelf are not properly understood.  IIRC they were
explained in the XSA-55 00/ patch, but that's not sufficient.


So I would prefer to:


Revert all of a01b6d4.


Delete the header count check in elf_phdr_count and replace it with a
compile-time assertion, in elf_phdr_count and in elf_shdr_count, that
sizeof the count field is <= 2.  (This may need a new macro
ELF_HANDLE_FIELD_SIZEOF implemented in terms of
ELF__HANDLE_FIELD_TYPE.)


Add a comment near the top of libelf.h explaining the rules for
programming inside libelf, to include:

  * Arithmetic on signed values is forbidden

  * Use of actual pointers into the ELF is forbidden

  * Division by non-constant values is forbidden

  * All loops must ensure that they have a reasonably low
    iteration count

  * Code (including ELF format sanity checks) which is necessary
    neither for safety nor for correct processing of correct files
    should not be added to libelf.  It is not libelf's role to
    be an ELF validator.


Thanks,
Ian.

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.