From f9ea792edccc5832eb92917ba87c46eccca42a0d Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Thu, 25 Oct 2012 17:04:37 +0100 Subject: xl: Do not leak events when a domain exits. The goto in both of these places misses the event free which would normally clean up. ==8655== 80 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==8655== at 0x4024370: calloc (vg_replace_malloc.c:593) ==8655== by 0x406EAAE: libxl__zalloc (libxl_internal.c:83) ==8655== by 0x4078173: libxl__event_new (libxl_event.c:1167) ==8655== by 0x4056373: domain_death_occurred (libxl.c:958) ==8655== by 0x4058D06: domain_death_xswatch_callback (libxl.c:1038) ==8655== by 0x4078EB5: watchfd_callback (libxl_event.c:458) ==8655== by 0x407839E: afterpoll_internal (libxl_event.c:949) ==8655== by 0x4079142: eventloop_iteration (libxl_event.c:1371) ==8655== by 0x40799BB: libxl_event_wait (libxl_event.c:1396) ==8655== by 0x805CC67: create_domain (xl_cmdimpl.c:1698) ==8655== by 0x805E001: main_create (xl_cmdimpl.c:3986) ==8655== by 0x804D43D: main (xl.c:285) Signed-off-by: Ian Campbell Acked-by: Ian Jackson Committed-by: Ian Jackson --- stubdom/grub/kexec.c | 4 ++ tools/libxc/xc_dom.h | 13 ++++++- tools/libxc/xc_dom_bzimageloader.c | 47 +++++++++++++++++++++-- tools/libxc/xc_dom_core.c | 77 ++++++++++++++++++++++++++++++++++++-- tools/libxl/xl_cmdimpl.c | 2 + 5 files changed, 134 insertions(+), 9 deletions(-) diff --git a/stubdom/grub/kexec.c b/stubdom/grub/kexec.c index 06bef52ac2..b21c91ae99 100644 --- a/stubdom/grub/kexec.c +++ b/stubdom/grub/kexec.c @@ -137,6 +137,10 @@ void kexec(void *kernel, long kernel_size, void *module, long module_size, char dom = xc_dom_allocate(xc_handle, cmdline, features); dom->allocate = kexec_allocate; + /* We are using guest owned memory, therefore no limits. */ + xc_dom_kernel_max_size(dom, 0); + xc_dom_ramdisk_max_size(dom, 0); + dom->kernel_blob = kernel; dom->kernel_size = kernel_size; diff --git a/tools/libxc/xc_dom.h b/tools/libxc/xc_dom.h index 3cd6dae6d6..abafb15a13 100644 --- a/tools/libxc/xc_dom.h +++ b/tools/libxc/xc_dom.h @@ -55,6 +55,9 @@ struct xc_dom_image { void *ramdisk_blob; size_t ramdisk_size; + size_t max_kernel_size; + size_t max_ramdisk_size; + /* arguments and parameters */ char *cmdline; uint32_t f_requested[XENFEAT_NR_SUBMAPS]; @@ -194,6 +197,13 @@ void xc_dom_release_phys(struct xc_dom_image *dom); void xc_dom_release(struct xc_dom_image *dom); int xc_dom_mem_init(struct xc_dom_image *dom, unsigned int mem_mb); +#define XC_DOM_DECOMPRESS_MAX (1024*1024*1024) /* 1GB */ +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); + size_t xc_dom_check_gzip(xc_interface *xch, void *blob, size_t ziplen); int xc_dom_do_gunzip(xc_interface *xch, @@ -254,7 +264,8 @@ void xc_dom_log_memory_footprint(struct xc_dom_image *dom); void *xc_dom_malloc(struct xc_dom_image *dom, size_t size); void *xc_dom_malloc_page_aligned(struct xc_dom_image *dom, size_t size); void *xc_dom_malloc_filemap(struct xc_dom_image *dom, - const char *filename, size_t * size); + const char *filename, size_t * size, + const size_t max_size); char *xc_dom_strdup(struct xc_dom_image *dom, const char *str); /* --- alloc memory pool ------------------------------------------- */ diff --git a/tools/libxc/xc_dom_bzimageloader.c b/tools/libxc/xc_dom_bzimageloader.c index 113d40ff2f..2b3602fcde 100644 --- a/tools/libxc/xc_dom_bzimageloader.c +++ b/tools/libxc/xc_dom_bzimageloader.c @@ -47,7 +47,7 @@ static int xc_try_bzip2_decode( char *out_buf; char *tmp_buf; int retval = -1; - int outsize; + unsigned int outsize; uint64_t total; stream.bzalloc = NULL; @@ -79,6 +79,17 @@ static int xc_try_bzip2_decode( stream.next_out = out_buf; stream.avail_out = dom->kernel_size; + /* + * stream.avail_in is an unsigned int, while kernel_size is a + * size_t. Check we aren't overflowing. + */ + if ( stream.avail_in != dom->kernel_size ) + { + DOMPRINTF("BZIP2: Input too large"); + free(out_buf); + goto bzip2_cleanup; + } + for ( ; ; ) { ret = BZ2_bzDecompress(&stream); @@ -98,13 +109,20 @@ static int xc_try_bzip2_decode( if ( stream.avail_out == 0 ) { /* Protect against output buffer overflow */ - if ( outsize > INT_MAX / 2 ) + if ( outsize > UINT_MAX / 2 ) { DOMPRINTF("BZIP2: output buffer overflow"); free(out_buf); goto bzip2_cleanup; } + if ( xc_dom_kernel_check_size(dom, outsize * 2) ) + { + DOMPRINTF("BZIP2: output too large"); + free(out_buf); + goto bzip2_cleanup; + } + tmp_buf = realloc(out_buf, outsize * 2); if ( tmp_buf == NULL ) { @@ -172,7 +190,7 @@ static int _xc_try_lzma_decode( unsigned char *out_buf; unsigned char *tmp_buf; int retval = -1; - int outsize; + size_t outsize; const char *msg; /* sigh. We don't know up-front how much memory we are going to need @@ -244,13 +262,20 @@ static int _xc_try_lzma_decode( if ( stream->avail_out == 0 ) { /* Protect against output buffer overflow */ - if ( outsize > INT_MAX / 2 ) + if ( outsize > SIZE_MAX / 2 ) { DOMPRINTF("%s: output buffer overflow", what); free(out_buf); goto lzma_cleanup; } + if ( xc_dom_kernel_check_size(dom, outsize * 2) ) + { + DOMPRINTF("%s: output too large", what); + free(out_buf); + goto lzma_cleanup; + } + tmp_buf = realloc(out_buf, outsize * 2); if ( tmp_buf == NULL ) { @@ -359,6 +384,12 @@ static int xc_try_lzo1x_decode( 0x89, 0x4c, 0x5a, 0x4f, 0x00, 0x0d, 0x0a, 0x1a, 0x0a }; + /* + * lzo_uint should match size_t. Check that this is the case to be + * sure we won't overflow various lzo_uint fields. + */ + XC_BUILD_BUG_ON(sizeof(lzo_uint) != sizeof(size_t)); + ret = lzo_init(); if ( ret != LZO_E_OK ) { @@ -438,6 +469,14 @@ static int xc_try_lzo1x_decode( if ( src_len <= 0 || src_len > dst_len || src_len > left ) break; + msg = "Output buffer overflow"; + if ( *size > SIZE_MAX - dst_len ) + break; + + msg = "Decompressed image too large"; + if ( xc_dom_kernel_check_size(dom, *size + dst_len) ) + break; + msg = "Failed to (re)alloc memory"; tmp_buf = realloc(out_buf, *size + dst_len); if ( tmp_buf == NULL ) diff --git a/tools/libxc/xc_dom_core.c b/tools/libxc/xc_dom_core.c index 5244b04c4f..2b4c6b76b7 100644 --- a/tools/libxc/xc_dom_core.c +++ b/tools/libxc/xc_dom_core.c @@ -159,7 +159,8 @@ void *xc_dom_malloc_page_aligned(struct xc_dom_image *dom, size_t size) } void *xc_dom_malloc_filemap(struct xc_dom_image *dom, - const char *filename, size_t * size) + const char *filename, size_t * size, + const size_t max_size) { struct xc_dom_mem *block = NULL; int fd = -1; @@ -171,6 +172,13 @@ void *xc_dom_malloc_filemap(struct xc_dom_image *dom, lseek(fd, 0, SEEK_SET); *size = lseek(fd, 0, SEEK_END); + if ( max_size && *size > max_size ) + { + xc_dom_panic(dom->xch, XC_OUT_OF_MEMORY, + "tried to map file which is too large"); + goto err; + } + block = malloc(sizeof(*block)); if ( block == NULL ) goto err; @@ -221,6 +229,40 @@ char *xc_dom_strdup(struct xc_dom_image *dom, const char *str) return nstr; } +/* ------------------------------------------------------------------------ */ +/* decompression buffer sizing */ +int xc_dom_kernel_check_size(struct xc_dom_image *dom, size_t sz) +{ + /* No limit */ + if ( !dom->max_kernel_size ) + return 0; + + if ( sz > dom->max_kernel_size ) + { + xc_dom_panic(dom->xch, XC_INVALID_KERNEL, + "kernel image too large"); + return 1; + } + + 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 */ @@ -235,7 +277,7 @@ size_t xc_dom_check_gzip(xc_interface *xch, void *blob, size_t ziplen) gzlen = blob + ziplen - 4; unziplen = gzlen[3] << 24 | gzlen[2] << 16 | gzlen[1] << 8 | gzlen[0]; - if ( (unziplen < 0) || (unziplen > (1024*1024*1024)) ) /* 1GB limit */ + if ( (unziplen < 0) || (unziplen > XC_DOM_DECOMPRESS_MAX) ) { xc_dom_printf (xch, @@ -288,6 +330,9 @@ int xc_dom_try_gunzip(struct xc_dom_image *dom, void **blob, size_t * size) if ( unziplen == 0 ) return 0; + if ( xc_dom_kernel_check_size(dom, unziplen) ) + return 0; + unzip = xc_dom_malloc(dom, unziplen); if ( unzip == NULL ) return -1; @@ -590,6 +635,9 @@ struct xc_dom_image *xc_dom_allocate(xc_interface *xch, memset(dom, 0, sizeof(*dom)); dom->xch = xch; + dom->max_kernel_size = XC_DOM_DECOMPRESS_MAX; + dom->max_ramdisk_size = XC_DOM_DECOMPRESS_MAX; + if ( cmdline ) dom->cmdline = xc_dom_strdup(dom, cmdline); if ( features ) @@ -610,10 +658,25 @@ struct xc_dom_image *xc_dom_allocate(xc_interface *xch, return NULL; } +int xc_dom_kernel_max_size(struct xc_dom_image *dom, size_t sz) +{ + DOMPRINTF("%s: kernel_max_size=%zx", __FUNCTION__, sz); + dom->max_kernel_size = sz; + return 0; +} + +int xc_dom_ramdisk_max_size(struct xc_dom_image *dom, size_t sz) +{ + DOMPRINTF("%s: ramdisk_max_size=%zx", __FUNCTION__, sz); + dom->max_ramdisk_size = sz; + return 0; +} + int xc_dom_kernel_file(struct xc_dom_image *dom, const char *filename) { DOMPRINTF("%s: filename=\"%s\"", __FUNCTION__, filename); - dom->kernel_blob = xc_dom_malloc_filemap(dom, filename, &dom->kernel_size); + dom->kernel_blob = xc_dom_malloc_filemap(dom, filename, &dom->kernel_size, + dom->max_kernel_size); if ( dom->kernel_blob == NULL ) return -1; return xc_dom_try_gunzip(dom, &dom->kernel_blob, &dom->kernel_size); @@ -622,8 +685,10 @@ int xc_dom_kernel_file(struct xc_dom_image *dom, const char *filename) int xc_dom_ramdisk_file(struct xc_dom_image *dom, const char *filename) { DOMPRINTF("%s: filename=\"%s\"", __FUNCTION__, filename); + /* We do not enforce any particular size limit here */ dom->ramdisk_blob = - xc_dom_malloc_filemap(dom, filename, &dom->ramdisk_size); + xc_dom_malloc_filemap(dom, filename, &dom->ramdisk_size, 0); + if ( dom->ramdisk_blob == NULL ) return -1; // return xc_dom_try_gunzip(dom, &dom->ramdisk_blob, &dom->ramdisk_size); @@ -783,7 +848,11 @@ int xc_dom_build_image(struct xc_dom_image *dom) void *ramdiskmap; unziplen = xc_dom_check_gzip(dom->xch, dom->ramdisk_blob, dom->ramdisk_size); + if ( xc_dom_ramdisk_check_size(dom, unziplen) != 0 ) + unziplen = 0; + ramdisklen = unziplen ? unziplen : dom->ramdisk_size; + if ( xc_dom_alloc_segment(dom, &dom->ramdisk_seg, "ramdisk", 0, ramdisklen) != 0 ) goto err; diff --git a/tools/libxl/xl_cmdimpl.c b/tools/libxl/xl_cmdimpl.c index 5a947da409..a43b3712f6 100644 --- a/tools/libxl/xl_cmdimpl.c +++ b/tools/libxl/xl_cmdimpl.c @@ -2118,6 +2118,7 @@ start: case 0: LOG("Done. Exiting now"); + libxl_event_free(ctx, event); ret = 0; goto out; @@ -2127,6 +2128,7 @@ start: case LIBXL_EVENT_TYPE_DOMAIN_DEATH: LOG("Domain %d has been destroyed.", domid); + libxl_event_free(ctx, event); ret = 0; goto out; -- cgit v1.2.3