File crash-kmem_cache-downsize.patch of Package crash.4081
From: Dave Anderson <anderson@redhat.com>
Date: Thu Feb 20 15:36:25 2014 -0500
Subject: Fix kmem slab initialization failures
References: bnc#885082
Patch-mainline: v6.0.6
Git-commit: c0b7a74fc13121203810d06d163550436b2d5476
Fix to prevent a possible invocation-time error on Linux 3.7 and
later kernels configured with CONFIG_SLAB, running against vmcore
files filtered with the makedumpfile(8) facility. Without the
patch, the message "crash: page excluded: kernel virtual address:
<address> type: kmem_cache buffer" is immediately followed by
the message "crash: unable to initialize kmem slab cache subsystem".
Because of a kernel data structure name change from "cache_cache" to
"kmem_cache_boot", the crash utility failed to properly downsize
the stored size of the kernel's kmem_cache data structure from the
size indicated by the vmlinux debuginfo data. This in turn could
lead to reading beyond the end of a kmem_cache data structure into
a page of memory that had been excluded from the vmcore. The fix
was also applied to kernels configured with CONFIG_SLUB.
(anderson@redhat.com)
Acked-by: Petr Tesarik <ptesarik@suse.cz>
diff --git a/memory.c b/memory.c
index 17e5d1c..01c5ffe 100644
--- a/memory.c
+++ b/memory.c
@@ -8677,13 +8677,32 @@ static void
kmem_cache_downsize(void)
{
char *cache_buf;
- uint buffer_size;
+ ulong kmem_cache;
+ uint buffer_size, object_size;
int nr_node_ids;
int nr_cpu_ids;
+ if (vt->flags & KMALLOC_SLUB) {
+ if (kernel_symbol_exists("kmem_cache") &&
+ VALID_MEMBER(kmem_cache_objsize) &&
+ try_get_symbol_data("kmem_cache",
+ sizeof(ulong), &kmem_cache) &&
+ readmem(kmem_cache + OFFSET(kmem_cache_objsize),
+ KVADDR, &object_size, sizeof(int),
+ "kmem_cache objsize/object_size", RETURN_ON_ERROR)) {
+ ASSIGN_SIZE(kmem_cache) = object_size;
+ if (CRASHDEBUG(1))
+ fprintf(fp, "\nkmem_cache_downsize: %ld to %ld\n",
+ STRUCT_SIZE("kmem_cache"),
+ SIZE(kmem_cache));
+ }
+ return;
+ }
+
if ((THIS_KERNEL_VERSION < LINUX(2,6,22)) ||
!(vt->flags & PERCPU_KMALLOC_V2_NODES) ||
- !kernel_symbol_exists("cache_cache") ||
+ (!kernel_symbol_exists("cache_cache") &&
+ !kernel_symbol_exists("kmem_cache_boot")) ||
(!MEMBER_EXISTS("kmem_cache", "buffer_size") &&
!MEMBER_EXISTS("kmem_cache", "size"))) {
return;
@@ -8691,7 +8710,28 @@ kmem_cache_downsize(void)
if (vt->flags & NODELISTS_IS_PTR) {
/*
- * kmem_cache.array[] is actually sized by
+ * More recent kernels have kmem_cache.array[] sized
+ * by the number of cpus plus the number of nodes.
+ */
+ if (kernel_symbol_exists("kmem_cache_boot") &&
+ MEMBER_EXISTS("kmem_cache", "object_size") &&
+ readmem(symbol_value("kmem_cache_boot") +
+ MEMBER_OFFSET("kmem_cache", "object_size"),
+ KVADDR, &object_size, sizeof(int),
+ "kmem_cache_boot object_size", RETURN_ON_ERROR))
+ ASSIGN_SIZE(kmem_cache_s) = object_size;
+ else if (kernel_symbol_exists("cache_cache") &&
+ MEMBER_EXISTS("kmem_cache", "object_size") &&
+ readmem(symbol_value("cache_cache") +
+ MEMBER_OFFSET("kmem_cache", "object_size"),
+ KVADDR, &object_size, sizeof(int),
+ "cache_cache object_size", RETURN_ON_ERROR))
+ ASSIGN_SIZE(kmem_cache_s) = object_size;
+ else
+ object_size = 0;
+
+ /*
+ * Older kernels have kmem_cache.array[] sized by
* the number of cpus; real value is nr_cpu_ids,
* but fallback is kt->cpus.
*/
@@ -8702,10 +8742,12 @@ kmem_cache_downsize(void)
nr_cpu_ids = kt->cpus;
ARRAY_LENGTH(kmem_cache_s_array) = nr_cpu_ids;
- ASSIGN_SIZE(kmem_cache_s) = OFFSET(kmem_cache_s_array) +
- sizeof(ulong) * nr_cpu_ids;
+
+ if (!object_size)
+ ASSIGN_SIZE(kmem_cache_s) = OFFSET(kmem_cache_s_array) +
+ sizeof(ulong) * nr_cpu_ids;
if (CRASHDEBUG(1))
- fprintf(fp, "kmem_cache_downsize: %ld to %ld\n",
+ fprintf(fp, "\nkmem_cache_downsize: %ld to %ld\n",
STRUCT_SIZE("kmem_cache"), SIZE(kmem_cache_s));
return;
}
@@ -8713,7 +8755,7 @@ kmem_cache_downsize(void)
cache_buf = GETBUF(SIZE(kmem_cache_s));
if (!readmem(symbol_value("cache_cache"), KVADDR, cache_buf,
- SIZE(kmem_cache_s), "kmem_cache buffer", FAULT_ON_ERROR)) {
+ SIZE(kmem_cache_s), "kmem_cache buffer", RETURN_ON_ERROR)) {
FREEBUF(cache_buf);
return;
}
@@ -8741,8 +8783,7 @@ kmem_cache_downsize(void)
if (CRASHDEBUG(1)) {
fprintf(fp,
- "\nkmem_cache_downsize: SIZE(kmem_cache_s): %ld "
- "cache_cache.buffer_size: %d\n",
+ "\nkmem_cache_downsize: %ld to %d\n",
STRUCT_SIZE("kmem_cache"), buffer_size);
fprintf(fp,
"kmem_cache_downsize: nr_node_ids: %ld\n",
@@ -16790,6 +16831,8 @@ kmem_cache_init_slub(void)
"kmem_cache_init_slub: numnodes: %d without CONFIG_NUMA\n",
vt->numnodes);
+ kmem_cache_downsize();
+
vt->cpu_slab_type = MEMBER_TYPE("kmem_cache", "cpu_slab");
vt->flags |= KMEM_CACHE_INIT;