libelf: Handle zero size decompressed data.
authorMark Wielaard <mark@klomp.org>
Tue, 19 Feb 2019 15:27:40 +0000 (16:27 +0100)
committerMark Wielaard <mark@klomp.org>
Tue, 19 Feb 2019 15:27:40 +0000 (16:27 +0100)
This is a corner case that will most likely never occur in practice,
but we have several testcases that compress and decompress zero sized
data. In that case during decompression we might malloc a buffer of
size zero. It is allowed for malloc to return NULL in that case. But
we do need a non-NULL buffer to return and set as result. So make sure
to always at least allocate one byte. Also make sure that we don't
allocate a zero sized conversion buffer for the data.

https://sourceware.org/bugzilla/show_bug.cgi?id=24000

Signed-off-by: Mark Wielaard <mark@klomp.org>
libelf/ChangeLog
libelf/elf_compress.c

index d8e8fdc..e4d39d3 100644 (file)
@@ -1,3 +1,13 @@
+2019-02-19  Mark Wielaard  <mark@klomp.org>
+
+       * elf_compress.c (do_deflate_cleanup): Remove ei_data argument,
+       check cdatap is not NULL before calling free.
+       (deflate_cleanup): Add cdata as argument.
+       (__libelf_compress): Also check whether the d_size is not zero
+       before converting data. Call deflate_cleanup with an extra
+       argument depending on whether there is converted data to free.
+       Always allocate allocate at least one byte for buf_out.
+
 2019-02-14  Mark Wielaard  <mark@klomp.org>
 
        * elf_begin.c (read_long_names): Make sure ar_size is properly
index be9eeab..874054a 100644 (file)
 /* Cleanup and return result.  Don't leak memory.  */
 static void *
 do_deflate_cleanup (void *result, z_stream *z, void *out_buf,
-                    int ei_data, Elf_Data *cdatap)
+                    Elf_Data *cdatap)
 {
   deflateEnd (z);
   free (out_buf);
-  if (ei_data != MY_ELFDATA)
+  if (cdatap != NULL)
     free (cdatap->d_buf);
   return result;
 }
 
-#define deflate_cleanup(result) \
-    do_deflate_cleanup(result, &z, out_buf, ei_data, &cdata)
+#define deflate_cleanup(result, cdata) \
+    do_deflate_cleanup(result, &z, out_buf, cdata)
 
 /* Given a section, uses the (in-memory) Elf_Data to extract the
    original data size (including the given header size) and data
@@ -127,7 +127,8 @@ __libelf_compress (Elf_Scn *scn, size_t hsize, int ei_data,
     {
       /* Convert to raw if different endianess.  */
       cdata = *data;
-      if (ei_data != MY_ELFDATA)
+      bool convert = ei_data != MY_ELFDATA && data->d_size > 0;
+      if (convert)
        {
          /* Don't do this conversion in place, we might want to keep
             the original data around, caller decides.  */
@@ -135,10 +136,10 @@ __libelf_compress (Elf_Scn *scn, size_t hsize, int ei_data,
          if (cdata.d_buf == NULL)
            {
              __libelf_seterrno (ELF_E_NOMEM);
-             return deflate_cleanup (NULL);
+             return deflate_cleanup (NULL, NULL);
            }
          if (gelf_xlatetof (scn->elf, &cdata, data, ei_data) == NULL)
-           return deflate_cleanup (NULL);
+           return deflate_cleanup (NULL, &cdata);
        }
 
       z.avail_in = cdata.d_size;
@@ -164,7 +165,7 @@ __libelf_compress (Elf_Scn *scn, size_t hsize, int ei_data,
          if (zrc == Z_STREAM_ERROR)
            {
              __libelf_seterrno (ELF_E_COMPRESS_ERROR);
-             return deflate_cleanup (NULL);
+             return deflate_cleanup (NULL, convert ? &cdata : NULL);
            }
          used += (out_size - used) - z.avail_out;
 
@@ -172,7 +173,7 @@ __libelf_compress (Elf_Scn *scn, size_t hsize, int ei_data,
             compression forced and we are using more compressed data
             than original data.  */
          if (!force && flush == Z_FINISH && used >= *orig_size)
-           return deflate_cleanup ((void *) -1);
+           return deflate_cleanup ((void *) -1, convert ? &cdata : NULL);
 
          if (z.avail_out == 0)
            {
@@ -180,7 +181,7 @@ __libelf_compress (Elf_Scn *scn, size_t hsize, int ei_data,
              if (bigger == NULL)
                {
                  __libelf_seterrno (ELF_E_NOMEM);
-                 return deflate_cleanup (NULL);
+                 return deflate_cleanup (NULL, convert ? &cdata : NULL);
                }
              out_buf = bigger;
              out_size += block;
@@ -188,7 +189,7 @@ __libelf_compress (Elf_Scn *scn, size_t hsize, int ei_data,
        }
       while (z.avail_out == 0); /* Need more output buffer.  */
 
-      if (ei_data != MY_ELFDATA)
+      if (convert)
        {
          free (cdata.d_buf);
          cdata.d_buf = NULL;
@@ -200,7 +201,7 @@ __libelf_compress (Elf_Scn *scn, size_t hsize, int ei_data,
   if (zrc != Z_OK)
     {
       __libelf_seterrno (ELF_E_COMPRESS_ERROR);
-      return deflate_cleanup (NULL);
+      return deflate_cleanup (NULL, NULL);
     }
 
   *new_size = used;
@@ -220,7 +221,11 @@ __libelf_decompress (void *buf_in, size_t size_in, size_t size_out)
       return NULL;
     }
 
-  void *buf_out = malloc (size_out);
+  /* Malloc might return NULL when requestion zero size.  This is highly
+     unlikely, it would only happen when the compression was forced.
+     But we do need a non-NULL buffer to return and set as result.
+     Just make sure to always allocate at least 1 byte.  */
+  void *buf_out = malloc (size_out ?: 1);
   if (unlikely (buf_out == NULL))
     {
       __libelf_seterrno (ELF_E_NOMEM);