• Chris Metcalf's avatar
    hugetlb: fix race condition in hugetlb_fault() · 66aebce7
    Chris Metcalf authored
    The race is as follows:
    
    Suppose a multi-threaded task forks a new process (on cpu A), thus
    bumping up the ref count on all the pages.  While the fork is occurring
    (and thus we have marked all the PTEs as read-only), another thread in
    the original process (on cpu B) tries to write to a huge page, taking an
    access violation from the write-protect and calling hugetlb_cow().  Now,
    suppose the fork() fails.  It will undo the COW and decrement the ref
    count on the pages, so the ref count on the huge page drops back to 1.
    Meanwhile hugetlb_cow() also decrements the ref count by one on the
    original page, since the original address space doesn't need it any
    more, having copied a new page to replace the original page.  This
    leaves the ref count at zero, and when we call unlock_page(), we panic.
    
    	fork on CPU A				fault on CPU B
    	=============				==============
    	...
    	down_write(&parent->mmap_sem);
    	down_write_nested(&child->mmap_sem);
    	...
    	while duplicating vmas
    		if error
    			break;
    	...
    	up_write(&child->mmap_sem);
    	up_write(&parent->mmap_sem);		...
    						down_read(&parent->mmap_sem);
    						...
    						lock_page(page);
    						handle COW
    						page_mapcount(old_page) == 2
    						alloc and prepare new_page
    	...
    	handle error
    	page_remove_rmap(page);
    	put_page(page);
    	...
    						fold new_page into pte
    						page_remove_rmap(page);
    						put_page(page);
    						...
    				oops ==>	unlock_page(page);
    						up_read(&parent->mmap_sem);
    
    The solution is to take an extra reference to the page while we are
    holding the lock on it.
    Signed-off-by: default avatarChris Metcalf <cmetcalf@tilera.com>
    Cc: Hillf Danton <dhillf@gmail.com>
    Cc: Michal Hocko <mhocko@suse.cz>
    Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
    Cc: Hugh Dickins <hughd@google.com>
    Cc: <stable@vger.kernel.org>
    Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
    Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
    66aebce7
hugetlb.c 78.9 KB