| From 12d0af8817be7d08486d3602ad279db70521189c Mon Sep 17 00:00:00 2001 |
| From: Hugh Dickins <hughd@google.com> |
| Date: Wed, 15 Jun 2011 15:08:58 -0700 |
| Subject: [PATCH] ksm: fix NULL pointer dereference in |
| scan_get_next_rmap_item() |
| |
| commit 2b472611a32a72f4a118c069c2d62a1a3f087afd upstream. |
| |
| 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: |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <sys/mman.h> |
| |
| #define BUFSIZE getpagesize() |
| |
| 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> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| --- |
| mm/ksm.c | 6 ++++++ |
| 1 file changed, 6 insertions(+) |
| |
| diff --git a/mm/ksm.c b/mm/ksm.c |
| index 956880f..5e8e222 100644 |
| --- a/mm/ksm.c |
| +++ b/mm/ksm.c |
| @@ -1270,6 +1270,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.9.6 |
| |