lkml.org 
[lkml]   [2015]   [Sep]   [26]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.1 112/159] lib/decompressors: use real out buf size for gunzip with kernel
    Date
    4.1-stable review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Yinghai Lu <yinghai@kernel.org>

    commit 2d3862d26e67a59340ba1cf1748196c76c5787de upstream.

    When loading x86 64bit kernel above 4GiB with patched grub2, got kernel
    gunzip error.

    | early console in decompress_kernel
    | decompress_kernel:
    | input: [0x807f2143b4-0x807ff61aee]
    | output: [0x807cc00000-0x807f3ea29b] 0x027ea29c: output_len
    | boot via startup_64
    | KASLR using RDTSC...
    | new output: [0x46fe000000-0x470138cfff] 0x0338d000: output_run_size
    | decompress: [0x46fe000000-0x47007ea29b] <=== [0x807f2143b4-0x807ff61aee]
    |
    | Decompressing Linux... gz...
    |
    | uncompression error
    |
    | -- System halted

    the new buffer is at 0x46fe000000ULL, decompressor_gzip is using
    0xffffffb901ffffff as out_len. gunzip in lib/zlib_inflate/inflate.c cap
    that len to 0x01ffffff and decompress fails later.

    We could hit this problem with crashkernel booting that uses kexec loading
    kernel above 4GiB.

    We have decompress_* support:
    1. inbuf[]/outbuf[] for kernel preboot.
    2. inbuf[]/flush() for initramfs
    3. fill()/flush() for initrd.
    This bug only affect kernel preboot path that use outbuf[].

    Add __decompress and take real out_buf_len for gunzip instead of guessing
    wrong buf size.

    Fixes: 1431574a1c4 (lib/decompressors: fix "no limit" output buffer length)
    Signed-off-by: Yinghai Lu <yinghai@kernel.org>
    Cc: Alexandre Courbot <acourbot@nvidia.com>
    Cc: Jon Medhurst <tixy@linaro.org>
    Cc: Stephen Warren <swarren@wwwdotorg.org>
    Cc: "H. Peter Anvin" <hpa@zytor.com>
    Cc: Thomas Gleixner <tglx@linutronix.de>
    Cc: Ingo Molnar <mingo@redhat.com>
    Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
    Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

    ---
    arch/arm/boot/compressed/decompress.c | 2 +-
    arch/m32r/boot/compressed/misc.c | 3 ++-
    arch/mips/boot/compressed/decompress.c | 4 ++--
    arch/s390/boot/compressed/misc.c | 2 +-
    arch/sh/boot/compressed/misc.c | 2 +-
    arch/unicore32/boot/compressed/misc.c | 4 ++--
    arch/x86/boot/compressed/misc.c | 3 ++-
    lib/decompress_bunzip2.c | 6 +++---
    lib/decompress_inflate.c | 31 ++++++++++++++++++++++++++-----
    lib/decompress_unlz4.c | 6 +++---
    lib/decompress_unlzma.c | 7 +++----
    lib/decompress_unlzo.c | 13 ++++++++++++-
    lib/decompress_unxz.c | 12 +++++++++++-
    13 files changed, 69 insertions(+), 26 deletions(-)

    --- a/arch/arm/boot/compressed/decompress.c
    +++ b/arch/arm/boot/compressed/decompress.c
    @@ -57,5 +57,5 @@ extern char * strstr(const char * s1, co

    int do_decompress(u8 *input, int len, u8 *output, void (*error)(char *x))
    {
    - return decompress(input, len, NULL, NULL, output, NULL, error);
    + return __decompress(input, len, NULL, NULL, output, 0, NULL, error);
    }
    --- a/arch/m32r/boot/compressed/misc.c
    +++ b/arch/m32r/boot/compressed/misc.c
    @@ -86,6 +86,7 @@ decompress_kernel(int mmu_on, unsigned c
    free_mem_end_ptr = free_mem_ptr + BOOT_HEAP_SIZE;

    puts("\nDecompressing Linux... ");
    - decompress(input_data, input_len, NULL, NULL, output_data, NULL, error);
    + __decompress(input_data, input_len, NULL, NULL, output_data, 0,
    + NULL, error);
    puts("done.\nBooting the kernel.\n");
    }
    --- a/arch/mips/boot/compressed/decompress.c
    +++ b/arch/mips/boot/compressed/decompress.c
    @@ -111,8 +111,8 @@ void decompress_kernel(unsigned long boo
    puts("\n");

    /* Decompress the kernel with according algorithm */
    - decompress((char *)zimage_start, zimage_size, 0, 0,
    - (void *)VMLINUX_LOAD_ADDRESS_ULL, 0, error);
    + __decompress((char *)zimage_start, zimage_size, 0, 0,
    + (void *)VMLINUX_LOAD_ADDRESS_ULL, 0, 0, error);

    /* FIXME: should we flush cache here? */
    puts("Now, booting the kernel...\n");
    --- a/arch/s390/boot/compressed/misc.c
    +++ b/arch/s390/boot/compressed/misc.c
    @@ -167,7 +167,7 @@ unsigned long decompress_kernel(void)
    #endif

    puts("Uncompressing Linux... ");
    - decompress(input_data, input_len, NULL, NULL, output, NULL, error);
    + __decompress(input_data, input_len, NULL, NULL, output, 0, NULL, error);
    puts("Ok, booting the kernel.\n");
    return (unsigned long) output;
    }
    --- a/arch/sh/boot/compressed/misc.c
    +++ b/arch/sh/boot/compressed/misc.c
    @@ -132,7 +132,7 @@ void decompress_kernel(void)

    puts("Uncompressing Linux... ");
    cache_control(CACHE_ENABLE);
    - decompress(input_data, input_len, NULL, NULL, output, NULL, error);
    + __decompress(input_data, input_len, NULL, NULL, output, 0, NULL, error);
    cache_control(CACHE_DISABLE);
    puts("Ok, booting the kernel.\n");
    }
    --- a/arch/unicore32/boot/compressed/misc.c
    +++ b/arch/unicore32/boot/compressed/misc.c
    @@ -119,8 +119,8 @@ unsigned long decompress_kernel(unsigned
    output_ptr = get_unaligned_le32(tmp);

    arch_decomp_puts("Uncompressing Linux...");
    - decompress(input_data, input_data_end - input_data, NULL, NULL,
    - output_data, NULL, error);
    + __decompress(input_data, input_data_end - input_data, NULL, NULL,
    + output_data, 0, NULL, error);
    arch_decomp_puts(" done, booting the kernel.\n");
    return output_ptr;
    }
    --- a/arch/x86/boot/compressed/misc.c
    +++ b/arch/x86/boot/compressed/misc.c
    @@ -424,7 +424,8 @@ asmlinkage __visible void *decompress_ke
    #endif

    debug_putstr("\nDecompressing Linux... ");
    - decompress(input_data, input_len, NULL, NULL, output, NULL, error);
    + __decompress(input_data, input_len, NULL, NULL, output, output_len,
    + NULL, error);
    parse_elf(output);
    /*
    * 32-bit always performs relocations. 64-bit relocations are only
    --- a/lib/decompress_bunzip2.c
    +++ b/lib/decompress_bunzip2.c
    @@ -743,12 +743,12 @@ exit_0:
    }

    #ifdef PREBOOT
    -STATIC int INIT decompress(unsigned char *buf, long len,
    +STATIC int INIT __decompress(unsigned char *buf, long len,
    long (*fill)(void*, unsigned long),
    long (*flush)(void*, unsigned long),
    - unsigned char *outbuf,
    + unsigned char *outbuf, long olen,
    long *pos,
    - void(*error)(char *x))
    + void (*error)(char *x))
    {
    return bunzip2(buf, len - 4, fill, flush, outbuf, pos, error);
    }
    --- a/lib/decompress_inflate.c
    +++ b/lib/decompress_inflate.c
    @@ -1,4 +1,5 @@
    #ifdef STATIC
    +#define PREBOOT
    /* Pre-boot environment: included */

    /* prevent inclusion of _LINUX_KERNEL_H in pre-boot environment: lots
    @@ -33,23 +34,23 @@ static long INIT nofill(void *buffer, un
    }

    /* Included from initramfs et al code */
    -STATIC int INIT gunzip(unsigned char *buf, long len,
    +STATIC int INIT __gunzip(unsigned char *buf, long len,
    long (*fill)(void*, unsigned long),
    long (*flush)(void*, unsigned long),
    - unsigned char *out_buf,
    + unsigned char *out_buf, long out_len,
    long *pos,
    void(*error)(char *x)) {
    u8 *zbuf;
    struct z_stream_s *strm;
    int rc;
    - size_t out_len;

    rc = -1;
    if (flush) {
    out_len = 0x8000; /* 32 K */
    out_buf = malloc(out_len);
    } else {
    - out_len = ((size_t)~0) - (size_t)out_buf; /* no limit */
    + if (!out_len)
    + out_len = ((size_t)~0) - (size_t)out_buf; /* no limit */
    }
    if (!out_buf) {
    error("Out of memory while allocating output buffer");
    @@ -181,4 +182,24 @@ gunzip_nomem1:
    return rc; /* returns Z_OK (0) if successful */
    }

    -#define decompress gunzip
    +#ifndef PREBOOT
    +STATIC int INIT gunzip(unsigned char *buf, long len,
    + long (*fill)(void*, unsigned long),
    + long (*flush)(void*, unsigned long),
    + unsigned char *out_buf,
    + long *pos,
    + void (*error)(char *x))
    +{
    + return __gunzip(buf, len, fill, flush, out_buf, 0, pos, error);
    +}
    +#else
    +STATIC int INIT __decompress(unsigned char *buf, long len,
    + long (*fill)(void*, unsigned long),
    + long (*flush)(void*, unsigned long),
    + unsigned char *out_buf, long out_len,
    + long *pos,
    + void (*error)(char *x))
    +{
    + return __gunzip(buf, len, fill, flush, out_buf, out_len, pos, error);
    +}
    +#endif
    --- a/lib/decompress_unlz4.c
    +++ b/lib/decompress_unlz4.c
    @@ -196,12 +196,12 @@ exit_0:
    }

    #ifdef PREBOOT
    -STATIC int INIT decompress(unsigned char *buf, long in_len,
    +STATIC int INIT __decompress(unsigned char *buf, long in_len,
    long (*fill)(void*, unsigned long),
    long (*flush)(void*, unsigned long),
    - unsigned char *output,
    + unsigned char *output, long out_len,
    long *posp,
    - void(*error)(char *x)
    + void (*error)(char *x)
    )
    {
    return unlz4(buf, in_len - 4, fill, flush, output, posp, error);
    --- a/lib/decompress_unlzma.c
    +++ b/lib/decompress_unlzma.c
    @@ -667,13 +667,12 @@ exit_0:
    }

    #ifdef PREBOOT
    -STATIC int INIT decompress(unsigned char *buf, long in_len,
    +STATIC int INIT __decompress(unsigned char *buf, long in_len,
    long (*fill)(void*, unsigned long),
    long (*flush)(void*, unsigned long),
    - unsigned char *output,
    + unsigned char *output, long out_len,
    long *posp,
    - void(*error)(char *x)
    - )
    + void (*error)(char *x))
    {
    return unlzma(buf, in_len - 4, fill, flush, output, posp, error);
    }
    --- a/lib/decompress_unlzo.c
    +++ b/lib/decompress_unlzo.c
    @@ -31,6 +31,7 @@
    */

    #ifdef STATIC
    +#define PREBOOT
    #include "lzo/lzo1x_decompress_safe.c"
    #else
    #include <linux/decompress/unlzo.h>
    @@ -287,4 +288,14 @@ exit:
    return ret;
    }

    -#define decompress unlzo
    +#ifdef PREBOOT
    +STATIC int INIT __decompress(unsigned char *buf, long len,
    + long (*fill)(void*, unsigned long),
    + long (*flush)(void*, unsigned long),
    + unsigned char *out_buf, long olen,
    + long *pos,
    + void (*error)(char *x))
    +{
    + return unlzo(buf, len, fill, flush, out_buf, pos, error);
    +}
    +#endif
    --- a/lib/decompress_unxz.c
    +++ b/lib/decompress_unxz.c
    @@ -394,4 +394,14 @@ error_alloc_state:
    * This macro is used by architecture-specific files to decompress
    * the kernel image.
    */
    -#define decompress unxz
    +#ifdef XZ_PREBOOT
    +STATIC int INIT __decompress(unsigned char *buf, long len,
    + long (*fill)(void*, unsigned long),
    + long (*flush)(void*, unsigned long),
    + unsigned char *out_buf, long olen,
    + long *pos,
    + void (*error)(char *x))
    +{
    + return unxz(buf, len, fill, flush, out_buf, pos, error);
    +}
    +#endif



    \
     
     \ /
      Last update: 2015-09-26 23:41    [W:4.434 / U:0.468 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site