Wed Dec 19 12:29:44 2012 UTC ()
Fix for DOS: XSA25 (CVE-2012-4544 / CVE-2012-2625) - dom0 memory exhaustion
possibility by uncompressing of domU kernels.

>From upstream; only file paths changed to be relative to our WRKSRC.

ok'd by cegger@/wiz@.


(is)
diff -r1.25 -r1.26 pkgsrc/sysutils/xentools41/Makefile
diff -r1.22 -r1.23 pkgsrc/sysutils/xentools41/distinfo
diff -r0 -r1.1 pkgsrc/sysutils/xentools41/patches/patch-XSA25

cvs diff -r1.25 -r1.26 pkgsrc/sysutils/xentools41/Attic/Makefile (expand / switch to unified diff)

--- pkgsrc/sysutils/xentools41/Attic/Makefile 2012/10/23 19:51:36 1.25
+++ pkgsrc/sysutils/xentools41/Attic/Makefile 2012/12/19 12:29:44 1.26
@@ -1,21 +1,21 @@ @@ -1,21 +1,21 @@
1# $NetBSD: Makefile,v 1.25 2012/10/23 19:51:36 asau Exp $ 1# $NetBSD: Makefile,v 1.26 2012/12/19 12:29:44 is Exp $
2# 2#
3# VERSION is set in version.mk as it is shared with other packages 3# VERSION is set in version.mk as it is shared with other packages
4.include "version.mk" 4.include "version.mk"
5 5
6DISTNAME= xen-${VERSION} 6DISTNAME= xen-${VERSION}
7PKGNAME= xentools41-${VERSION} 7PKGNAME= xentools41-${VERSION}
8PKGREVISION= 3 8PKGREVISION= 4
9CATEGORIES= sysutils 9CATEGORIES= sysutils
10MASTER_SITES= http://bits.xensource.com/oss-xen/release/${VERSION}/ 10MASTER_SITES= http://bits.xensource.com/oss-xen/release/${VERSION}/
11 11
12DISTFILES= ${DISTNAME}.tar.gz 12DISTFILES= ${DISTNAME}.tar.gz
13DISTFILES+= ipxe-git-v1.0.0.tar.gz 13DISTFILES+= ipxe-git-v1.0.0.tar.gz
14SITES.ipxe-git-v1.0.0.tar.gz += http://xenbits.xensource.com/xen-extfiles/ 14SITES.ipxe-git-v1.0.0.tar.gz += http://xenbits.xensource.com/xen-extfiles/
15 15
16MAINTAINER= cegger@NetBSD.org 16MAINTAINER= cegger@NetBSD.org
17HOMEPAGE= http://xen.org/ 17HOMEPAGE= http://xen.org/
18COMMENT= Userland Tools for Xen 4.1.x 18COMMENT= Userland Tools for Xen 4.1.x
19 19
20LICENSE= gnu-gpl-v2 20LICENSE= gnu-gpl-v2
21 21

cvs diff -r1.22 -r1.23 pkgsrc/sysutils/xentools41/Attic/distinfo (expand / switch to unified diff)

--- pkgsrc/sysutils/xentools41/Attic/distinfo 2012/09/12 11:09:32 1.22
+++ pkgsrc/sysutils/xentools41/Attic/distinfo 2012/12/19 12:29:44 1.23
@@ -1,23 +1,24 @@ @@ -1,23 +1,24 @@
1$NetBSD: distinfo,v 1.22 2012/09/12 11:09:32 drochner Exp $ 1$NetBSD: distinfo,v 1.23 2012/12/19 12:29:44 is Exp $
2 2
3SHA1 (ipxe-git-v1.0.0.tar.gz) = da052c8de5f3485fe0253c19cf52ed6d72528485 3SHA1 (ipxe-git-v1.0.0.tar.gz) = da052c8de5f3485fe0253c19cf52ed6d72528485
4RMD160 (ipxe-git-v1.0.0.tar.gz) = dcd9b6eaafa1ce05c1ebf2a15f2f73ad7a8c5547 4RMD160 (ipxe-git-v1.0.0.tar.gz) = dcd9b6eaafa1ce05c1ebf2a15f2f73ad7a8c5547
5Size (ipxe-git-v1.0.0.tar.gz) = 1996881 bytes 5Size (ipxe-git-v1.0.0.tar.gz) = 1996881 bytes
6SHA1 (xen-4.1.3.tar.gz) = 0f688955262d08fba28361ca338f3ad0c0f53d74 6SHA1 (xen-4.1.3.tar.gz) = 0f688955262d08fba28361ca338f3ad0c0f53d74
7RMD160 (xen-4.1.3.tar.gz) = a6296a16579fd628a1ff2aa64b6b800e4913eeae 7RMD160 (xen-4.1.3.tar.gz) = a6296a16579fd628a1ff2aa64b6b800e4913eeae
8Size (xen-4.1.3.tar.gz) = 10382132 bytes 8Size (xen-4.1.3.tar.gz) = 10382132 bytes
9SHA1 (patch-CVE-2012-3515) = ab861e94a23e87f6e2f1338c9b7b2c61818a6943 9SHA1 (patch-CVE-2012-3515) = ab861e94a23e87f6e2f1338c9b7b2c61818a6943
10SHA1 (patch-CVE-2012-4411) = 4ede574c9d97a0553631ac94b48f2d7a5cb68628 10SHA1 (patch-CVE-2012-4411) = 4ede574c9d97a0553631ac94b48f2d7a5cb68628
 11SHA1 (patch-XSA25) = bdadcc2dfeef2a6afe000324fc08d5d3214c4f3b
11SHA1 (patch-aa) = 9b53ba4a809dad7a1de34c8fa0dbe493d7256ada 12SHA1 (patch-aa) = 9b53ba4a809dad7a1de34c8fa0dbe493d7256ada
12SHA1 (patch-ab) = 0906a5ec3a7450fc987b01289e2560e60966d00d 13SHA1 (patch-ab) = 0906a5ec3a7450fc987b01289e2560e60966d00d
13SHA1 (patch-ac) = c3cc5335a1d6b066307c5f03fe72f513a9eb2bdb 14SHA1 (patch-ac) = c3cc5335a1d6b066307c5f03fe72f513a9eb2bdb
14SHA1 (patch-ad) = 5eb15470bff85d30b6d26d8fe094f59fc8e34175 15SHA1 (patch-ad) = 5eb15470bff85d30b6d26d8fe094f59fc8e34175
15SHA1 (patch-ae) = 400bd6cac23af1e75f45c3e4e88e3130a3517129 16SHA1 (patch-ae) = 400bd6cac23af1e75f45c3e4e88e3130a3517129
16SHA1 (patch-af) = e866e7d96766b735a53432350275810803eeb510 17SHA1 (patch-af) = e866e7d96766b735a53432350275810803eeb510
17SHA1 (patch-ag) = 90893326dcce4e3e2ef273f22ec5ddf5af0f7cd8 18SHA1 (patch-ag) = 90893326dcce4e3e2ef273f22ec5ddf5af0f7cd8
18SHA1 (patch-ah) = ab91c41ef6bbdd7f7f3d992b9f81e43056a765e2 19SHA1 (patch-ah) = ab91c41ef6bbdd7f7f3d992b9f81e43056a765e2
19SHA1 (patch-ai) = 8da6bba38bd7677ea829ca35058f7d2d1d7acad4 20SHA1 (patch-ai) = 8da6bba38bd7677ea829ca35058f7d2d1d7acad4
20SHA1 (patch-aj) = d0999d8dcbc1eef4de7037db0e54dcd8d2f706eb 21SHA1 (patch-aj) = d0999d8dcbc1eef4de7037db0e54dcd8d2f706eb
21SHA1 (patch-ak) = 722a6b0541b036d84c703037134e25bc47f3eb65 22SHA1 (patch-ak) = 722a6b0541b036d84c703037134e25bc47f3eb65
22SHA1 (patch-al) = d9a310c16db708dd86170a13946f87e4cd21eb7a 23SHA1 (patch-al) = d9a310c16db708dd86170a13946f87e4cd21eb7a
23SHA1 (patch-ba) = 2c65e4b4b85e91e92dfb3aa402ebc44694bdff06 24SHA1 (patch-ba) = 2c65e4b4b85e91e92dfb3aa402ebc44694bdff06

File Added: pkgsrc/sysutils/xentools41/patches/Attic/patch-XSA25
$NetBSD: patch-XSA25,v 1.1 2012/12/19 12:29:44 is Exp $

libxc: builder: limit maximum size of kernel/ramdisk.

Allowing user supplied kernels of arbitrary sizes, especially during
decompression, can swallow up dom0 memory leading to either virtual
address space exhaustion in the builder process or allocation
failures/OOM killing of both toolstack and unrelated processes.

We disable these checks when building in a stub domain for pvgrub
since this uses the guest's own memory and is isolated.

Decompression of gzip compressed kernels and ramdisks has been safe
since 14954:58205257517d (Xen 3.1.0 onwards).

This is XSA-25 / CVE-2012-4544.

Also make explicit checks for buffer overflows in various
decompression routines. These were already ruled out due to other
properties of the code but check them as a belt-and-braces measure.

Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
Acked-by: Ian Jackson <ian.jackson@eu.citrix.com>
[ Includes 25589:60f09d1ab1fe for CVE-2012-2625 ]

diff --git a/stubdom/grub/kexec.c b/stubdom/grub/kexec.c
index 06bef52..b21c91a 100644
--- a/stubdom/grub/kexec.c
+++ ../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 e72f066..7043f96 100644
--- a/tools/libxc/xc_dom.h
+++ libxc/xc_dom.h
@@ -52,6 +52,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];
@@ -175,6 +178,23 @@ 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);
 
+/* Set this larger if you have enormous ramdisks/kernels. Note that
+ * you should trust all kernels not to be maliciously large (e.g. to
+ * exhaust all dom0 memory) if you do this (see CVE-2012-4544 /
+ * XSA-25). You can also set the default independently for
+ * ramdisks/kernels in xc_dom_allocate() or call
+ * xc_dom_{kernel,ramdisk}_max_size.
+ */
+#ifndef XC_DOM_DECOMPRESS_MAX
+#define XC_DOM_DECOMPRESS_MAX (1024*1024*1024) /* 1GB */
+#endif
+
+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,
@@ -224,7 +244,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 9852e67..73cfad1 100644
--- a/tools/libxc/xc_dom_bzimageloader.c
+++ libxc/xc_dom_bzimageloader.c
@@ -47,13 +47,19 @@ 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;
     stream.bzfree = NULL;
     stream.opaque = NULL;
 
+    if ( dom->kernel_size == 0)
+    {
+        DOMPRINTF("BZIP2: Input is 0 size");
+        return -1;
+    }
+
     ret = BZ2_bzDecompressInit(&stream, 0, 0);
     if ( ret != BZ_OK )
     {
@@ -66,6 +72,17 @@ static int xc_try_bzip2_decode(
      * the input buffer to start, and we'll realloc as needed.
      */
     outsize = dom->kernel_size;
+
+    /*
+     * stream.avail_in and outsize are unsigned int, while kernel_size
+     * is a size_t. Check we aren't overflowing.
+     */
+    if ( outsize != dom->kernel_size )
+    {
+        DOMPRINTF("BZIP2: Input too large");
+        goto bzip2_cleanup;
+    }
+
     out_buf = malloc(outsize);
     if ( out_buf == NULL )
     {
@@ -98,13 +115,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,9 +196,15 @@ 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;
 
+    if ( dom->kernel_size == 0)
+    {
+        DOMPRINTF("LZMA: Input is 0 size");
+        return -1;
+    }
+
     ret = lzma_alone_decoder(&stream, 128*1024*1024);
     if ( ret != LZMA_OK )
     {
@@ -251,13 +281,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("LZMA: output buffer overflow");
                 free(out_buf);
                 goto lzma_cleanup;
             }
 
+            if ( xc_dom_kernel_check_size(dom, outsize * 2) )
+            {
+                DOMPRINTF("LZMA: output too large");
+                free(out_buf);
+                goto lzma_cleanup;
+            }
+
             tmp_buf = realloc(out_buf, outsize * 2);
             if ( tmp_buf == NULL )
             {
@@ -327,6 +364,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 )
     {
@@ -406,6 +449,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 fea9de5..2a01d7c 100644
--- a/tools/libxc/xc_dom_core.c
+++ 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;
@@ -222,6 +230,40 @@ char *xc_dom_strdup(struct xc_dom_image *dom, const char *str)
 }
 
 /* ------------------------------------------------------------------------ */
+/* 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                  */
 
 size_t xc_dom_check_gzip(xc_interface *xch, void *blob, size_t ziplen)
@@ -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;
@@ -588,6 +633,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 )
@@ -608,10 +656,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);
@@ -621,7 +684,9 @@ int xc_dom_ramdisk_file(struct xc_dom_image *dom, const char *filename)
 {
     DOMPRINTF("%s: filename=\"%s\"", __FUNCTION__, filename);
     dom->ramdisk_blob =
-        xc_dom_malloc_filemap(dom, filename, &dom->ramdisk_size);
+        xc_dom_malloc_filemap(dom, filename, &dom->ramdisk_size,
+                              dom->max_ramdisk_size);
+
     if ( dom->ramdisk_blob == NULL )
         return -1;
 //    return xc_dom_try_gunzip(dom, &dom->ramdisk_blob, &dom->ramdisk_size);
@@ -781,7 +846,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/pygrub/src/pygrub b/tools/pygrub/src/pygrub
index 17c0083..1a3c1c3 100644
--- a/tools/pygrub/src/pygrub
+++ pygrub/src/pygrub
@@ -28,6 +28,7 @@ import grub.LiloConf
 import grub.ExtLinuxConf
 
 PYGRUB_VER = 0.6
+FS_READ_MAX = 1024 * 1024
 
 def enable_cursor(ison):
     if ison:
@@ -421,7 +422,8 @@ class Grub:
         if self.__dict__.get('cf', None) is None:
             raise RuntimeError, "couldn't find bootloader config file in the image provided."
         f = fs.open_file(self.cf.filename)
-        buf = f.read()
+        # limit read size to avoid pathological cases
+        buf = f.read(FS_READ_MAX)
         del f
         self.cf.parse(buf)
 
@@ -670,6 +672,37 @@ if __name__ == "__main__":
     def usage():
         print >> sys.stderr, "Usage: %s [-q|--quiet] [-i|--interactive] [-n|--not-really] [--output=] [--kernel=] [--ramdisk=] [--args=] [--entry=] [--output-directory=] [--output-format=sxp|simple|simple0] <image>" %(sys.argv[0],)
 
+    def copy_from_image(fs, file_to_read, file_type, output_directory,
+                        not_really):
+        if not_really:
+            if fs.file_exists(file_to_read):
+                return "<%s:%s>" % (file_type, file_to_read)
+            else:
+                sys.exit("The requested %s file does not exist" % file_type)
+        try:
+            datafile = fs.open_file(file_to_read)
+        except Exception, e:
+            print >>sys.stderr, e
+            sys.exit("Error opening %s in guest" % file_to_read)
+        (tfd, ret) = tempfile.mkstemp(prefix="boot_"+file_type+".",
+                                      dir=output_directory)
+        dataoff = 0
+        while True:
+            data = datafile.read(FS_READ_MAX, dataoff)
+            if len(data) == 0:
+                os.close(tfd)
+                del datafile
+                return ret
+            try:
+                os.write(tfd, data)
+            except Exception, e:
+                print >>sys.stderr, e
+                os.close(tfd)
+                os.unlink(ret)
+                del datafile
+                sys.exit("Error writing temporary copy of "+file_type)
+            dataoff += len(data)
+
     try:
         opts, args = getopt.gnu_getopt(sys.argv[1:], 'qinh::',
                                    ["quiet", "interactive", "not-really", "help", 
@@ -786,24 +819,18 @@ if __name__ == "__main__":
     if not fs:
         raise RuntimeError, "Unable to find partition containing kernel"
 
-    if not_really:
-        bootcfg["kernel"] = "<kernel:%s>" % chosencfg["kernel"]
-    else:
-        data = fs.open_file(chosencfg["kernel"]).read()
-        (tfd, bootcfg["kernel"]) = tempfile.mkstemp(prefix="boot_kernel.",
-                                                    dir=output_directory)
-        os.write(tfd, data)
-        os.close(tfd)
+    bootcfg["kernel"] = copy_from_image(fs, chosencfg["kernel"], "kernel",
+                                        output_directory, not_really)
 
     if chosencfg["ramdisk"]:
-        if not_really:
-            bootcfg["ramdisk"] = "<ramdisk:%s>" % chosencfg["ramdisk"]
-        else:
-            data = fs.open_file(chosencfg["ramdisk"],).read()
-            (tfd, bootcfg["ramdisk"]) = tempfile.mkstemp(
-                prefix="boot_ramdisk.", dir=output_directory)
-            os.write(tfd, data)
-            os.close(tfd)
+        try:
+            bootcfg["ramdisk"] = copy_from_image(fs, chosencfg["ramdisk"],
+                                                 "ramdisk", output_directory,
+                                                 not_really)
+        except:
+            if not not_really:
+                os.unlink(bootcfg["kernel"])
+            raise
     else:
         initrd = None