aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIan Jackson <ian.jackson@eu.citrix.com>2011-05-09 15:04:01 +0100
committerIan Jackson <ian.jackson@eu.citrix.com>2011-05-09 15:04:01 +0100
commit0416d74e9e31b6e8b5b2ef4d4b840bfe8ab2e72e (patch)
tree34527f54846abe3817a2071b244263d4724aa461
parent0ad69b1cd0ccbe4e26484cf9641b6d784bb5856a (diff)
downloadxen-0416d74e9e31b6e8b5b2ef4d4b840bfe8ab2e72e.tar.gz
xen-0416d74e9e31b6e8b5b2ef4d4b840bfe8ab2e72e.tar.bz2
xen-0416d74e9e31b6e8b5b2ef4d4b840bfe8ab2e72e.zip
libxc: [CVE-2011-1583] pv kernel image validation
The functions which interpret the kernel image supplied for a paravirtualised guest, and decompress it into memory when booting the domain, are incautious. Specifically: (i) Integer overflow in the decompression loop memory allocator might result in overrunning the buffer used for the decompressed image; (ii) Integer overflows and lack of checking of certain length fields can result in the loader reading its own address space beyond the size of the supplied kernel image file. (iii) Lack of error checking in the decompression loop can lead to an infinite loop. This patch fixes these problems. CVE-2011-1583. Signed-off-by: Ian Campbell <Ian.Campbell@eu.citrix.com> Signed-off-by: Ian Jackson <ian.jackson@eu.citrix.com> Committed-by: Ian Jackson <ian.jackson@eu.citrix.com>
-rw-r--r--tools/libxc/xc_dom_bzimageloader.c137
1 files changed, 94 insertions, 43 deletions
diff --git a/tools/libxc/xc_dom_bzimageloader.c b/tools/libxc/xc_dom_bzimageloader.c
index d14acbdff7..9852e67657 100644
--- a/tools/libxc/xc_dom_bzimageloader.c
+++ b/tools/libxc/xc_dom_bzimageloader.c
@@ -82,8 +82,29 @@ static int xc_try_bzip2_decode(
for ( ; ; )
{
ret = BZ2_bzDecompress(&stream);
- if ( (stream.avail_out == 0) || (ret != BZ_OK) )
+ if ( ret == BZ_STREAM_END )
{
+ DOMPRINTF("BZIP2: Saw data stream end");
+ retval = 0;
+ break;
+ }
+ if ( ret != BZ_OK )
+ {
+ DOMPRINTF("BZIP2: error %d", ret);
+ free(out_buf);
+ goto bzip2_cleanup;
+ }
+
+ if ( stream.avail_out == 0 )
+ {
+ /* Protect against output buffer overflow */
+ if ( outsize > INT_MAX / 2 )
+ {
+ DOMPRINTF("BZIP2: output buffer overflow");
+ free(out_buf);
+ goto bzip2_cleanup;
+ }
+
tmp_buf = realloc(out_buf, outsize * 2);
if ( tmp_buf == NULL )
{
@@ -97,16 +118,18 @@ static int xc_try_bzip2_decode(
stream.avail_out = (outsize * 2) - outsize;
outsize *= 2;
}
-
- if ( ret != BZ_OK )
+ else if ( stream.avail_in == 0 )
{
- if ( ret == BZ_STREAM_END )
- {
- DOMPRINTF("BZIP2: Saw data stream end");
- retval = 0;
- break;
- }
- DOMPRINTF("BZIP2: error");
+ /*
+ * If there is output buffer available then this indicates
+ * that BZ2_bzDecompress would like more input data to be
+ * provided. However our complete input buffer is in
+ * memory and provided upfront so if avail_in is zero this
+ * actually indicates a truncated input.
+ */
+ DOMPRINTF("BZIP2: not enough input");
+ free(out_buf);
+ goto bzip2_cleanup;
}
}
@@ -180,31 +203,14 @@ static int xc_try_lzma_decode(
for ( ; ; )
{
ret = lzma_code(&stream, action);
- if ( (stream.avail_out == 0) || (ret != LZMA_OK) )
+ if ( ret == LZMA_STREAM_END )
{
- tmp_buf = realloc(out_buf, outsize * 2);
- if ( tmp_buf == NULL )
- {
- DOMPRINTF("LZMA: Failed to realloc memory");
- free(out_buf);
- goto lzma_cleanup;
- }
- out_buf = tmp_buf;
-
- stream.next_out = out_buf + outsize;
- stream.avail_out = (outsize * 2) - outsize;
- outsize *= 2;
+ DOMPRINTF("LZMA: Saw data stream end");
+ retval = 0;
+ break;
}
-
if ( ret != LZMA_OK )
{
- if ( ret == LZMA_STREAM_END )
- {
- DOMPRINTF("LZMA: Saw data stream end");
- retval = 0;
- break;
- }
-
switch ( ret )
{
case LZMA_MEM_ERROR:
@@ -238,7 +244,32 @@ static int xc_try_lzma_decode(
}
DOMPRINTF("%s: LZMA decompression error %s",
__FUNCTION__, msg);
- break;
+ free(out_buf);
+ goto lzma_cleanup;
+ }
+
+ if ( stream.avail_out == 0 )
+ {
+ /* Protect against output buffer overflow */
+ if ( outsize > INT_MAX / 2 )
+ {
+ DOMPRINTF("LZMA: output buffer overflow");
+ free(out_buf);
+ goto lzma_cleanup;
+ }
+
+ tmp_buf = realloc(out_buf, outsize * 2);
+ if ( tmp_buf == NULL )
+ {
+ DOMPRINTF("LZMA: Failed to realloc memory");
+ free(out_buf);
+ goto lzma_cleanup;
+ }
+ out_buf = tmp_buf;
+
+ stream.next_out = out_buf + outsize;
+ stream.avail_out = (outsize * 2) - outsize;
+ outsize *= 2;
}
}
@@ -489,18 +520,18 @@ struct setup_header {
extern struct xc_dom_loader elf_loader;
-static unsigned int payload_offset(struct setup_header *hdr)
+static int check_magic(struct xc_dom_image *dom, const void *magic, size_t len)
{
- unsigned int off;
+ if (len > dom->kernel_size)
+ return 0;
- off = (hdr->setup_sects + 1) * 512;
- off += hdr->payload_offset;
- return off;
+ return (memcmp(dom->kernel_blob, magic, len) == 0);
}
static int xc_dom_probe_bzimage_kernel(struct xc_dom_image *dom)
{
struct setup_header *hdr;
+ uint64_t payload_offset, payload_length;
int ret;
if ( dom->kernel_blob == NULL )
@@ -533,10 +564,30 @@ static int xc_dom_probe_bzimage_kernel(struct xc_dom_image *dom)
return -EINVAL;
}
- dom->kernel_blob = dom->kernel_blob + payload_offset(hdr);
- dom->kernel_size = hdr->payload_length;
- if ( memcmp(dom->kernel_blob, "\037\213", 2) == 0 )
+ /* upcast to 64 bits to avoid overflow */
+ /* setup_sects is u8 and so cannot overflow */
+ payload_offset = (hdr->setup_sects + 1) * 512;
+ payload_offset += hdr->payload_offset;
+ payload_length = hdr->payload_length;
+
+ if ( payload_offset >= dom->kernel_size )
+ {
+ xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s: payload offset overflow",
+ __FUNCTION__);
+ return -EINVAL;
+ }
+ if ( (payload_offset + payload_length) > dom->kernel_size )
+ {
+ xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s: payload length overflow",
+ __FUNCTION__);
+ return -EINVAL;
+ }
+
+ dom->kernel_blob = dom->kernel_blob + payload_offset;
+ dom->kernel_size = payload_length;
+
+ if ( check_magic(dom, "\037\213", 2) )
{
ret = xc_dom_try_gunzip(dom, &dom->kernel_blob, &dom->kernel_size);
if ( ret == -1 )
@@ -546,7 +597,7 @@ static int xc_dom_probe_bzimage_kernel(struct xc_dom_image *dom)
return -EINVAL;
}
}
- else if ( memcmp(dom->kernel_blob, "\102\132\150", 3) == 0 )
+ else if ( check_magic(dom, "\102\132\150", 3) )
{
ret = xc_try_bzip2_decode(dom, &dom->kernel_blob, &dom->kernel_size);
if ( ret < 0 )
@@ -557,7 +608,7 @@ static int xc_dom_probe_bzimage_kernel(struct xc_dom_image *dom)
return -EINVAL;
}
}
- else if ( memcmp(dom->kernel_blob, "\135\000", 2) == 0 )
+ else if ( check_magic(dom, "\135\000", 2) )
{
ret = xc_try_lzma_decode(dom, &dom->kernel_blob, &dom->kernel_size);
if ( ret < 0 )
@@ -568,7 +619,7 @@ static int xc_dom_probe_bzimage_kernel(struct xc_dom_image *dom)
return -EINVAL;
}
}
- else if ( memcmp(dom->kernel_blob, "\x89LZO", 5) == 0 )
+ else if ( check_magic(dom, "\x89LZO", 5) )
{
ret = xc_try_lzo1x_decode(dom, &dom->kernel_blob, &dom->kernel_size);
if ( ret < 0 )