futex: Fix uninterruptble loop due to gate_area
Subsystems (3)
PATCH 1/1 futex: Fix uninterruptble loop due to gate_area
Subject: futex: Fix uninterruptble loop due to gate_area
From: Peter Zijlstra <[email protected]>
Date: Fri Dec 02 14:12:06 CET 2011
It was found (by Sasha) that if you use a futex located in the gate area we get stuck in an uninterruptible infinite loop, much like the ZERO_PAGE issue.
While looking at this problem, I realized you'll get into similar trouble when hitting any install_special_pages() mapping. The solution chosen was not to modify special_mapping_fault() to install a non-zero page->mapping because that might lead to issues when freeing these pages. Instead do a find_vma() when we find we're again in the !mapping branch.
--- include/linux/mm.h | 1 + kernel/futex.c | 40 +++++++++++++++++++++++++++++++++++----- mm/mmap.c | 5 +++++ 3 files changed, 41 insertions(+), 5 deletions(-)
include/linux/mm.h +1 -0
@@ -1394,6 +1394,7 @@ extern int may_expand_vm(struct mm_strucextern int install_special_mapping (struct mm_struct *mm,unsigned long addr,unsigned long len,unsigned long flags,struct page **pages); +extern bool is_special_mapping (struct vm_area_struct *vma);extern unsigned long get_unmapped_area (struct file *,unsigned long ,unsigned long ,unsigned long ,unsigned long );
kernel/futex.c +35 -5
@@ -59,6 +59,7 @@#include <linux/magic.h> #include <linux/pid.h> #include <linux/nsproxy.h> +#include <linux/mm.h> #include <asm/futex.h>
@@ -236,7 +237,7 @@ get_futex_key(u32 __user *uaddr, int fshunsigned long address = (unsigned long )uaddr;struct mm_struct *mm = current->mm;struct page *page, *page_head; -int err, ro =0 ; +int err, ro =0 , no_mapping_tries =0 ;/* * The futex address must be "naturally" aligned.
@@ -317,13 +318,42 @@ get_futex_key(u32 __user *uaddr, int fshif (!page_head->mapping) {unlock_page (page_head);put_page (page_head); +/* -* ZERO_PAGE pages don't have a mapping. Avoid a busy loop -* trying to find one. RW mapping would have COW'd (and thus -* have a mapping) so this page is RO and won't ever change. -*/ +* ZERO_PAGE pages don't have a mapping. Avoid a busy loop +* trying to find one. RW mapping would have COW'd (and thus +* have a mapping) so this page is RO and won't ever change. +*/ if ((page_head ==ZERO_PAGE (address)))return -EFAULT ; + +/* +* Similar problem for the gate area. +*/ +if (in_gate_area (mm, address)) +return -EFAULT ; + +/* +* There is a special class of pages that will have no mapping +* and yet is perfectly valid and not going anywhere. These +* are the pages from install_special_mapping(). Since looking +* up the vma is expensive, don't do so on the first go round. +*/ +if (no_mapping_tries) { +struct vm_area_struct *vma; + + err =0 ; +down_read (&mm->mmap_sem); + vma =find_vma (mm, address); +if (vma &&is_special_mapping (vma)) + err = -EFAULT ; +up_read (&mm->mmap_sem); + +if (err) +return err; + } + + ++no_mapping_tries; goto again; }
mm/mmap.c +5 -0
@@ -2479,6 +2479,11 @@ int install_special_mapping(struct mm_streturn ret; } +bool is_special_mapping (struct vm_area_struct *vma) +{ +return vma->vm_ops == &special_mapping_vmops; +} +static DEFINE_MUTEX (mm_all_locks_mutex);static void vm_lock_anon_vma (struct mm_struct *mm,struct anon_vma *anon_vma)
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
[tip:core/urgent] futex: Fix uninterruptible loop due to gate_area
Commit-ID: ab69f41ef93d98d27402440039f805585e5447ac Gitweb: http://git.kernel.org/tip/ab69f41ef93d98d27402440039f805585e5447ac Author: Peter Zijlstra <[email protected]> AuthorDate: Fri, 2 Dec 2011 14:12:06 +0100 Committer: Thomas Gleixner <[email protected]> CommitDate: Wed, 21 Dec 2011 23:59:17 +0100
futex: Fix uninterruptible loop due to gate_area
It was found (by Sasha) that if you use a futex located in the gate area we get stuck in an uninterruptible infinite loop, much like the ZERO_PAGE issue.
While looking at this problem, I realized you'll get into similar trouble when hitting any install_special_pages() mapping. The solution chosen was not to modify special_mapping_fault() to install a non-zero page->mapping because that might lead to issues when freeing these pages. Instead do a find_vma() when we find we're again in the !mapping branch.
--- include/linux/mm.h | 1 + kernel/futex.c | 40 +++++++++++++++++++++++++++++++++++----- mm/mmap.c | 5 +++++ 3 files changed, 41 insertions(+), 5 deletions(-)
include/linux/mm.h +1 -0
@@ -1395,6 +1395,7 @@ extern int may_expand_vm(struct mm_struct *mm, unsigned long npages);extern int install_special_mapping (struct mm_struct *mm,unsigned long addr,unsigned long len,unsigned long flags,struct page **pages); +extern bool is_special_mapping (struct vm_area_struct *vma);extern unsigned long get_unmapped_area (struct file *,unsigned long ,unsigned long ,unsigned long ,unsigned long );
kernel/futex.c +35 -5
@@ -59,6 +59,7 @@#include <linux/magic.h> #include <linux/pid.h> #include <linux/nsproxy.h> +#include <linux/mm.h> #include <asm/futex.h>
@@ -236,7 +237,7 @@ get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key, int rw)unsigned long address = (unsigned long )uaddr;struct mm_struct *mm = current->mm;struct page *page, *page_head; -int err, ro =0 ; +int err, ro =0 , no_mapping_tries =0 ;/* * The futex address must be "naturally" aligned.
@@ -317,13 +318,42 @@ again:if (!page_head->mapping) {unlock_page (page_head);put_page (page_head); +/* -* ZERO_PAGE pages don't have a mapping. Avoid a busy loop -* trying to find one. RW mapping would have COW'd (and thus -* have a mapping) so this page is RO and won't ever change. -*/ +* ZERO_PAGE pages don't have a mapping. Avoid a busy loop +* trying to find one. RW mapping would have COW'd (and thus +* have a mapping) so this page is RO and won't ever change. +*/ if ((page_head ==ZERO_PAGE (address)))return -EFAULT ; + +/* +* Similar problem for the gate area. +*/ +if (in_gate_area (mm, address)) +return -EFAULT ; + +/* +* There is a special class of pages that will have no mapping +* and yet is perfectly valid and not going anywhere. These +* are the pages from install_special_mapping(). Since looking +* up the vma is expensive, don't do so on the first go round. +*/ +if (no_mapping_tries) { +struct vm_area_struct *vma; + + err =0 ; +down_read (&mm->mmap_sem); + vma =find_vma (mm, address); +if (vma &&is_special_mapping (vma)) + err = -EFAULT ; +up_read (&mm->mmap_sem); + +if (err) +return err; + } + + ++no_mapping_tries; goto again; }
mm/mmap.c +5 -0
@@ -2479,6 +2479,11 @@ out:return ret; } +bool is_special_mapping (struct vm_area_struct *vma) +{ +return vma->vm_ops == &special_mapping_vmops; +} +static DEFINE_MUTEX (mm_all_locks_mutex);static void vm_lock_anon_vma (struct mm_struct *mm,struct anon_vma *anon_vma)
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>