Commit 48fb4b3d authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull s390 fixes from Vasily Gorbik:

 - Fix KASLR base offset to account for symbol offsets in the vmlinux
   ELF file, preventing tool breakages like the drgn debugger

 - Fix potential memory corruption of physmem_info during kernel
   physical address randomization

 - Fix potential memory corruption due to overlap between the relocated
   lowcore and identity mapping by correctly reserving lowcore memory

 - Fix performance regression and avoid randomizing identity mapping
   base by default

 - Fix unnecessary delay of AP bus binding complete uevent to prevent
   startup lag in KVM guests using AP

* tag 's390-6.11-4' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux:
  s390/boot: Fix KASLR base offset off by __START_KERNEL bytes
  s390/boot: Avoid possible physmem_info segment corruption
  s390/ap: Refine AP bus bindings complete processing
  s390/mm: Pin identity mapping base to zero
  s390/mm: Prevent lowcore vs identity mapping overlap
parents 891e811a 1642285e
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -604,6 +604,19 @@ config RANDOMIZE_BASE
	  as a security feature that deters exploit attempts relying on
	  knowledge of the location of kernel internals.

config RANDOMIZE_IDENTITY_BASE
	bool "Randomize the address of the identity mapping base"
	depends on RANDOMIZE_BASE
	default DEBUG_VM
	help
	  The identity mapping base address is pinned to zero by default.
	  Allow randomization of that base to expose otherwise missed
	  notion of physical and virtual addresses of data structures.
	  That does not have any impact on the base address at which the
	  kernel image is loaded.

	  If unsure, say N

config KERNEL_IMAGE_BASE
	hex "Kernel image base address"
	range 0x100000 0x1FFFFFE0000000 if !KASAN
+32 −26
Original line number Diff line number Diff line
@@ -162,7 +162,7 @@ static void kaslr_adjust_relocs(unsigned long min_addr, unsigned long max_addr,
		loc = (long)*reloc + phys_offset;
		if (loc < min_addr || loc > max_addr)
			error("64-bit relocation outside of kernel!\n");
		*(u64 *)loc += offset - __START_KERNEL;
		*(u64 *)loc += offset;
	}
}

@@ -177,7 +177,7 @@ static void kaslr_adjust_got(unsigned long offset)
	 */
	for (entry = (u64 *)vmlinux.got_start; entry < (u64 *)vmlinux.got_end; entry++) {
		if (*entry)
			*entry += offset - __START_KERNEL;
			*entry += offset;
	}
}

@@ -252,7 +252,7 @@ static unsigned long setup_kernel_memory_layout(unsigned long kernel_size)
	vmemmap_size = SECTION_ALIGN_UP(pages) * sizeof(struct page);

	/* choose kernel address space layout: 4 or 3 levels. */
	BUILD_BUG_ON(!IS_ALIGNED(__START_KERNEL, THREAD_SIZE));
	BUILD_BUG_ON(!IS_ALIGNED(TEXT_OFFSET, THREAD_SIZE));
	BUILD_BUG_ON(!IS_ALIGNED(__NO_KASLR_START_KERNEL, THREAD_SIZE));
	BUILD_BUG_ON(__NO_KASLR_END_KERNEL > _REGION1_SIZE);
	vsize = get_vmem_size(ident_map_size, vmemmap_size, vmalloc_size, _REGION3_SIZE);
@@ -341,6 +341,7 @@ static unsigned long setup_kernel_memory_layout(unsigned long kernel_size)
	BUILD_BUG_ON(MAX_DCSS_ADDR > (1UL << MAX_PHYSMEM_BITS));
	max_mappable = max(ident_map_size, MAX_DCSS_ADDR);
	max_mappable = min(max_mappable, vmemmap_start);
	if (IS_ENABLED(CONFIG_RANDOMIZE_IDENTITY_BASE))
		__identity_base = round_down(vmemmap_start - max_mappable, rte_size);

	return asce_limit;
@@ -388,31 +389,25 @@ static void kaslr_adjust_vmlinux_info(long offset)
#endif
}

static void fixup_vmlinux_info(void)
{
	vmlinux.entry -= __START_KERNEL;
	kaslr_adjust_vmlinux_info(-__START_KERNEL);
}

void startup_kernel(void)
{
	unsigned long kernel_size = vmlinux.image_size + vmlinux.bss_size;
	unsigned long nokaslr_offset_phys, kaslr_large_page_offset;
	unsigned long amode31_lma = 0;
	unsigned long vmlinux_size = vmlinux.image_size + vmlinux.bss_size;
	unsigned long nokaslr_text_lma, text_lma = 0, amode31_lma = 0;
	unsigned long kernel_size = TEXT_OFFSET + vmlinux_size;
	unsigned long kaslr_large_page_offset;
	unsigned long max_physmem_end;
	unsigned long asce_limit;
	unsigned long safe_addr;
	psw_t psw;

	fixup_vmlinux_info();
	setup_lpp();

	/*
	 * Non-randomized kernel physical start address must be _SEGMENT_SIZE
	 * aligned (see blow).
	 */
	nokaslr_offset_phys = ALIGN(mem_safe_offset(), _SEGMENT_SIZE);
	safe_addr = PAGE_ALIGN(nokaslr_offset_phys + kernel_size);
	nokaslr_text_lma = ALIGN(mem_safe_offset(), _SEGMENT_SIZE);
	safe_addr = PAGE_ALIGN(nokaslr_text_lma + vmlinux_size);

	/*
	 * Reserve decompressor memory together with decompression heap,
@@ -456,16 +451,27 @@ void startup_kernel(void)
	 */
	kaslr_large_page_offset = __kaslr_offset & ~_SEGMENT_MASK;
	if (kaslr_enabled()) {
		unsigned long end = ident_map_size - kaslr_large_page_offset;
		unsigned long size = vmlinux_size + kaslr_large_page_offset;

		__kaslr_offset_phys = randomize_within_range(kernel_size, _SEGMENT_SIZE, 0, end);
		text_lma = randomize_within_range(size, _SEGMENT_SIZE, TEXT_OFFSET, ident_map_size);
	}
	if (!__kaslr_offset_phys)
		__kaslr_offset_phys = nokaslr_offset_phys;
	__kaslr_offset_phys |= kaslr_large_page_offset;
	if (!text_lma)
		text_lma = nokaslr_text_lma;
	text_lma |= kaslr_large_page_offset;

	/*
	 * [__kaslr_offset_phys..__kaslr_offset_phys + TEXT_OFFSET] region is
	 * never accessed via the kernel image mapping as per the linker script:
	 *
	 *	. = TEXT_OFFSET;
	 *
	 * Therefore, this region could be used for something else and does
	 * not need to be reserved. See how it is skipped in setup_vmem().
	 */
	__kaslr_offset_phys = text_lma - TEXT_OFFSET;
	kaslr_adjust_vmlinux_info(__kaslr_offset_phys);
	physmem_reserve(RR_VMLINUX, __kaslr_offset_phys, kernel_size);
	deploy_kernel((void *)__kaslr_offset_phys);
	physmem_reserve(RR_VMLINUX, text_lma, vmlinux_size);
	deploy_kernel((void *)text_lma);

	/* vmlinux decompression is done, shrink reserved low memory */
	physmem_reserve(RR_DECOMPRESSOR, 0, (unsigned long)_decompressor_end);
@@ -488,7 +494,7 @@ void startup_kernel(void)
		amode31_lma = randomize_within_range(vmlinux.amode31_size, PAGE_SIZE, amode31_min, SZ_2G);
	}
	if (!amode31_lma)
		amode31_lma = __kaslr_offset_phys - vmlinux.amode31_size;
		amode31_lma = text_lma - vmlinux.amode31_size;
	physmem_reserve(RR_AMODE31, amode31_lma, vmlinux.amode31_size);

	/*
@@ -504,8 +510,8 @@ void startup_kernel(void)
	 * - copy_bootdata() must follow setup_vmem() to propagate changes
	 *   to bootdata made by setup_vmem()
	 */
	clear_bss_section(__kaslr_offset_phys);
	kaslr_adjust_relocs(__kaslr_offset_phys, __kaslr_offset_phys + vmlinux.image_size,
	clear_bss_section(text_lma);
	kaslr_adjust_relocs(text_lma, text_lma + vmlinux.image_size,
			    __kaslr_offset, __kaslr_offset_phys);
	kaslr_adjust_got(__kaslr_offset);
	setup_vmem(__kaslr_offset, __kaslr_offset + kernel_size, asce_limit);
+12 −2
Original line number Diff line number Diff line
@@ -90,7 +90,7 @@ static void kasan_populate_shadow(unsigned long kernel_start, unsigned long kern
		}
		memgap_start = end;
	}
	kasan_populate(kernel_start, kernel_end, POPULATE_KASAN_MAP_SHADOW);
	kasan_populate(kernel_start + TEXT_OFFSET, kernel_end, POPULATE_KASAN_MAP_SHADOW);
	kasan_populate(0, (unsigned long)__identity_va(0), POPULATE_KASAN_ZERO_SHADOW);
	kasan_populate(AMODE31_START, AMODE31_END, POPULATE_KASAN_ZERO_SHADOW);
	if (IS_ENABLED(CONFIG_KASAN_VMALLOC)) {
@@ -475,7 +475,17 @@ void setup_vmem(unsigned long kernel_start, unsigned long kernel_end, unsigned l
				 (unsigned long)__identity_va(end),
				 POPULATE_IDENTITY);
	}
	pgtable_populate(kernel_start, kernel_end, POPULATE_KERNEL);

	/*
	 * [kernel_start..kernel_start + TEXT_OFFSET] region is never
	 * accessed as per the linker script:
	 *
	 *	. = TEXT_OFFSET;
	 *
	 * Therefore, skip mapping TEXT_OFFSET bytes to prevent access to
	 * [__kaslr_offset_phys..__kaslr_offset_phys + TEXT_OFFSET] region.
	 */
	pgtable_populate(kernel_start + TEXT_OFFSET, kernel_end, POPULATE_KERNEL);
	pgtable_populate(AMODE31_START, AMODE31_END, POPULATE_DIRECT);
	pgtable_populate(__abs_lowcore, __abs_lowcore + sizeof(struct lowcore),
			 POPULATE_ABS_LOWCORE);
+6 −1
Original line number Diff line number Diff line
@@ -109,7 +109,12 @@ SECTIONS
#ifdef CONFIG_KERNEL_UNCOMPRESSED
	. = ALIGN(PAGE_SIZE);
	. += AMODE31_SIZE;		/* .amode31 section */
	. = ALIGN(1 << 20);		/* _SEGMENT_SIZE */

	/*
	 * Make sure the location counter is not less than TEXT_OFFSET.
	 * _SEGMENT_SIZE is not available, use ALIGN(1 << 20) instead.
	 */
	. = MAX(TEXT_OFFSET, ALIGN(1 << 20));
#else
	. = ALIGN(8);
#endif
+2 −1
Original line number Diff line number Diff line
@@ -279,8 +279,9 @@ static inline unsigned long virt_to_pfn(const void *kaddr)
#define AMODE31_SIZE		(3 * PAGE_SIZE)

#define KERNEL_IMAGE_SIZE	(512 * 1024 * 1024)
#define __START_KERNEL		0x100000
#define __NO_KASLR_START_KERNEL	CONFIG_KERNEL_IMAGE_BASE
#define __NO_KASLR_END_KERNEL	(__NO_KASLR_START_KERNEL + KERNEL_IMAGE_SIZE)

#define TEXT_OFFSET		0x100000

#endif /* _S390_PAGE_H */
Loading