Message ID | 1318607325-5762-2-git-send-email-paolo.pisati@canonical.com |
---|---|
State | New |
Headers | show |
On 14.10.2011 16:48, Paolo Pisati wrote: > From: Hugh Dickins <hughd@google.com> > > Andrea Righi reported a case where an exiting task can race against > ksmd::scan_get_next_rmap_item (http://lkml.org/lkml/2011/6/1/742) easily > triggering a NULL pointer dereference in ksmd. > > ksm_scan.mm_slot == &ksm_mm_head with only one registered mm > > CPU 1 (__ksm_exit) CPU 2 (scan_get_next_rmap_item) > list_empty() is false > lock slot == &ksm_mm_head > list_del(slot->mm_list) > (list now empty) > unlock > lock > slot = list_entry(slot->mm_list.next) > (list is empty, so slot is still ksm_mm_head) > unlock > slot->mm == NULL ... Oops > > Close this race by revalidating that the new slot is not simply the list > head again. > > Andrea's test case: > > int main(int argc, char **argv) > { > void *ptr; > > if (posix_memalign(&ptr, getpagesize(), BUFSIZE) < 0) { > perror("posix_memalign"); > exit(1); > } > if (madvise(ptr, BUFSIZE, MADV_MERGEABLE) < 0) { > perror("madvise"); > exit(1); > } > *(char *)NULL = 0; > > return 0; > } > > Reported-by: Andrea Righi <andrea@betterlinux.com> > Tested-by: Andrea Righi <andrea@betterlinux.com> > Cc: Andrea Arcangeli <aarcange@redhat.com> > Signed-off-by: Hugh Dickins <hughd@google.com> > Signed-off-by: Chris Wright <chrisw@sous-sol.org> > Cc: <stable@kernel.org> > Signed-off-by: Andrew Morton <akpm@linux-foundation.org> > Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> > > CVE-2011-2183 > > (cherry picked from commit 2b472611a32a72f4a118c069c2d62a1a3f087afd) > > BugLink: http://bugs.launchpad.net/bugs/869227 > > Signed-off-by: Paolo Pisati <paolo.pisati@canonical.com> > --- > mm/ksm.c | 6 ++++++ > 1 files changed, 6 insertions(+), 0 deletions(-) > > diff --git a/mm/ksm.c b/mm/ksm.c > index 79cd773..2db786a 100644 > --- a/mm/ksm.c > +++ b/mm/ksm.c > @@ -1272,6 +1272,12 @@ static struct rmap_item *scan_get_next_rmap_item(struct page **page) > slot = list_entry(slot->mm_list.next, struct mm_slot, mm_list); > ksm_scan.mm_slot = slot; > spin_unlock(&ksm_mmlist_lock); > + /* > + * Although we tested list_empty() above, a racing __ksm_exit > + * of the last mm on the list may have removed it since then. > + */ > + if (slot == &ksm_mm_head) > + return NULL; > next_mm: > ksm_scan.address = 0; > ksm_scan.rmap_list = &slot->rmap_list;
On Fri, Oct 14, 2011 at 05:48:45PM +0200, Paolo Pisati wrote: > From: Hugh Dickins <hughd@google.com> > > Andrea Righi reported a case where an exiting task can race against > ksmd::scan_get_next_rmap_item (http://lkml.org/lkml/2011/6/1/742) easily > triggering a NULL pointer dereference in ksmd. > > ksm_scan.mm_slot == &ksm_mm_head with only one registered mm > > CPU 1 (__ksm_exit) CPU 2 (scan_get_next_rmap_item) > list_empty() is false > lock slot == &ksm_mm_head > list_del(slot->mm_list) > (list now empty) > unlock > lock > slot = list_entry(slot->mm_list.next) > (list is empty, so slot is still ksm_mm_head) > unlock > slot->mm == NULL ... Oops > > Close this race by revalidating that the new slot is not simply the list > head again. > > Andrea's test case: > > int main(int argc, char **argv) > { > void *ptr; > > if (posix_memalign(&ptr, getpagesize(), BUFSIZE) < 0) { > perror("posix_memalign"); > exit(1); > } > if (madvise(ptr, BUFSIZE, MADV_MERGEABLE) < 0) { > perror("madvise"); > exit(1); > } > *(char *)NULL = 0; > > return 0; > } > > Reported-by: Andrea Righi <andrea@betterlinux.com> > Tested-by: Andrea Righi <andrea@betterlinux.com> > Cc: Andrea Arcangeli <aarcange@redhat.com> > Signed-off-by: Hugh Dickins <hughd@google.com> > Signed-off-by: Chris Wright <chrisw@sous-sol.org> > Cc: <stable@kernel.org> > Signed-off-by: Andrew Morton <akpm@linux-foundation.org> > Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> > > CVE-2011-2183 > > (cherry picked from commit 2b472611a32a72f4a118c069c2d62a1a3f087afd) > > BugLink: http://bugs.launchpad.net/bugs/869227 > > Signed-off-by: Paolo Pisati <paolo.pisati@canonical.com> > --- > mm/ksm.c | 6 ++++++ > 1 files changed, 6 insertions(+), 0 deletions(-) > > diff --git a/mm/ksm.c b/mm/ksm.c > index 79cd773..2db786a 100644 > --- a/mm/ksm.c > +++ b/mm/ksm.c > @@ -1272,6 +1272,12 @@ static struct rmap_item *scan_get_next_rmap_item(struct page **page) > slot = list_entry(slot->mm_list.next, struct mm_slot, mm_list); > ksm_scan.mm_slot = slot; > spin_unlock(&ksm_mmlist_lock); > + /* > + * Although we tested list_empty() above, a racing __ksm_exit > + * of the last mm on the list may have removed it since then. > + */ > + if (slot == &ksm_mm_head) > + return NULL; > next_mm: > ksm_scan.address = 0; > ksm_scan.rmap_list = &slot->rmap_list; > -- > 1.7.5.4 > Acked-by: Herton Ronaldo Krzesinski <herton.krzesinski@canonical.com>
diff --git a/mm/ksm.c b/mm/ksm.c index 79cd773..2db786a 100644 --- a/mm/ksm.c +++ b/mm/ksm.c @@ -1272,6 +1272,12 @@ static struct rmap_item *scan_get_next_rmap_item(struct page **page) slot = list_entry(slot->mm_list.next, struct mm_slot, mm_list); ksm_scan.mm_slot = slot; spin_unlock(&ksm_mmlist_lock); + /* + * Although we tested list_empty() above, a racing __ksm_exit + * of the last mm on the list may have removed it since then. + */ + if (slot == &ksm_mm_head) + return NULL; next_mm: ksm_scan.address = 0; ksm_scan.rmap_list = &slot->rmap_list;