Exemple #1
0
int xc_dom_try_gunzip(struct xc_dom_image *dom, void **blob, size_t * size)
{
    void *unzip;
    size_t unziplen;

    unziplen = xc_dom_check_gzip(dom->xch, *blob, *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;

    if ( xc_dom_do_gunzip(dom->xch, *blob, *size, unzip, unziplen) == -1 )
        return -1;

    *blob = unzip;
    *size = unziplen;
    return 0;
}
static int xc_try_lzo1x_decode(
    struct xc_dom_image *dom, void **blob, size_t *size)
{
    int ret;
    const unsigned char *cur = dom->kernel_blob;
    unsigned char *out_buf = NULL;
    size_t left = dom->kernel_size;
    const char *msg;
    unsigned version;
    static const unsigned char magic[] = {
        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.
     */
    BUILD_BUG_ON(sizeof(lzo_uint) != sizeof(size_t));

    ret = lzo_init();
    if ( ret != LZO_E_OK )
    {
        DOMPRINTF("LZO1x: Failed to init library (%d)\n", ret);
        return -1;
    }

    if ( left < 16 || memcmp(cur, magic, 9) )
    {
        DOMPRINTF("LZO1x: Unrecognized magic\n");
        return -1;
    }

    /* get version (2bytes), skip library version (2),
     * 'need to be extracted' version (2) and method (1) */
    version = lzo_read_16(cur + 9);
    cur += 16;
    left -= 16;

    if ( version >= 0x0940 )
    {
        /* skip level */
        ++cur;
        if ( left )
            --left;
    }

    if ( left >= 4 && (lzo_read_32(cur) & LZOP_HEADER_HAS_FILTER) )
        ret = 8; /* flags + filter info */
    else
        ret = 4; /* flags */

    /* skip mode and mtime_low */
    ret += 8;
    if ( version >= 0x0940 )
        ret += 4; /* skip mtime_high */

    /* don't care about the file name, and skip checksum */
    if ( left > ret )
        ret += 1 + cur[ret] + 4;

    if ( left < ret )
    {
        DOMPRINTF("LZO1x: Incomplete header\n");
        return -1;
    }
    cur += ret;
    left -= ret;

    for ( *size = 0; ; )
    {
        lzo_uint src_len, dst_len, out_len;
        unsigned char *tmp_buf;

        msg = "Short input";
        if ( left < 4 )
            break;

        dst_len = lzo_read_32(cur);
        if ( !dst_len )
        {
            msg = "Error registering stream output";
            if ( xc_dom_register_external(dom, out_buf, *size) )
                break;

            return 0;
        }

        if ( dst_len > LZOP_MAX_BLOCK_SIZE )
        {
            msg = "Block size too large";
            break;
        }

        if ( left < 12 )
            break;

        src_len = lzo_read_32(cur + 4);
        cur += 12; /* also skip block checksum info */
        left -= 12;

        msg = "Bad source length";
        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 )
            break;

        out_buf = tmp_buf;
        out_len = dst_len;

        ret = lzo1x_decompress_safe(cur, src_len,
                                    out_buf + *size, &out_len, NULL);
        switch ( ret )
        {
        case LZO_E_OK:
            msg = "Input underrun";
            if ( out_len != dst_len )
                break;

            *blob = out_buf;
            *size += out_len;
            cur += src_len;
            left -= src_len;
            continue;

        case LZO_E_INPUT_NOT_CONSUMED:
            msg = "Unconsumed input";
            break;

        case LZO_E_OUTPUT_OVERRUN:
            msg = "Output overrun";
            break;

        case LZO_E_INPUT_OVERRUN:
            msg = "Input overrun";
            break;

        case LZO_E_LOOKBEHIND_OVERRUN:
            msg = "Look-behind overrun";
            break;

        case LZO_E_EOF_NOT_FOUND:
            msg = "No EOF marker";
            break;

        case LZO_E_ERROR:
            msg = "General error";
            break;

        default:
            msg = "Internal program error (bug)";
            break;
        }

        break;
    }

    free(out_buf);
    DOMPRINTF("LZO1x decompression error: %s\n", msg);

    return -1;
}
static int xc_try_bzip2_decode(
    struct xc_dom_image *dom, void **blob, size_t *size)
{
    bz_stream stream;
    int ret;
    char *out_buf;
    char *tmp_buf;
    int retval = -1;
    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 )
    {
        DOMPRINTF("BZIP2: Error initting stream");
        return -1;
    }

    /* sigh.  We don't know up-front how much memory we are going to need
     * for the output buffer.  Allocate the output buffer to be equal
     * 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 )
    {
        DOMPRINTF("BZIP2: Failed to alloc memory");
        goto bzip2_cleanup;
    }

    stream.next_in = dom->kernel_blob;
    stream.avail_in = dom->kernel_size;

    stream.next_out = out_buf;
    stream.avail_out = dom->kernel_size;

    for ( ; ; )
    {
        ret = BZ2_bzDecompress(&stream);
        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 > 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 )
            {
                DOMPRINTF("BZIP2: Failed to realloc memory");
                free(out_buf);
                goto bzip2_cleanup;
            }
            out_buf = tmp_buf;

            stream.next_out = out_buf + outsize;
            stream.avail_out = (outsize * 2) - outsize;
            outsize *= 2;
        }
        else if ( stream.avail_in == 0 )
        {
            /*
             * 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;
        }
    }

    total = (((uint64_t)stream.total_out_hi32) << 32) | stream.total_out_lo32;

    if ( xc_dom_register_external(dom, out_buf, total) )
    {
        DOMPRINTF("BZIP2: Error registering stream output");
        free(out_buf);
        goto bzip2_cleanup;
    }

    DOMPRINTF("%s: BZIP2 decompress OK, 0x%zx -> 0x%lx",
              __FUNCTION__, *size, (long unsigned int) total);

    *blob = out_buf;
    *size = total;

bzip2_cleanup:
    BZ2_bzDecompressEnd(&stream);

    return retval;
}
static int _xc_try_lzma_decode(
    struct xc_dom_image *dom, void **blob, size_t *size,
    lzma_stream *stream, const char *what)
{
    lzma_ret ret;
    lzma_action action = LZMA_RUN;
    unsigned char *out_buf;
    unsigned char *tmp_buf;
    int retval = -1;
    size_t outsize;
    const char *msg;

    if ( dom->kernel_size == 0)
    {
        DOMPRINTF("%s: Input is 0 size", what);
        return -1;
    }

    /* sigh.  We don't know up-front how much memory we are going to need
     * for the output buffer.  Allocate the output buffer to be equal
     * the input buffer to start, and we'll realloc as needed.
     */
    outsize = dom->kernel_size;
    out_buf = malloc(outsize);
    if ( out_buf == NULL )
    {
        DOMPRINTF("%s: Failed to alloc memory", what);
        goto lzma_cleanup;
    }

    stream->next_in = dom->kernel_blob;
    stream->avail_in = dom->kernel_size;

    stream->next_out = out_buf;
    stream->avail_out = dom->kernel_size;

    for ( ; ; )
    {
        ret = lzma_code(stream, action);
        if ( ret == LZMA_STREAM_END )
        {
            DOMPRINTF("%s: Saw data stream end", what);
            retval = 0;
            break;
        }
        if ( ret != LZMA_OK )
        {
            switch ( ret )
            {
            case LZMA_MEM_ERROR:
                msg = strerror(ENOMEM);
                break;

            case LZMA_MEMLIMIT_ERROR:
                msg = "Memory usage limit reached";
                break;

            case LZMA_FORMAT_ERROR:
                msg = "File format not recognized";
                break;

            case LZMA_OPTIONS_ERROR:
                // FIXME: Better message?
                msg = "Unsupported compression options";
                break;

            case LZMA_DATA_ERROR:
                msg = "File is corrupt";
                break;

            case LZMA_BUF_ERROR:
                msg = "Unexpected end of input";
                break;

            default:
                msg = "Internal program error (bug)";
                break;
            }
            DOMPRINTF("%s: %s decompression error: %s",
                      __FUNCTION__, what, msg);
            free(out_buf);
            goto lzma_cleanup;
        }

        if ( stream->avail_out == 0 )
        {
            /* Protect against output buffer overflow */
            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 )
            {
                DOMPRINTF("%s: Failed to realloc memory", what);
                free(out_buf);
                goto lzma_cleanup;
            }
            out_buf = tmp_buf;

            stream->next_out = out_buf + outsize;
            stream->avail_out = (outsize * 2) - outsize;
            outsize *= 2;
        }
    }

    if ( xc_dom_register_external(dom, out_buf, stream->total_out) )
    {
        DOMPRINTF("%s: Error registering stream output", what);
        free(out_buf);
        goto lzma_cleanup;
    }

    DOMPRINTF("%s: %s decompress OK, 0x%zx -> 0x%zx",
              __FUNCTION__, what, *size, (size_t)stream->total_out);

    *blob = out_buf;
    *size = stream->total_out;

lzma_cleanup:
    lzma_end(stream);

    return retval;
}