diff options
| author | Andrew Morton <akpm@osdl.org> | 2004-03-07 22:42:37 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2004-03-07 22:42:37 -0800 |
| commit | bc3d0059577fa9c59b2188e649e2a109b3fdde95 (patch) | |
| tree | 9968c9d3623501e71e097cde21173fda402a13f5 /include/linux | |
| parent | ecad96d53e6056d1bc2c1badec28b4ac68503fe9 (diff) | |
[PATCH] vma corruption fix
From: Hugh Dickins <hugh@veritas.com>
Fixes bugzilla #2219
fork's dup_mmap leaves child mm_rb as copied from parent mm while doing all
the copy_page_ranges, and then calls build_mmap_rb without holding
page_table_lock.
try_to_unmap_one's find_vma (holding page_table_lock not mmap_sem) coming
on another cpu may cause mm mayhem. It may leave the child's mmap_cache
pointing to a vma of the parent mm.
When the parent exits and the child faults, quite what happens rather
depends on what junk then inhabits vm_page_prot, which gets set in the page
table, with page_add_rmap adding the ptep, but junk pte likely to fail the
tests for page_remove_rmap.
Eventually the child exits, the page table is freed and try_to_unmap_one
oopses on null ptep_to_mm (but in a kernel with rss limiting, usually
page_referenced hits the null ptep_to_mm first).
This took me days and days to unravel! Big thanks to Matthieu for
reporting it with a good test case.
Diffstat (limited to 'include/linux')
| -rw-r--r-- | include/linux/mm.h | 3 |
1 files changed, 2 insertions, 1 deletions
diff --git a/include/linux/mm.h b/include/linux/mm.h index d9c541550efd..da8873610af3 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -530,7 +530,8 @@ extern void si_meminfo_node(struct sysinfo *val, int nid); /* mmap.c */ extern void insert_vm_struct(struct mm_struct *, struct vm_area_struct *); -extern void build_mmap_rb(struct mm_struct *); +extern void __vma_link_rb(struct mm_struct *, struct vm_area_struct *, + struct rb_node **, struct rb_node *); extern void exit_mmap(struct mm_struct *); extern unsigned long get_unmapped_area(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); |
