References: bnc#823011 XSA-55

From: Ian Jackson <ian.jackson@eu.citrix.com>
Date: Thu, 13 Jun 2013 19:10:15 +0100
Subject: [PATCH 19/21] libxc: check return values from malloc

A sufficiently malformed input to libxc (such as a malformed input ELF
or other guest-controlled data) might cause one of libxc's malloc() to
fail.  In this case we need to make sure we don't dereference or do
pointer arithmetic on the result.

Search for all occurrences of \b(m|c|re)alloc in libxc, and all
functions which call them, and add appropriate error checking where
missing.

This includes the functions xc_dom_malloc*, which now print a message
when they fail so that callers don't have to do so.

The function xc_cpuid_to_str wasn't provided with a sane return value
and has a pretty strange API, which now becomes a little stranger.
There are no in-tree callers.

Changes in the Xen 4.2 version of this series:
* No need to fix code relating to ARM.
* No need to fix code relating to superpage support.
* Additionally fix `dom->p2m_host = xc_dom_malloc...' in xc_dom_ia64.c.

Changes in the Xen 4.1 version of this series:
* An additional check is needed in xc_flask.c:xc_flask_access.

This is part of the fix to a security issue, XSA-55.

Signed-off-by: Ian Jackson <ian.jackson@eu.citrix.com>

--- a/tools/libxc/xc_cpuid_x86.c
+++ b/tools/libxc/xc_cpuid_x86.c
@@ -398,6 +398,8 @@ static int xc_cpuid_do_domctl(
 static char *alloc_str(void)
 {
     char *s = malloc(33);
+    if ( s == NULL )
+        return s;
     memset(s, 0, 33);
     return s;
 }
@@ -409,6 +411,8 @@ void xc_cpuid_to_str(const unsigned int 
     for ( i = 0; i < 4; i++ )
     {
         strs[i] = alloc_str();
+        if ( strs[i] == NULL )
+            continue;
         for ( j = 0; j < 32; j++ )
             strs[i][j] = !!((regs[i] & (1U << (31 - j)))) ? '1' : '0';
     }
@@ -475,7 +479,7 @@ int xc_cpuid_check(
     const char **config,
     char **config_transformed)
 {
-    int i, j;
+    int i, j, rc;
     unsigned int regs[4];
 
     memset(config_transformed, 0, 4 * sizeof(*config_transformed));
@@ -487,6 +491,11 @@ int xc_cpuid_check(
         if ( config[i] == NULL )
             continue;
         config_transformed[i] = alloc_str();
+        if ( config_transformed[i] == NULL )
+        {
+            rc = -ENOMEM;
+            goto fail_rc;
+        }
         for ( j = 0; j < 32; j++ )
         {
             unsigned char val = !!((regs[i] & (1U << (31 - j))));
@@ -503,12 +512,14 @@ int xc_cpuid_check(
     return 0;
 
  fail:
+    rc = -EPERM;
+ fail_rc:
     for ( i = 0; i < 4; i++ )
     {
         free(config_transformed[i]);
         config_transformed[i] = NULL;
     }
-    return -EPERM;
+    return rc;
 }
 
 /*
@@ -553,6 +564,11 @@ int xc_cpuid_set(
         }
         
         config_transformed[i] = alloc_str();
+        if ( config_transformed[i] == NULL )
+        {
+            rc = -ENOMEM;
+            goto fail;
+        }
 
         for ( j = 0; j < 32; j++ )
         {
--- a/tools/libxc/xc_dom_core.c
+++ b/tools/libxc/xc_dom_core.c
@@ -100,9 +100,17 @@ void *xc_dom_malloc(struct xc_dom_image 
 {
     struct xc_dom_mem *block;
 
+    if ( size > SIZE_MAX - sizeof(*block) )
+    {
+        xc_dom_printf("%s: unreasonable allocation size\n", __FUNCTION__);
+        return NULL;
+    }
     block = malloc(sizeof(*block) + size);
     if ( block == NULL )
+    {
+        xc_dom_printf("%s: allocation failed\n", __FUNCTION__);
         return NULL;
+    }
     memset(block, 0, sizeof(*block) + size);
     block->next = dom->memblocks;
     dom->memblocks = block;
@@ -118,7 +126,10 @@ void *xc_dom_malloc_page_aligned(struct 
 
     block = malloc(sizeof(*block));
     if ( block == NULL )
+    {
+        xc_dom_printf("%s: allocation failed\n", __FUNCTION__);
         return NULL;
+    }
     memset(block, 0, sizeof(*block));
     block->mmap_len = size;
     block->mmap_ptr = mmap(NULL, block->mmap_len,
@@ -126,6 +137,7 @@ void *xc_dom_malloc_page_aligned(struct 
                            -1, 0);
     if ( block->mmap_ptr == MAP_FAILED )
     {
+        xc_dom_printf("%s: mmap failed\n", __FUNCTION__);
         free(block);
         return NULL;
     }
@@ -182,6 +194,7 @@ void *xc_dom_malloc_filemap(struct xc_do
         close(fd);
     if ( block != NULL )
         free(block);
+    xc_dom_printf("%s: failed (on file `%s')\n", __FUNCTION__, filename);
     return NULL;
 }
 
--- a/tools/libxc/xc_dom_elfloader.c
+++ b/tools/libxc/xc_dom_elfloader.c
@@ -295,6 +295,8 @@ static elf_errorstatus xc_dom_parse_elf_
         return rc;
 
     elf = xc_dom_malloc(dom, sizeof(*elf));
+    if ( elf == NULL )
+        return -1;
     dom->private_loader = elf;
     rc = elf_init(elf, dom->kernel_blob, dom->kernel_size);
     if ( xc_dom_logfile )
--- a/tools/libxc/xc_dom_ia64.c
+++ b/tools/libxc/xc_dom_ia64.c
@@ -174,6 +174,12 @@ int arch_setup_meminit(struct xc_dom_ima
 
     /* setup initial p2m */
     dom->p2m_host = xc_dom_malloc(dom, sizeof(xen_pfn_t) * nbr);
+    if ( dom->p2m_host == NULL )
+    {
+        xc_dom_printf("%s: xc_dom_malloc failed for p2m_host\n",
+                      __FUNCTION__);
+        return -1;
+    }
     for ( pfn = 0; pfn < nbr; pfn++ )
         dom->p2m_host[pfn] = start + pfn;
 
--- a/tools/libxc/xc_dom_x86.c
+++ b/tools/libxc/xc_dom_x86.c
@@ -777,6 +777,9 @@ int arch_setup_meminit(struct xc_dom_ima
     }
 
     dom->p2m_host = xc_dom_malloc(dom, sizeof(xen_pfn_t) * dom->total_pages);
+    if ( dom->p2m_host == NULL )
+        return -EINVAL;
+
     if ( dom->superpages )
     {
         int count = dom->total_pages >> SUPERPAGE_PFN_SHIFT;
--- a/tools/libxc/xc_domain_restore.c
+++ b/tools/libxc/xc_domain_restore.c
@@ -874,6 +874,11 @@ static int apply_batch(int xc_handle, ui
 
     /* Map relevant mfns */
     pfn_err = calloc(j, sizeof(*pfn_err));
+    if ( pfn_err == NULL )
+    {
+        PERROR("allocation for pfn_err failed");
+        return -1;
+    }
     region_base = xc_map_foreign_bulk(
         xc_handle, dom, PROT_WRITE, region_mfn, pfn_err, j);
 
--- a/tools/libxc/xc_linux.c
+++ b/tools/libxc/xc_linux.c
@@ -272,6 +272,8 @@ void *xc_map_foreign_range(int xc_handle
 
     num = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
     arr = calloc(num, sizeof(xen_pfn_t));
+    if ( arr == NULL )
+        return NULL;
 
     for ( i = 0; i < num; i++ )
         arr[i] = mfn + i;
@@ -295,6 +297,8 @@ void *xc_map_foreign_ranges(int xc_handl
     num_per_entry = chunksize >> PAGE_SHIFT;
     num = num_per_entry * nentries;
     arr = calloc(num, sizeof(xen_pfn_t));
+    if ( arr == NULL )
+        return NULL;
 
     for ( i = 0; i < nentries; i++ )
         for ( j = 0; j < num_per_entry; j++ )
--- a/tools/libxc/xc_private.c
+++ b/tools/libxc/xc_private.c
@@ -63,12 +63,20 @@ _xc_get_last_error(void)
 
 const xc_error *xc_get_last_error(void)
 {
-    return _xc_get_last_error();
+    static const xc_error last_error_dummy = {
+        .code = XC_OUT_OF_MEMORY,
+        .message = "Not recorded"
+    };
+
+    return _xc_get_last_error() ?: &last_error_dummy;
 }
 
 void xc_clear_last_error(void)
 {
     xc_error *last_error = _xc_get_last_error();
+
+    if (!last_error)
+        return;
     last_error->code = XC_ERROR_NONE;
     last_error->message[0] = '\0';
 }
@@ -103,6 +111,9 @@ xc_error_handler xc_set_error_handler(xc
 static void _xc_set_error(int code, const char *msg)
 {
     xc_error *last_error = _xc_get_last_error();
+
+    if (!last_error)
+        return;
     last_error->code = code;
     strncpy(last_error->message, msg, XC_MAX_ERROR_MSG_LEN - 1);
     last_error->message[XC_MAX_ERROR_MSG_LEN-1] = '\0';
@@ -125,7 +136,8 @@ void xc_set_error(int code, const char *
 
     if ( error_handler != NULL ) {
         xc_error *last_error = _xc_get_last_error();
-        error_handler(last_error);
+        if (last_error)
+            error_handler(last_error);
     }
 }
 
@@ -642,6 +654,8 @@ char *safe_strerror(int errcode)
     errbuf = pthread_getspecific(errbuf_pkey);
     if (errbuf == NULL) {
         errbuf = malloc(XS_BUFSIZE);
+        if ( errbuf == NULL )
+            return "(failed to allocate errbuf)";
         pthread_setspecific(errbuf_pkey, errbuf);
     }
 
--- a/tools/libxc/xenctrl.h
+++ b/tools/libxc/xenctrl.h
@@ -1199,7 +1199,7 @@ int xc_cpuid_set(int xc,
 int xc_cpuid_apply_policy(int xc,
                           domid_t domid);
 void xc_cpuid_to_str(const unsigned int *regs,
-                     char **strs);
+                     char **strs); /* some strs[] may be NULL if ENOMEM */
 int xc_mca_op(int xc_handle, struct xen_mc *mc);
 #endif
 
