x86/boot: Calculate decompression size during boot not build
authorYinghai Lu <yinghai@kernel.org>
Fri, 29 Apr 2016 00:09:05 +0000 (17:09 -0700)
committerIngo Molnar <mingo@kernel.org>
Fri, 29 Apr 2016 09:03:29 +0000 (11:03 +0200)
Currently z_extract_offset is calculated in boot/compressed/mkpiggy.c.
This doesn't work well because mkpiggy.c doesn't know the details of the
decompressor in use. As a result, it can only make an estimation, which
has risks:

 - output + output_len (VO) could be much bigger than input + input_len
   (ZO). In this case, the decompressed kernel plus relocs could overwrite
   the decompression code while it is running.

 - The head code of ZO could be bigger than z_extract_offset. In this case
   an overwrite could happen when the head code is running to move ZO to
   the end of buffer. Though currently the size of the head code is very
   small it's still a potential risk. Since there is no rule to limit the
   size of the head code of ZO, it runs the risk of suddenly becoming a
   (hard to find) bug.

Instead, this moves the z_extract_offset calculation into header.S, and
makes adjustments to be sure that the above two cases can never happen,
and further corrects the comments describing the calculations.

Since we have (in the previous patch) made ZO always be located against
the end of decompression buffer, z_extract_offset is only used here to
calculate an appropriate buffer size (INIT_SIZE), and is not longer used
elsewhere. As such, it can be removed from voffset.h.

Additionally clean up #if/#else #define to improve readability.

Signed-off-by: Yinghai Lu <yinghai@kernel.org>
Signed-off-by: Baoquan He <bhe@redhat.com>
[ Rewrote the changelog and comments. ]
Signed-off-by: Kees Cook <keescook@chromium.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Vivek Goyal <vgoyal@redhat.com>
Cc: lasse.collin@tukaani.org
Link: http://lkml.kernel.org/r/1461888548-32439-4-git-send-email-keescook@chromium.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
arch/x86/boot/Makefile
arch/x86/boot/compressed/mkpiggy.c
arch/x86/boot/header.S

index b1ef9e48908474bf95dbe6f577cc1fab1a5ea669..942f7dabfb1ef3f7319c2b40dd894a6b1f645d6b 100644 (file)
@@ -95,7 +95,7 @@ targets += voffset.h
 $(obj)/voffset.h: vmlinux FORCE
        $(call if_changed,voffset)
 
-sed-zoffset := -e 's/^\([0-9a-fA-F]*\) [ABCDGRSTVW] \(startup_32\|startup_64\|efi32_stub_entry\|efi64_stub_entry\|efi_pe_entry\|input_data\|_end\|z_.*\)$$/\#define ZO_\2 0x\1/p'
+sed-zoffset := -e 's/^\([0-9a-fA-F]*\) [ABCDGRSTVW] \(startup_32\|startup_64\|efi32_stub_entry\|efi64_stub_entry\|efi_pe_entry\|input_data\|_end\|_ehead\|_text\|z_.*\)$$/\#define ZO_\2 0x\1/p'
 
 quiet_cmd_zoffset = ZOFFSET $@
       cmd_zoffset = $(NM) $< | sed -n $(sed-zoffset) > $@
index b980046c33293e115c98e10eb3ae8869a9ba0907..f095ed9e7d3c425ea2e5ff4faf86fb463e3b540e 100644 (file)
  *
  *  H. Peter Anvin <hpa@linux.intel.com>
  *
- * ----------------------------------------------------------------------- */
-
-/*
- * Compute the desired load offset from a compressed program; outputs
- * a small assembly wrapper with the appropriate symbols defined.
+ * -----------------------------------------------------------------------
+ *
+ * Outputs a small assembly wrapper with the appropriate symbols defined.
+ *
  */
 
 #include <stdlib.h>
@@ -35,7 +34,6 @@ int main(int argc, char *argv[])
 {
        uint32_t olen;
        long ilen;
-       unsigned long offs;
        unsigned long run_size;
        FILE *f = NULL;
        int retval = 1;
@@ -67,15 +65,6 @@ int main(int argc, char *argv[])
        ilen = ftell(f);
        olen = get_unaligned_le32(&olen);
 
-       /*
-        * Now we have the input (compressed) and output (uncompressed)
-        * sizes, compute the necessary decompression offset...
-        */
-
-       offs = (olen > ilen) ? olen - ilen : 0;
-       offs += olen >> 12;     /* Add 8 bytes for each 32K block */
-       offs += 64*1024 + 128;  /* Add 64K + 128 bytes slack */
-       offs = (offs+4095) & ~4095; /* Round to a 4K boundary */
        run_size = atoi(argv[2]);
 
        printf(".section \".rodata..compressed\",\"a\",@progbits\n");
@@ -83,8 +72,6 @@ int main(int argc, char *argv[])
        printf("z_input_len = %lu\n", ilen);
        printf(".globl z_output_len\n");
        printf("z_output_len = %lu\n", (unsigned long)olen);
-       printf(".globl z_extract_offset\n");
-       printf("z_extract_offset = 0x%lx\n", offs);
        printf(".globl z_run_size\n");
        printf("z_run_size = %lu\n", run_size);
 
index fd85b9e4e953089a2dddcfad9f33651f6c54eb68..3dd5be33aaa7b091fce5c21c3c8105cbbef5f707 100644 (file)
@@ -508,13 +508,10 @@ pref_address:             .quad LOAD_PHYSICAL_ADDR        # preferred load addr
 # To avoid problems with the compressed data's meta information an extra 18
 # bytes are needed.  Leading to the formula:
 #
-# extra_bytes = (uncompressed_size >> 12) + 32768 + 18 + decompressor_size
+# extra_bytes = (uncompressed_size >> 12) + 32768 + 18
 #
 # Adding 8 bytes per 32K is a bit excessive but much easier to calculate.
 # Adding 32768 instead of 32767 just makes for round numbers.
-# Adding the decompressor_size is necessary as it musht live after all
-# of the data as well.  Last I measured the decompressor is about 14K.
-# 10K of actual data and 4K of bss.
 #
 # Above analysis is for decompressing gzip compressed kernel only. Up to
 # now 6 different decompressor are supported all together. And among them
@@ -524,17 +521,35 @@ pref_address:             .quad LOAD_PHYSICAL_ADDR        # preferred load addr
 # the description in lib/decompressor_xxx.c for specific information.
 #
 # extra_bytes = (uncompressed_size >> 12) + 65536 + 128
-#
-# Note that this calculation, which results in z_extract_offset (below),
-# is currently generated in compressed/mkpiggy.c
 
-#define ZO_INIT_SIZE   (ZO__end - ZO_startup_32 + ZO_z_extract_offset)
+#define ZO_z_extra_bytes       ((ZO_z_output_len >> 12) + 65536 + 128)
+#if ZO_z_output_len > ZO_z_input_len
+# define ZO_z_extract_offset   (ZO_z_output_len + ZO_z_extra_bytes - \
+                                ZO_z_input_len)
+#else
+# define ZO_z_extract_offset   ZO_z_extra_bytes
+#endif
+
+/*
+ * The extract_offset has to be bigger than ZO head section. Otherwise when
+ * the head code is running to move ZO to the end of the buffer, it will
+ * overwrite the head code itself.
+ */
+#if (ZO__ehead - ZO_startup_32) > ZO_z_extract_offset
+# define ZO_z_min_extract_offset ((ZO__ehead - ZO_startup_32 + 4095) & ~4095)
+#else
+# define ZO_z_min_extract_offset ((ZO_z_extract_offset + 4095) & ~4095)
+#endif
+
+#define ZO_INIT_SIZE   (ZO__end - ZO_startup_32 + ZO_z_min_extract_offset)
+
 #define VO_INIT_SIZE   (VO__end - VO__text)
 #if ZO_INIT_SIZE > VO_INIT_SIZE
-#define INIT_SIZE ZO_INIT_SIZE
+# define INIT_SIZE ZO_INIT_SIZE
 #else
-#define INIT_SIZE VO_INIT_SIZE
+# define INIT_SIZE VO_INIT_SIZE
 #endif
+
 init_size:             .long INIT_SIZE         # kernel initialization size
 handover_offset:       .long 0                 # Filled in by build.c