linux/mm
Dmitry Adamushko bdb2192851 slub: Fix use-after-preempt of per-CPU data structure
Vegard Nossum reported a crash in kmem_cache_alloc():

	BUG: unable to handle kernel paging request at da87d000
	IP: [<c01991c7>] kmem_cache_alloc+0xc7/0xe0
	*pde = 28180163 *pte = 1a87d160
	Oops: 0002 [#1] PREEMPT SMP DEBUG_PAGEALLOC
	Pid: 3850, comm: grep Not tainted (2.6.26-rc9-00059-gb190333 #5)
	EIP: 0060:[<c01991c7>] EFLAGS: 00210203 CPU: 0
	EIP is at kmem_cache_alloc+0xc7/0xe0
	EAX: 00000000 EBX: da87c100 ECX: 1adad71a EDX: 6b6b6b6b
	ESI: 00200282 EDI: da87d000 EBP: f60bfe74 ESP: f60bfe54
	DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068

and analyzed it:

  "The register %ecx looks innocent but is very important here. The disassembly:

       mov    %edx,%ecx
       shr    $0x2,%ecx
       rep stos %eax,%es:(%edi) <-- the fault

   So %ecx has been loaded from %edx... which is 0x6b6b6b6b/POISON_FREE.
   (0x6b6b6b6b >> 2 == 0x1adadada.)

   %ecx is the counter for the memset, from here:

       memset(object, 0, c->objsize);

  i.e. %ecx was loaded from c->objsize, so "c" must have been freed.
  Where did "c" come from? Uh-oh...

       c = get_cpu_slab(s, smp_processor_id());

  This looks like it has very much to do with CPU hotplug/unplug. Is
  there a race between SLUB/hotplug since the CPU slab is used after it
  has been freed?"

Good analysis.

Yeah, it's possible that a caller of kmem_cache_alloc() -> slab_alloc()
can be migrated on another CPU right after local_irq_restore() and
before memset().  The inital cpu can become offline in the mean time (or
a migration is a consequence of the CPU going offline) so its
'kmem_cache_cpu' structure gets freed ( slab_cpuup_callback).

At some point of time the caller continues on another CPU having an
obsolete pointer...

Signed-off-by: Dmitry Adamushko <dmitry.adamushko@gmail.com>
Reported-by: Vegard Nossum <vegard.nossum@gmail.com>
Acked-by: Ingo Molnar <mingo@elte.hu>
Cc: stable@kernel.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-07-10 15:18:50 -07:00
..
allocpercpu.c Christoph has moved 2008-07-04 10:40:04 -07:00
backing-dev.c
bootmem.c Add return value to reserve_bootmem_node() 2008-06-21 11:25:10 -07:00
bounce.c
dmapool.c
fadvise.c
filemap_xip.c
filemap.c
fremap.c
highmem.c
hugetlb.c hugetlb: fix lockdep error 2008-06-06 11:29:09 -07:00
internal.h
Kconfig
maccess.c
madvise.c
Makefile
memcontrol.c
memory_hotplug.c
memory.c get_user_pages(): fix possible page leak on oom 2008-07-04 10:40:04 -07:00
mempolicy.c mempolicy: mask off internal flags for userspace API 2008-07-04 13:03:05 -07:00
mempool.c
migrate.c Christoph has moved 2008-07-04 10:40:04 -07:00
mincore.c
mlock.c
mmap.c brk: make sys_brk() honor COMPAT_BRK when computing lower bound 2008-06-06 11:29:09 -07:00
mmzone.c
mprotect.c
mremap.c
msync.c
nommu.c nommu: Correct kobjsize() page validity checks. 2008-06-12 07:56:17 -07:00
oom_kill.c
page_alloc.c Do not overwrite nr_zones on !NUMA when initialising zlcache_ptr 2008-07-03 09:22:59 -07:00
page_io.c
page_isolation.c
page-writeback.c
pagewalk.c pagemap: pass mm into pagewalkers 2008-06-12 18:05:41 -07:00
pdflush.c
prio_tree.c
quicklist.c
readahead.c
rmap.c
shmem_acl.c
shmem.c
slab.c Slab: Fix memory leak in fallback_alloc() 2008-06-21 16:51:02 -07:00
slob.c
slub.c slub: Fix use-after-preempt of per-CPU data structure 2008-07-10 15:18:50 -07:00
sparse-vmemmap.c Christoph has moved 2008-07-04 10:40:04 -07:00
sparse.c
swap_state.c
swap.c
swapfile.c
thrash.c
tiny-shmem.c
truncate.c
util.c
vmalloc.c
vmscan.c mm: fix incorrect variable type in do_try_to_free_pages() 2008-06-12 18:05:39 -07:00
vmstat.c