References: bsc#1055047

# Commit d0115f96ea633fd6d668f2c067785912c0ad4c00
# Date 2018-02-06 17:29:33 +0100
# Author Jan Beulich <jbeulich@suse.com>
# Committer Jan Beulich <jbeulich@suse.com>
libxc: don't fail domain creation when unpacking initrd fails

At least Linux kernels have been able to work with gzip-ed initrd for
quite some time; initrd compressed with other methods aren't even being
attempted to unpack. Furthermore the unzip-ing routine used here isn't
capable of dealing with various forms of concatenated files, each of
which was gzip-ed separately (it is this particular case which has been
the source of observed VM creation failures).

Hence, if unpacking fails, simply hand the compressed blob to the guest
as is.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Wei Liu <wei.liu2@citrix.com>

--- a/tools/libxc/xc_dom.h
+++ b/tools/libxc/xc_dom.h
@@ -223,7 +223,6 @@ int xc_dom_mem_init(struct xc_dom_image
 int xc_dom_kernel_check_size(struct xc_dom_image *dom, size_t sz);
 int xc_dom_kernel_max_size(struct xc_dom_image *dom, size_t sz);
 
-int xc_dom_ramdisk_check_size(struct xc_dom_image *dom, size_t sz);
 int xc_dom_ramdisk_max_size(struct xc_dom_image *dom, size_t sz);
 
 int xc_dom_devicetree_max_size(struct xc_dom_image *dom, size_t sz);
--- a/tools/libxc/xc_dom_core.c
+++ b/tools/libxc/xc_dom_core.c
@@ -281,22 +281,6 @@ int xc_dom_kernel_check_size(struct xc_d
     return 0;
 }
 
-int xc_dom_ramdisk_check_size(struct xc_dom_image *dom, size_t sz)
-{
-    /* No limit */
-    if ( !dom->max_ramdisk_size )
-        return 0;
-
-    if ( sz > dom->max_ramdisk_size )
-    {
-        xc_dom_panic(dom->xch, XC_INVALID_KERNEL,
-                     "ramdisk image too large");
-        return 1;
-    }
-
-    return 0;
-}
-
 /* ------------------------------------------------------------------------ */
 /* read files, copy memory blocks, with transparent gunzip                  */
 
@@ -956,16 +940,27 @@ int xc_dom_build_image(struct xc_dom_ima
         void *ramdiskmap;
 
         if ( !dom->ramdisk_seg.vstart )
-        {
             unziplen = xc_dom_check_gzip(dom->xch,
                                          dom->ramdisk_blob, dom->ramdisk_size);
-            if ( xc_dom_ramdisk_check_size(dom, unziplen) != 0 )
-                unziplen = 0;
-        }
         else
             unziplen = 0;
 
-        ramdisklen = unziplen ? unziplen : dom->ramdisk_size;
+        ramdisklen = max(unziplen, dom->ramdisk_size);
+        if ( dom->max_ramdisk_size )
+        {
+            if ( unziplen && ramdisklen > dom->max_ramdisk_size )
+            {
+                ramdisklen = min(unziplen, dom->ramdisk_size);
+                if ( unziplen > ramdisklen )
+                    unziplen = 0;
+            }
+            if ( ramdisklen > dom->max_ramdisk_size )
+            {
+                xc_dom_panic(dom->xch, XC_INVALID_KERNEL,
+                             "ramdisk image too large");
+                goto err;
+            }
+        }
 
         if ( xc_dom_alloc_segment(dom, &dom->ramdisk_seg, "ramdisk",
                                   dom->ramdisk_seg.vstart,
@@ -982,13 +977,21 @@ int xc_dom_build_image(struct xc_dom_ima
         {
             if ( xc_dom_do_gunzip(dom->xch,
                                   dom->ramdisk_blob, dom->ramdisk_size,
-                                  ramdiskmap, ramdisklen) == -1 )
+                                  ramdiskmap, unziplen) != -1 )
+                goto devtree;
+            if ( dom->ramdisk_size > ramdisklen )
                 goto err;
         }
-        else
-            memcpy(ramdiskmap, dom->ramdisk_blob, dom->ramdisk_size);
+
+        /* Fall back to handing over the raw blob. */
+        memcpy(ramdiskmap, dom->ramdisk_blob, dom->ramdisk_size);
+        /* If an unzip attempt was made, the buffer may no longer be all zero. */
+        if ( unziplen > dom->ramdisk_size )
+            memset(ramdiskmap + dom->ramdisk_size, 0,
+                   unziplen - dom->ramdisk_size);
     }
 
+ devtree:
     /* load devicetree */
     if ( dom->devicetree_blob )
     {
--- a/tools/libxc/xc_private.h
+++ b/tools/libxc/xc_private.h
@@ -328,6 +328,22 @@ int xc_ffs16(uint16_t x);
 int xc_ffs32(uint32_t x);
 int xc_ffs64(uint64_t x);
 
+#define min(X, Y) ({                             \
+            const typeof (X) _x = (X);           \
+            const typeof (Y) _y = (Y);           \
+            (void) (&_x == &_y);                 \
+            (_x < _y) ? _x : _y; })
+#define max(X, Y) ({                             \
+            const typeof (X) _x = (X);           \
+            const typeof (Y) _y = (Y);           \
+            (void) (&_x == &_y);                 \
+            (_x > _y) ? _x : _y; })
+
+#define min_t(type,x,y) \
+        ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
+#define max_t(type,x,y) \
+        ({ type __x = (x); type __y = (y); __x > __y ? __x: __y; })
+
 #define DOMPRINTF(fmt, args...) xc_dom_printf(dom->xch, fmt, ## args)
 #define DOMPRINTF_CALLED(xch) xc_dom_printf((xch), "%s: called", __FUNCTION__)
 
--- a/tools/libxc/xc_dom_decompress_unsafe_xz.c
+++ b/tools/libxc/xc_dom_decompress_unsafe_xz.c
@@ -34,17 +34,6 @@ static inline u32 le32_to_cpup(const u32
 	return cpu_to_le32(*p);
 }
 
-#define min(x,y) ({ \
-        const typeof(x) _x = (x);       \
-        const typeof(y) _y = (y);       \
-        (void) (&_x == &_y);            \
-        _x < _y ? _x : _y; })
-
-#define min_t(type,x,y) \
-        ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
-#define max_t(type,x,y) \
-        ({ type __x = (x); type __y = (y); __x > __y ? __x: __y; })
-
 #define __force
 #define always_inline
 
