Commit 86c662db authored by Andrew Morton's avatar Andrew Morton Committed by David S. Miller

[PATCH] slab: hexdump for check_poison

From: Manfred Spraul <manfred@colorfullife.com>

The patch is designed improve the diagnostics which are presented when the
slab memory poison detector triggers.


check_poison_obj checks for write accesses after kfree by comparing the
object contents with the poison value.  The current implementation contains
several flaws:

- it accepts both POISON_BEFORE and POISON_AFTER.  check_poison_obj is
  only called with POISON_AFTER poison bytes.  Fix: only accept
  POISON_AFTER.

- the output is unreadable.  Fix: use hexdump.

- if a large objects is corrupted, then the relevant lines can scroll of
  the screen/dmesg buffer.  Fix: line limit.

- it can access addresses behind the end of the object, which can oops
  with CONFIG_DEBUG_PAGEALLOC.  Fix: bounds checks.

Additionally, the patch contains the following changes:

- rename POISON_BEFORE and POISON_AFTER to POISON_FREE and POISON_INUSE.
  The old names are ambiguous.

- use the new hexdump object function in ptrinfo.

- store_stackinfo was called with wrong parameters: it should store
  caller, i.e.  __builtin_return_address(0), not POISON_AFTER in the
  object.

- dump both the object before and after the corrupted one, not just the
  one after.

Example output:
<<<
Slab corruption: start=194e708c, len=2048
Redzone: 0x5a2cf071/0x5a2cf071.
Last user: [<02399d7c>](dummy_init_module+0x1c/0xb0)
010: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 7b
030: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 63
Prev obj: start=194e6880, len=2048
Redzone: 0x5a2cf071/0x5a2cf071.
Last user: [<00000000>](0x0)
000: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b
010: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b
<<<
parent c03544dd
...@@ -357,8 +357,8 @@ struct kmem_cache_s { ...@@ -357,8 +357,8 @@ struct kmem_cache_s {
#define RED_ACTIVE 0x170FC2A5UL /* when obj is active */ #define RED_ACTIVE 0x170FC2A5UL /* when obj is active */
/* ...and for poisoning */ /* ...and for poisoning */
#define POISON_BEFORE 0x5a /* for use-uninitialised poisoning */ #define POISON_INUSE 0x5a /* for use-uninitialised poisoning */
#define POISON_AFTER 0x6b /* for use-after-free poisoning */ #define POISON_FREE 0x6b /* for use-after-free poisoning */
#define POISON_END 0xa5 /* end-byte of poisoning */ #define POISON_END 0xa5 /* end-byte of poisoning */
/* memory layout of objects: /* memory layout of objects:
...@@ -887,60 +887,105 @@ static void poison_obj(kmem_cache_t *cachep, void *addr, unsigned char val) ...@@ -887,60 +887,105 @@ static void poison_obj(kmem_cache_t *cachep, void *addr, unsigned char val)
*(unsigned char *)(addr+size-1) = POISON_END; *(unsigned char *)(addr+size-1) = POISON_END;
} }
static void *scan_poisoned_obj(unsigned char* addr, unsigned int size) static void dump_line(char *data, int offset, int limit)
{ {
unsigned char *end; int i;
printk(KERN_ERR "%03x:", offset);
end = addr + size - 1; for (i=0;i<limit;i++) {
printk(" %02x", (unsigned char)data[offset+i]);
}
printk("\n");
}
#endif
static void print_objinfo(kmem_cache_t *cachep, void *objp, int lines)
{
#if DEBUG
int i, size;
char *realobj;
for (; addr < end; addr++) { if (cachep->flags & SLAB_RED_ZONE) {
if (*addr != POISON_BEFORE && *addr != POISON_AFTER) printk(KERN_ERR "Redzone: 0x%lx/0x%lx.\n",
return addr; *dbg_redzone1(cachep, objp),
*dbg_redzone2(cachep, objp));
} }
if (*addr != POISON_END)
return addr; if (cachep->flags & SLAB_STORE_USER) {
return NULL; printk(KERN_ERR "Last user: [<%p>]", *dbg_userword(cachep, objp));
print_symbol("(%s)", (unsigned long)*dbg_userword(cachep, objp));
printk("\n");
}
realobj = (char*)objp+obj_dbghead(cachep);
size = cachep->objsize;
for (i=0; i<size && lines;i+=16, lines--) {
int limit;
limit = 16;
if (i+limit > size)
limit = size-i;
dump_line(realobj, i, limit);
}
#endif
} }
#if DEBUG
static void check_poison_obj(kmem_cache_t *cachep, void *objp) static void check_poison_obj(kmem_cache_t *cachep, void *objp)
{ {
void *end; char *realobj;
void *realobj; int size, i;
int size = obj_reallen(cachep); int lines = 0;
realobj = objp+obj_dbghead(cachep); realobj = (char*)objp+obj_dbghead(cachep);
size = obj_reallen(cachep);
end = scan_poisoned_obj(realobj, size);
if (end) { for (i=0;i<size;i++) {
int s; char exp = POISON_FREE;
printk(KERN_ERR "Slab corruption: start=%p, expend=%p, " if (i == size-1)
"problemat=%p\n", realobj, realobj+size-1, end); exp = POISON_END;
if (cachep->flags & SLAB_STORE_USER) { if (realobj[i] != exp) {
printk(KERN_ERR "Last user: [<%p>]", *dbg_userword(cachep, objp)); int limit;
print_symbol("(%s)", (unsigned long)*dbg_userword(cachep, objp)); /* Mismatch ! */
printk("\n"); /* Print header */
if (lines == 0) {
printk(KERN_ERR "Slab corruption: start=%p, len=%d\n",
realobj, size);
print_objinfo(cachep, objp, 0);
}
/* Hexdump the affected line */
i = (i/16)*16;
limit = 16;
if (i+limit > size)
limit = size-i;
dump_line(realobj, i, limit);
i += 16;
lines++;
/* Limit to 5 lines */
if (lines > 5)
break;
} }
printk(KERN_ERR "Data: "); }
for (s = 0; s < size; s++) { if (lines != 0) {
if (((char*)realobj)[s] == POISON_BEFORE) /* Print some data about the neighboring objects, if they
printk("."); * exist:
else if (((char*)realobj)[s] == POISON_AFTER) */
printk("*"); struct slab *slabp = GET_PAGE_SLAB(virt_to_page(objp));
else int objnr;
printk("%02X ", ((unsigned char*)realobj)[s]);
objnr = (objp-slabp->s_mem)/cachep->objsize;
if (objnr) {
objp = slabp->s_mem+(objnr-1)*cachep->objsize;
realobj = (char*)objp+obj_dbghead(cachep);
printk(KERN_ERR "Prev obj: start=%p, len=%d\n",
realobj, size);
print_objinfo(cachep, objp, 2);
} }
printk("\n"); if (objnr+1 < cachep->num) {
printk(KERN_ERR "Next: "); objp = slabp->s_mem+(objnr+1)*cachep->objsize;
for (; s < size + 32; s++) { realobj = (char*)objp+obj_dbghead(cachep);
if (((char*)realobj)[s] == POISON_BEFORE) printk(KERN_ERR "Next obj: start=%p, len=%d\n",
printk("."); realobj, size);
else if (((char*)realobj)[s] == POISON_AFTER) print_objinfo(cachep, objp, 2);
printk("*");
else
printk("%02X ", ((unsigned char*)realobj)[s]);
} }
printk("\n");
slab_error(cachep, "object was modified after freeing");
} }
} }
#endif #endif
...@@ -1495,7 +1540,7 @@ static void cache_init_objs (kmem_cache_t * cachep, ...@@ -1495,7 +1540,7 @@ static void cache_init_objs (kmem_cache_t * cachep,
#if DEBUG #if DEBUG
/* need to poison the objs? */ /* need to poison the objs? */
if (cachep->flags & SLAB_POISON) if (cachep->flags & SLAB_POISON)
poison_obj(cachep, objp, POISON_BEFORE); poison_obj(cachep, objp, POISON_FREE);
if (cachep->flags & SLAB_STORE_USER) if (cachep->flags & SLAB_STORE_USER)
*dbg_userword(cachep, objp) = NULL; *dbg_userword(cachep, objp) = NULL;
...@@ -1714,13 +1759,13 @@ static inline void *cache_free_debugcheck (kmem_cache_t * cachep, void * objp, v ...@@ -1714,13 +1759,13 @@ static inline void *cache_free_debugcheck (kmem_cache_t * cachep, void * objp, v
if (cachep->flags & SLAB_POISON) { if (cachep->flags & SLAB_POISON) {
#ifdef CONFIG_DEBUG_PAGEALLOC #ifdef CONFIG_DEBUG_PAGEALLOC
if ((cachep->objsize % PAGE_SIZE) == 0 && OFF_SLAB(cachep)) { if ((cachep->objsize % PAGE_SIZE) == 0 && OFF_SLAB(cachep)) {
store_stackinfo(cachep, objp, POISON_AFTER); store_stackinfo(cachep, objp, (unsigned long)caller);
kernel_map_pages(virt_to_page(objp), cachep->objsize/PAGE_SIZE, 0); kernel_map_pages(virt_to_page(objp), cachep->objsize/PAGE_SIZE, 0);
} else { } else {
poison_obj(cachep, objp, POISON_AFTER); poison_obj(cachep, objp, POISON_FREE);
} }
#else #else
poison_obj(cachep, objp, POISON_AFTER); poison_obj(cachep, objp, POISON_FREE);
#endif #endif
} }
#endif #endif
...@@ -1877,7 +1922,7 @@ cache_alloc_debugcheck_after(kmem_cache_t *cachep, ...@@ -1877,7 +1922,7 @@ cache_alloc_debugcheck_after(kmem_cache_t *cachep,
#else #else
check_poison_obj(cachep, objp); check_poison_obj(cachep, objp);
#endif #endif
poison_obj(cachep, objp, POISON_BEFORE); poison_obj(cachep, objp, POISON_INUSE);
} }
if (cachep->flags & SLAB_STORE_USER) if (cachep->flags & SLAB_STORE_USER)
*dbg_userword(cachep, objp) = caller; *dbg_userword(cachep, objp) = caller;
...@@ -2868,14 +2913,7 @@ void ptrinfo(unsigned long addr) ...@@ -2868,14 +2913,7 @@ void ptrinfo(unsigned long addr)
kernel_map_pages(virt_to_page(objp), kernel_map_pages(virt_to_page(objp),
c->objsize/PAGE_SIZE, 1); c->objsize/PAGE_SIZE, 1);
if (c->flags & SLAB_RED_ZONE) print_objinfo(c, objp, 2);
printk("redzone: 0x%lx/0x%lx.\n",
*dbg_redzone1(c, objp),
*dbg_redzone2(c, objp));
if (c->flags & SLAB_STORE_USER)
printk("Last user: %p.\n",
*dbg_userword(c, objp));
} }
spin_unlock_irqrestore(&c->spinlock, flags); spin_unlock_irqrestore(&c->spinlock, flags);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment