Skip to content

Commit d82c0d1

Browse files
marcelocerriMartin Schwidefsky
authored andcommitted
s390/decompressor: fix initrd corruption caused by bss clear
Reorder the operations in decompress_kernel() to ensure initrd is moved to a safe location before the bss section is zeroed. During decompression bss can overlap with the initrd and this can corrupt the initrd contents depending on the size of the compressed kernel (which affects where the initrd is placed by the bootloader) and the size of the bss section of the decompressor. Also use the correct initrd size when checking for overlaps with parmblock. Fixes: 06c0dd7 ([S390] fix boot failures with compressed kernels) Cc: stable@vger.kernel.org Reviewed-by: Joy Latten <joy.latten@canonical.com> Reviewed-by: Vineetha HariPai <vineetha.hari.pai@canonical.com> Signed-off-by: Marcelo Henrique Cerri <marcelo.cerri@canonical.com> Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
1 parent 093b995 commit d82c0d1

File tree

1 file changed

+19
-16
lines changed
  • arch/s390/boot/compressed

1 file changed

+19
-16
lines changed

arch/s390/boot/compressed/misc.c

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -141,31 +141,34 @@ static void check_ipl_parmblock(void *start, unsigned long size)
141141

142142
unsigned long decompress_kernel(void)
143143
{
144-
unsigned long output_addr;
145-
unsigned char *output;
144+
void *output, *kernel_end;
146145

147-
output_addr = ((unsigned long) &_end + HEAP_SIZE + 4095UL) & -4096UL;
148-
check_ipl_parmblock((void *) 0, output_addr + SZ__bss_start);
149-
memset(&_bss, 0, &_ebss - &_bss);
150-
free_mem_ptr = (unsigned long)&_end;
151-
free_mem_end_ptr = free_mem_ptr + HEAP_SIZE;
152-
output = (unsigned char *) output_addr;
146+
output = (void *) ALIGN((unsigned long) &_end + HEAP_SIZE, PAGE_SIZE);
147+
kernel_end = output + SZ__bss_start;
148+
check_ipl_parmblock((void *) 0, (unsigned long) kernel_end);
153149

154150
#ifdef CONFIG_BLK_DEV_INITRD
155151
/*
156152
* Move the initrd right behind the end of the decompressed
157-
* kernel image.
153+
* kernel image. This also prevents initrd corruption caused by
154+
* bss clearing since kernel_end will always be located behind the
155+
* current bss section..
158156
*/
159-
if (INITRD_START && INITRD_SIZE &&
160-
INITRD_START < (unsigned long) output + SZ__bss_start) {
161-
check_ipl_parmblock(output + SZ__bss_start,
162-
INITRD_START + INITRD_SIZE);
163-
memmove(output + SZ__bss_start,
164-
(void *) INITRD_START, INITRD_SIZE);
165-
INITRD_START = (unsigned long) output + SZ__bss_start;
157+
if (INITRD_START && INITRD_SIZE && kernel_end > (void *) INITRD_START) {
158+
check_ipl_parmblock(kernel_end, INITRD_SIZE);
159+
memmove(kernel_end, (void *) INITRD_START, INITRD_SIZE);
160+
INITRD_START = (unsigned long) kernel_end;
166161
}
167162
#endif
168163

164+
/*
165+
* Clear bss section. free_mem_ptr and free_mem_end_ptr need to be
166+
* initialized afterwards since they reside in bss.
167+
*/
168+
memset(&_bss, 0, &_ebss - &_bss);
169+
free_mem_ptr = (unsigned long) &_end;
170+
free_mem_end_ptr = free_mem_ptr + HEAP_SIZE;
171+
169172
puts("Uncompressing Linux... ");
170173
__decompress(input_data, input_len, NULL, NULL, output, 0, NULL, error);
171174
puts("Ok, booting the kernel.\n");

0 commit comments

Comments
 (0)