Index: linux-2.6.21-rc1/mm/slub.c =================================================================== --- linux-2.6.21-rc1.orig/mm/slub.c 2007-02-27 22:51:43.000000000 -0800 +++ linux-2.6.21-rc1/mm/slub.c 2007-02-27 23:01:30.000000000 -0800 @@ -234,7 +234,7 @@ static void object_err(struct kmem_cache { u8 *addr = page_address(page); - printk(KERN_ERR "*** SLUB: %s failure in %s@%p Slab %p\n", + printk(KERN_ERR "*** SLUB: %s in %s@%p Slab %p\n", reason, s->name, object, page); printk(KERN_ERR " offset=%ld flags=%04lx inuse=%d freelist=%p\n", object - addr, page->flags, page->inuse, page->freelist); @@ -276,6 +276,24 @@ static int check_bytes(u8 *start, unsign return 1; } + +static int check_valid_pointer(struct kmem_cache *s, struct page *page, + void *object) +{ + void *base; + + if (!object) + return 1; + + base = page_address(page); + if (object < base || object >= base + s->objects * s->size || + (object - base) % s->size) { + return 0; + } + + return 1; +} + /* * Object layout: * @@ -296,8 +314,6 @@ static int check_object(struct kmem_cach { u8 *p = object; u8 *endobject = object + s->objsize; - u8 *page_addr; - u8 *fp; /* Offset of first byte after free pointer */ unsigned long off = s->inuse; @@ -314,15 +330,15 @@ static int check_object(struct kmem_cach active ? RED_ACTIVE : RED_INACTIVE, s->inuse - s->objsize)) { object_err(s, page, object, - active ? "Redzone Active" : - "Redzone Inactive"); + active ? "Redzone Active check fails" : + "Redzone Inactive check fails"); return 0; } } else if ((s->flags & SLAB_POISON) && s->objsize < s->inuse && !check_bytes(endobject, POISON_INUSE, s->inuse - s->objsize)) - object_err(s, page, p, "Alignment Filler Check"); + object_err(s, page, p, "Alignment Filler check fails"); if (s->flags & SLAB_POISON) { if (!active && (!check_bytes(p, POISON_FREE, s->objsize - 1) || @@ -333,17 +349,19 @@ static int check_object(struct kmem_cach if (s->size > off && !check_bytes(p + off, POISON_INUSE, s->size - off)) object_err(s, page, p, - "Interobject Filler Check"); + "Interobject Filler check fails"); } - /* Check free pointer validity */ - fp = get_freepointer(s, p); - page_addr = page_address(page); + if (s->offset == 0 && active) + /* + * Object and freepointer overlap. Cannot check + * if object is allocated. + */ + return 1; - if (fp && (fp < page_addr || - fp >= page_addr + (PAGE_SIZE << s->order) || - ((fp - page_addr) % s->size))) { - object_err(s, page, p, "Freepointer corrupted"); + /* Check free pointer validity */ + if (!check_valid_pointer(s, page, get_freepointer(s, p))) { + object_err(s, page, p, "Freepointer corrupt"); /* * No choice but to zap it. This may cause * another error because the object count @@ -355,32 +373,6 @@ static int check_object(struct kmem_cach return 1; } -static int check_valid_pointer(struct kmem_cache *s, struct page *page, - void *object, void *origin) -{ - void *base = page_address(page); - - if (!object) - return 1; - - if (object < base || object >= base + s->objects * s->size) { - printk(KERN_CRIT "SLUB: %s size %d: pointer %p->%p not in" - " range (%p-%p) in page %p\n", s->name, s->size, - origin, object, base, base + s->objects * s->size, - page); - return 0; - } - - if ((object - base) % s->size) { - printk(KERN_CRIT "SLUB: %s size %d: pointer %p->%p\n" - "does not properly point" - "to an object in slab %p\n", - s->name, s->size, origin, object, page); - return 0; - } - return 1; -} - static int check_slab(struct kmem_cache *s, struct page *page) { if (!PageSlab(page)) { @@ -415,27 +407,42 @@ static int check_slab(struct kmem_cache static int on_freelist(struct kmem_cache *s, struct page *page, void *search) { int nr = 0; - void **object = page->freelist; - void *origin = &page->lru; + void *fp = page->freelist; + void *object = NULL; if (s->objects == 1) return 0; - while (object && nr <= s->objects) { - if (object == search) + while (fp && nr <= s->objects) { + if (fp == search) return 1; - if (!check_valid_pointer(s, page, object, origin)) - return 0; - origin = object; - object = object[s->offset]; + if (!check_valid_pointer(s, page, fp)) { + if (object) { + object_err(s, page, object, "Freechain corrupt"); + set_freepointer(s, object, NULL); + break; + } else { + printk(KERN_ERR "SLUB: %s slab %p freepointer %p corrupted.\n", + s->name, page, fp); + dump_stack(); + page->freelist = NULL; + page->inuse = s->objects; + return 0; + } + break; + } + object = fp; + fp = get_freepointer(s, object); nr++; } - if (page->inuse != s->objects - nr) + if (page->inuse != s->objects - nr) { printk(KERN_CRIT "slab %s: page %p wrong object count." " counter is %d but counted were %d\n", s->name, page, page->inuse, s->objects - nr); + page->inuse = s->objects - nr; + } return 0; } @@ -451,11 +458,16 @@ static void alloc_object_checks(struct k if (!check_slab(s, page)) goto bad; - if (object && !on_freelist(s, page, object)) - goto bad; + if (object && !on_freelist(s, page, object)) { + printk(KERN_ERR "SLAB: %s Object %p@%p already allocated.\n", + s->name, object, page); + goto dump; + } - if (!check_valid_pointer(s, page, object, object)) - goto bad; + if (!check_valid_pointer(s, page, object)) { + object_err(s, page, object, "Freelist Pointer check fails"); + goto dump; + } if (!object) return; @@ -472,6 +484,8 @@ static void alloc_object_checks(struct k dump_stack(); } return; +dump: + dump_stack(); bad: /* Mark slab full */ page->inuse = s->objects; @@ -480,14 +494,24 @@ bad: static int free_object_checks(struct kmem_cache *s, struct page *page, void *object) { - if (!check_slab(s, page)) - return 0; + if (!check_slab(s, page)) { + goto fail; + } - if (!check_valid_pointer(s, page, object, NULL)) - goto dumpret; + if (!check_valid_pointer(s, page, object)) { + printk(KERN_ERR "SLUB: %s slab %p invalid free pointer %p\n", + s->name, page, object); + goto fail; + } + + if (on_freelist(s, page, object)) { + printk(KERN_CRIT "SLUB: %s slab %p object %p already free.\n", + s->name, page, object); + goto fail; + } if (!check_object(s, page, object, 1)) - goto dumpret; + return 0; if (unlikely(s != page->slab)) { if (!PageSlab(page)) @@ -495,33 +519,29 @@ static int free_object_checks(struct kme "free object(%p) outside of slab.\n", s->name, s->size, object); else - if (!page->slab) { + if (!page->slab) printk(KERN_CRIT "slab_free : no slab(NULL) for object %p.\n", object); - goto dumpret; - } else + else printk(KERN_CRIT "slab_free %s(%d): object at %p" " belongs to slab %s(%d)\n", s->name, s->size, object, page->slab->name, page->slab->size); - goto dumpret; + goto fail; } if (s->flags & SLAB_TRACE) { printk("SLUB-Trace %s free object=%p slab=%p" "inuse=%d freelist=%p\n", s->name, object, page, page->inuse, page->freelist); - print_section("SLUB-Trace", object, min(s->objsize, 128)); + print_section("SLUB-Trace", object, s->objsize); dump_stack(); } init_object(s, object, 0); return 1; - -dumpret: +fail: dump_stack(); - printk(KERN_CRIT "***** Trying to continue by not " - "freeing object.\n"); return 0; } @@ -646,7 +666,7 @@ static void __free_slab(struct kmem_cach if ((s->flags & SLAB_POISON) && check_bytes(end, POISON_INUSE, (PAGE_SIZE << s->order) - (end - start))) - object_err(s, page, p, "Slabend filler"); + object_err(s, page, p, "Slab End fill check fails"); } mod_zone_page_state(page_zone(page), @@ -961,15 +981,15 @@ static __always_inline void *__slab_allo if (unlikely(node != -1 && page_to_nid(page) != node)) goto another_slab; redo: - if (unlikely(!page->freelist)) + object = page->freelist; + if (unlikely(!object)) goto another_slab; if (unlikely(PageError(page))) { - alloc_object_checks(s, page, page->freelist); + alloc_object_checks(s, page, object); if (s->flags & SLAB_STORE_USER) set_tracking(s, object, 0); } page->inuse++; - object = page->freelist; page->freelist = object[page->offset]; SetPageReferenced(page); slab_unlock(page); @@ -1052,32 +1072,24 @@ void kmem_cache_free(struct kmem_cache * local_irq_save(flags); - if (unlikely(PageError(page))) { - if (s->objects == 1) - goto single_object_slab; - if (!free_object_checks(s, page, x)) - goto out; - if (s->flags & SLAB_STORE_USER) - set_tracking(s, object, 1); - } + if (unlikely(PageError(page)) && s->objects == 1) + goto single_object_slab; slab_lock(page); if (unlikely(PageError(page))) { - if (on_freelist(s, page, object)) - goto double_free; + if (!free_object_checks(s, page, x)) + goto out_unlock; + if (s->flags & SLAB_STORE_USER) + set_tracking(s, object, 1); } prior = object[page->offset] = page->freelist; page->freelist = object; page->inuse--; - if (likely(PageActive(page) || (page->inuse && prior))) { -out_unlock: - slab_unlock(page); - local_irq_restore(flags); - return; - } + if (likely(PageActive(page) || (page->inuse && prior))) + goto out_unlock; if (!prior) { /* @@ -1095,16 +1107,12 @@ out_unlock: slab_unlock(page); single_object_slab: discard_slab(s, page); -out: local_irq_restore(flags); return; -double_free: - printk(KERN_CRIT "slab_free %s: object %p already free.\n", - s->name, object); - dump_stack(); - goto out_unlock; - +out_unlock: + slab_unlock(page); + local_irq_restore(flags); } EXPORT_SYMBOL(kmem_cache_free); @@ -1317,7 +1325,7 @@ int kmem_cache_open(struct kmem_cache *s flags &= ~SLAB_POISON; } - s->name = name; + s->name = kstrdup(name, gfpflags); s->ctor = ctor; s->dtor = dtor; s->objsize = size;