250 lines
7.7 KiB
Text
250 lines
7.7 KiB
Text
$NetBSD: patch-CVE-2011-1583,v 1.1 2011/05/12 15:57:38 bouyer Exp $
|
|
|
|
from:
|
|
http://xenbits.xensource.com/hg/staging/xen-4.1-testing.hg/rev/e2e575f8b5d9
|
|
|
|
# HG changeset patch
|
|
# User Ian Jackson <ian.jackson@eu.citrix.com>
|
|
# Date 1304949841 -3600
|
|
# Node ID e2e575f8b5d961db23ea8bb7b3820be8621789b3
|
|
# Parent bdc6dd89d83c2fcd87b069557b6f8867ab95dda1
|
|
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>
|
|
|
|
diff -r bdc6dd89d83c -r e2e575f8b5d9 libxc/xc_dom_bzimageloader.c
|
|
--- libxc/xc_dom_bzimageloader.c Mon May 09 12:18:50 2011 +0100
|
|
+++ libxc/xc_dom_bzimageloader.c Mon May 09 15:04:01 2011 +0100
|
|
@@ -82,8 +82,29 @@
|
|
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 @@
|
|
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 @@
|
|
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 @@
|
|
}
|
|
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 @@
|
|
|
|
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 @@
|
|
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 @@
|
|
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 @@
|
|
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 @@
|
|
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 )
|
|
|