[PATCH makedumpfile] handle mem_section as either a pointer or an array

Thadeu Lima de Souza Cascardo cascardo at canonical.com
Mon Feb 19 12:04:59 PST 2018


Some kernel versions that have been recently shipped have mem_section point to
a pointer to the array, instead of pointing directly to the array. That only
happens on SPARSEMEM_EXTREME configurations.

As dwarf information might not be present that would have allowed us to detect
which type it is, we need to try it either as an array or as the pointer to the
array. Then, we validate all section_mem_map: they must either be present or
null. If any problems are found when traversing it, consider it invalid. Only
one way may be valid. Otherwise, fail.

This has been tested with both types of kernels and succeeded in producing a
compressed dump that could be analyzed with crash 7.2.1.

Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo at canonical.com>
---
 makedumpfile.c | 153 +++++++++++++++++++++++++++++++++++++++++++--------------
 makedumpfile.h |   1 +
 2 files changed, 118 insertions(+), 36 deletions(-)

diff --git a/makedumpfile.c b/makedumpfile.c
index ed138d3..cd3fa4d 100644
--- a/makedumpfile.c
+++ b/makedumpfile.c
@@ -3297,7 +3297,7 @@ get_mm_discontigmem(void)
 	return TRUE;
 }
 
-unsigned long
+static unsigned long
 nr_to_section(unsigned long nr, unsigned long *mem_sec)
 {
 	unsigned long addr;
@@ -3311,17 +3311,17 @@ nr_to_section(unsigned long nr, unsigned long *mem_sec)
 		addr = SYMBOL(mem_section) + (nr * SIZE(mem_section));
 	}
 
-	if (!is_kvaddr(addr))
-		return NOT_KV_ADDR;
-
 	return addr;
 }
 
-unsigned long
-section_mem_map_addr(unsigned long addr)
+static unsigned long
+section_mem_map_addr(unsigned long addr, unsigned long *map_mask)
 {
 	char *mem_section;
 	unsigned long map;
+	unsigned long mask;
+
+	*map_mask = 0;
 
 	if (!is_kvaddr(addr))
 		return NOT_KV_ADDR;
@@ -3338,15 +3338,19 @@ section_mem_map_addr(unsigned long addr)
 	}
 	map = ULONG(mem_section + OFFSET(mem_section.section_mem_map));
 	if (info->kernel_version < KERNEL_VERSION(4, 13, 0))
-		map &= SECTION_MAP_MASK_4_12;
+		mask = SECTION_MAP_MASK_4_12;
 	else
-		map &= SECTION_MAP_MASK;
+		mask = SECTION_MAP_MASK;
+	*map_mask = map & ~mask;
+	if (map == 0x0)
+		*map_mask |= SECTION_MARKED_PRESENT;
+	map &= mask;
 	free(mem_section);
 
 	return map;
 }
 
-unsigned long
+static unsigned long
 sparse_decode_mem_map(unsigned long coded_mem_map, unsigned long section_nr)
 {
 	unsigned long mem_map;
@@ -3354,17 +3358,110 @@ sparse_decode_mem_map(unsigned long coded_mem_map, unsigned long section_nr)
 	mem_map =  coded_mem_map +
 	    (SECTION_NR_TO_PFN(section_nr) * SIZE(page));
 
-	if (!is_kvaddr(mem_map))
-		return NOT_KV_ADDR;
 	return mem_map;
 }
+
+/*
+ * On some kernels, mem_section may be a pointer or an array, when
+ * SPARSEMEM_EXTREME is on.
+ *
+ * We assume that section_mem_map is either 0 or has the present bit set.
+ *
+ */
+
+static int
+validate_mem_section(unsigned long *mem_sec,
+		     unsigned long mem_section_ptr, unsigned int mem_section_size,
+		     unsigned long *mem_maps, unsigned int num_section)
+{
+	unsigned int section_nr;
+	unsigned long map_mask;
+	unsigned long section, mem_map;
+	if (!readmem(VADDR, mem_section_ptr, mem_sec, mem_section_size)) {
+		ERRMSG("Can't read mem_section array.\n");
+		return FALSE;
+	}
+	for (section_nr = 0; section_nr < num_section; section_nr++) {
+		section = nr_to_section(section_nr, mem_sec);
+		if (section == NOT_KV_ADDR) {
+			mem_map = NOT_MEMMAP_ADDR;
+		} else {
+			mem_map = section_mem_map_addr(section, &map_mask);
+			if (!(map_mask & SECTION_MARKED_PRESENT)) {
+				return FALSE;
+			}
+			if (mem_map == 0) {
+				mem_map = NOT_MEMMAP_ADDR;
+			} else {
+				mem_map = sparse_decode_mem_map(mem_map,
+								section_nr);
+				if (!is_kvaddr(mem_map)) {
+					return FALSE;
+				}
+			}
+		}
+		mem_maps[section_nr] = mem_map;
+	}
+	return TRUE;
+}
+
+static int
+get_mem_section(unsigned int mem_section_size, unsigned long *mem_maps,
+		unsigned int num_section)
+{
+	unsigned long mem_section_ptr;
+	int ret = FALSE;
+	unsigned long *mem_sec = NULL;
+
+	if ((mem_sec = malloc(mem_section_size)) == NULL) {
+		ERRMSG("Can't allocate memory for the mem_section. %s\n",
+		    strerror(errno));
+		return FALSE;
+	}
+	ret = validate_mem_section(mem_sec, SYMBOL(mem_section),
+				   mem_section_size, mem_maps, num_section);
+
+	if (is_sparsemem_extreme()) {
+		int symbol_valid = ret;
+		int pointer_valid;
+		int mem_maps_size = sizeof(*mem_maps) * num_section;
+		unsigned long *mem_maps_ex = NULL;
+		if (!readmem(VADDR, SYMBOL(mem_section), &mem_section_ptr,
+			     sizeof(mem_section_ptr)))
+			goto out;
+
+		if ((mem_maps_ex = malloc(mem_maps_size)) == NULL) {
+			ERRMSG("Can't allocate memory for the mem_maps. %s\n",
+			    strerror(errno));
+			goto out;
+		}
+
+		pointer_valid = validate_mem_section(mem_sec,
+						     mem_section_ptr,
+						     mem_section_size,
+						     mem_maps_ex,
+						     num_section);
+		if (pointer_valid)
+			memcpy(mem_maps, mem_maps_ex, mem_maps_size);
+		if (mem_maps_ex)
+			free(mem_maps_ex);
+		ret = symbol_valid ^ pointer_valid;
+		if (!ret) {
+			ERRMSG("Could not validate mem_section.\n");
+		}
+	}
+out:
+	if (mem_sec != NULL)
+		free(mem_sec);
+	return ret;
+}
+
 int
 get_mm_sparsemem(void)
 {
 	unsigned int section_nr, mem_section_size, num_section;
 	mdf_pfn_t pfn_start, pfn_end;
-	unsigned long section, mem_map;
-	unsigned long *mem_sec = NULL;
+	unsigned long *mem_maps = NULL;
 
 	int ret = FALSE;
 
@@ -3379,13 +3476,12 @@ get_mm_sparsemem(void)
 		info->sections_per_root = _SECTIONS_PER_ROOT();
 		mem_section_size = SIZE(mem_section) * NR_SECTION_ROOTS();
 	}
-	if ((mem_sec = malloc(mem_section_size)) == NULL) {
-		ERRMSG("Can't allocate memory for the mem_section. %s\n",
-		    strerror(errno));
+	if ((mem_maps = malloc(sizeof(*mem_maps) * num_section)) == NULL) {
+		ERRMSG("Can't allocate memory for the mem_maps. %s\n",
+			strerror(errno));
 		return FALSE;
 	}
-	if (!readmem(VADDR, SYMBOL(mem_section), mem_sec,
-	    mem_section_size)) {
+	if (!get_mem_section(mem_section_size, mem_maps, num_section)) {
 		ERRMSG("Can't get the address of mem_section.\n");
 		goto out;
 	}
@@ -3397,31 +3493,16 @@ get_mm_sparsemem(void)
 		goto out;
 	}
 	for (section_nr = 0; section_nr < num_section; section_nr++) {
-		section = nr_to_section(section_nr, mem_sec);
-		if (section == NOT_KV_ADDR) {
-			mem_map = NOT_MEMMAP_ADDR;
-		} else {
-			mem_map = section_mem_map_addr(section);
-			if (mem_map == 0) {
-				mem_map = NOT_MEMMAP_ADDR;
-			} else {
-				mem_map = sparse_decode_mem_map(mem_map,
-								section_nr);
-				if (!is_kvaddr(mem_map))
-					mem_map = NOT_MEMMAP_ADDR;
-			}
-		}
 		pfn_start = section_nr * PAGES_PER_SECTION();
 		pfn_end   = pfn_start + PAGES_PER_SECTION();
 		if (info->max_mapnr < pfn_end)
 			pfn_end = info->max_mapnr;
-		dump_mem_map(pfn_start, pfn_end, mem_map, section_nr);
+		dump_mem_map(pfn_start, pfn_end, mem_maps[section_nr], section_nr);
 	}
 	ret = TRUE;
 out:
-	if (mem_sec != NULL)
-		free(mem_sec);
-
+	if (mem_maps != NULL)
+		free(mem_maps);
 	return ret;
 }
 
diff --git a/makedumpfile.h b/makedumpfile.h
index 01eece2..58e1aaa 100644
--- a/makedumpfile.h
+++ b/makedumpfile.h
@@ -184,6 +184,7 @@ isAnon(unsigned long mapping)
 #define SECTIONS_PER_ROOT()	(info->sections_per_root)
 #define SECTION_ROOT_MASK()	(SECTIONS_PER_ROOT() - 1)
 #define SECTION_NR_TO_ROOT(sec)	((sec) / SECTIONS_PER_ROOT())
+#define SECTION_MARKED_PRESENT  (1UL<<0)
 #define SECTION_IS_ONLINE	(1UL<<2)
 #define SECTION_MAP_LAST_BIT	(1UL<<3)
 #define SECTION_MAP_MASK_4_12	(~(SECTION_IS_ONLINE-1))
-- 
2.14.1




More information about the kexec mailing list