alloc_bootmem can corrupt memory

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 





Hi,
	During bootup, the EFI memory map is scanned and converted to
an in-kernel representation.  The memory for that in-kernel
representation is allocated directly out of the EFI memory map using a
fairly arcane alogrithm.  But the allocation algorithm doesn't take
into account any reserved regions.  Which means that for some memory
layouts, it can allocate memory that overlaps an already reserved
region.

For example, assume an EFI memory map like this:

0-1MB WB EFI_BOOT_SERVICES_DATA
1-2MB WB EFI_PAL_CODE
2-1025MB WB EFI_CONVENTIONAL_MEMORY

and assume the kernel is loaded at 64M, and there is a 64M granule
size.

arch/ia64/kernel/efi.c:find_memmap_space() will find memory at the 64M
granule boundary.  This is already used by the kernel.  So the
rsvd_space[] array might look like:

rsvd[0] 10ac48->10acc0
rsvd[1] 10acc0->10ad10
rsvd[2] 10ad10->10ad58
rsvd[3] 4000000->4738dc8
rsvd[4] 4000000->4000030

The last entry is for the kernel copy of the EFI map.  It has already
corrupted the VHPT miss handler.  Then alloc_bootmem starts allocating
after the highest reserved space.

Maybe a patch something like this is needed (minus the debugging
output) (yes there are cleaner ways to do this, and I can do them if
the general approach is acceptable).

diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c
index bb8770a..f8ae8d0 100644
--- a/arch/ia64/kernel/efi.c
+++ b/arch/ia64/kernel/efi.c
@@ -894,6 +895,16 @@ find_memmap_space (void)
 		as = max(contig_low, md->phys_addr);
 		ae = min(contig_high, efi_md_end(md));
 
+		{
+			int i;
+			for (i = 0; i < num_rsvd_regions; i++) {
+				if (rsvd_region[i].end < as)
+					continue;
+				if (rsvd_region[i].start <= as && rsvd_region[i].end >= as + space_needed)
+					as = rsvd_region[i].end;
+			}
+		}
+
 		/* keep within max_addr= command line arg */
 		ae = min(ae, max_addr);
 		if (ae <= as)
diff --git a/arch/ia64/kernel/setup.c b/arch/ia64/kernel/setup.c
index c4caa80..288f367 100644
--- a/arch/ia64/kernel/setup.c
+++ b/arch/ia64/kernel/setup.c
@@ -248,6 +248,7 @@ #ifdef CONFIG_BLK_DEV_INITRD
 		n++;
 	}
 #endif
+	num_rsvd_regions = n;
 
 	efi_memmap_init(&rsvd_region[n].start, &rsvd_region[n].end);
 	n++;
@@ -261,6 +262,14 @@ #endif
 	BUG_ON(IA64_MAX_RSVD_REGIONS + 1 < n);
 
 	sort_regions(rsvd_region, num_rsvd_regions);
+	{
+		int i;
+		for (i = 0; i < num_rsvd_regions; i++)
+			printk("rsvd[%d] %lx->%lx\n",
+				i,
+				rsvd_region[i].start,
+				rsvd_region[i].end);
+	}
 }
 
 /**
-
To unsubscribe from this list: send the line "unsubscribe linux-ia64" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel]     [Sparc Linux]     [DCCP]     [Linux ARM]     [Yosemite News]     [Linux SCSI]     [Linux x86_64]     [Linux for Ham Radio]

  Powered by Linux