Message ID | 1380112289-19572-1-git-send-email-steve.capper@linaro.org |
---|---|
State | New |
Headers | show |
On Wed, Sep 25, 2013 at 01:31:29PM +0100, Steve Capper wrote: > The memory pinning code in uaccess_with_memcpy.c does not check > for HugeTLB or THP pmds, and will enter an infinite loop should > a __copy_to_user or __clear_user occur against a huge page. This patch causes a build error with certain configuration options: arch/arm/lib/uaccess_with_memcpy.c: In function 'pin_page_for_write': arch/arm/lib/uaccess_with_memcpy.c:57:2: error: implicit declaration of function 'pmd_thp_or_huge' arch/arm/lib/uaccess_with_memcpy.c:60:3: error: implicit declaration of function 'pmd_hugewillfault' found via a randconfig build, configuration: http://www.arm.linux.org.uk/developer/build/file.php?type=config&idx=6265 > --- > arch/arm/include/asm/pgtable-3level.h | 3 +++ > arch/arm/lib/uaccess_with_memcpy.c | 41 ++++++++++++++++++++++++++++++++--- > 2 files changed, 41 insertions(+), 3 deletions(-) As the macros are in pgtable-3level.h, this won't work with any configuration using the 2level header file.
On Fri, Oct 11, 2013 at 10:57:51AM +0100, Russell King - ARM Linux wrote: > On Wed, Sep 25, 2013 at 01:31:29PM +0100, Steve Capper wrote: > > The memory pinning code in uaccess_with_memcpy.c does not check > > for HugeTLB or THP pmds, and will enter an infinite loop should > > a __copy_to_user or __clear_user occur against a huge page. > > This patch causes a build error with certain configuration options: > > arch/arm/lib/uaccess_with_memcpy.c: In function 'pin_page_for_write': > arch/arm/lib/uaccess_with_memcpy.c:57:2: error: implicit declaration of function 'pmd_thp_or_huge' > arch/arm/lib/uaccess_with_memcpy.c:60:3: error: implicit declaration of function 'pmd_hugewillfault' > > found via a randconfig build, configuration: > > http://www.arm.linux.org.uk/developer/build/file.php?type=config&idx=6265 > > > --- > > arch/arm/include/asm/pgtable-3level.h | 3 +++ > > arch/arm/lib/uaccess_with_memcpy.c | 41 ++++++++++++++++++++++++++++++++--- > > 2 files changed, 41 insertions(+), 3 deletions(-) > > As the macros are in pgtable-3level.h, this won't work with any configuration > using the 2level header file. Hi Russell, Apologies, that was careless rebasing on my part. Would you like a fix for this, or should I send a new patch? Thanks,
On Fri, Oct 11, 2013 at 11:11:32AM +0100, Steve Capper wrote: > On Fri, Oct 11, 2013 at 10:57:51AM +0100, Russell King - ARM Linux wrote: > > On Wed, Sep 25, 2013 at 01:31:29PM +0100, Steve Capper wrote: > > > The memory pinning code in uaccess_with_memcpy.c does not check > > > for HugeTLB or THP pmds, and will enter an infinite loop should > > > a __copy_to_user or __clear_user occur against a huge page. > > > > This patch causes a build error with certain configuration options: > > > > arch/arm/lib/uaccess_with_memcpy.c: In function 'pin_page_for_write': > > arch/arm/lib/uaccess_with_memcpy.c:57:2: error: implicit declaration of function 'pmd_thp_or_huge' > > arch/arm/lib/uaccess_with_memcpy.c:60:3: error: implicit declaration of function 'pmd_hugewillfault' > > > > found via a randconfig build, configuration: > > > > http://www.arm.linux.org.uk/developer/build/file.php?type=config&idx=6265 > > > > > --- > > > arch/arm/include/asm/pgtable-3level.h | 3 +++ > > > arch/arm/lib/uaccess_with_memcpy.c | 41 ++++++++++++++++++++++++++++++++--- > > > 2 files changed, 41 insertions(+), 3 deletions(-) > > > > As the macros are in pgtable-3level.h, this won't work with any configuration > > using the 2level header file. > > Hi Russell, > Apologies, that was careless rebasing on my part. > > Would you like a fix for this, or should I send a new patch? I'd prefer a new patch please. Thanks.
diff --git a/arch/arm/include/asm/pgtable-3level.h b/arch/arm/include/asm/pgtable-3level.h index 5689c18..39c54cf 100644 --- a/arch/arm/include/asm/pgtable-3level.h +++ b/arch/arm/include/asm/pgtable-3level.h @@ -206,6 +206,9 @@ static inline pmd_t *pmd_offset(pud_t *pud, unsigned long addr) #define __HAVE_ARCH_PMD_WRITE #define pmd_write(pmd) (!(pmd_val(pmd) & PMD_SECT_RDONLY)) +#define pmd_hugewillfault(pmd) (!pmd_young(pmd) || !pmd_write(pmd)) +#define pmd_thp_or_huge(pmd) (pmd_huge(pmd) || pmd_trans_huge(pmd)) + #ifdef CONFIG_TRANSPARENT_HUGEPAGE #define pmd_trans_huge(pmd) (pmd_val(pmd) && !(pmd_val(pmd) & PMD_TABLE_BIT)) #define pmd_trans_splitting(pmd) (pmd_val(pmd) & PMD_SECT_SPLITTING) diff --git a/arch/arm/lib/uaccess_with_memcpy.c b/arch/arm/lib/uaccess_with_memcpy.c index 025f742..3e58d71 100644 --- a/arch/arm/lib/uaccess_with_memcpy.c +++ b/arch/arm/lib/uaccess_with_memcpy.c @@ -18,6 +18,7 @@ #include <linux/hardirq.h> /* for in_atomic() */ #include <linux/gfp.h> #include <linux/highmem.h> +#include <linux/hugetlb.h> #include <asm/current.h> #include <asm/page.h> @@ -40,7 +41,35 @@ pin_page_for_write(const void __user *_addr, pte_t **ptep, spinlock_t **ptlp) return 0; pmd = pmd_offset(pud, addr); - if (unlikely(pmd_none(*pmd) || pmd_bad(*pmd))) + if (unlikely(pmd_none(*pmd))) + return 0; + + /* + * A pmd can be bad if it refers to a HugeTLB or THP page. + * + * Both THP and HugeTLB pages have the same pmd layout + * and should not be manipulated by the pte functions. + * + * Lock the page table for the destination and check + * to see that it's still huge and whether or not we will + * need to fault on write, or if we have a splitting THP. + */ + if (unlikely(pmd_thp_or_huge(*pmd))) { + ptl = ¤t->mm->page_table_lock; + spin_lock(ptl); + if (unlikely(!pmd_thp_or_huge(*pmd) + || pmd_hugewillfault(*pmd) + || pmd_trans_splitting(*pmd))) { + spin_unlock(ptl); + return 0; + } + + *ptep = NULL; + *ptlp = ptl; + return 1; + } + + if (unlikely(pmd_bad(*pmd))) return 0; pte = pte_offset_map_lock(current->mm, pmd, addr, &ptl); @@ -94,7 +123,10 @@ __copy_to_user_memcpy(void __user *to, const void *from, unsigned long n) from += tocopy; n -= tocopy; - pte_unmap_unlock(pte, ptl); + if (pte) + pte_unmap_unlock(pte, ptl); + else + spin_unlock(ptl); } if (!atomic) up_read(¤t->mm->mmap_sem); @@ -147,7 +179,10 @@ __clear_user_memset(void __user *addr, unsigned long n) addr += tocopy; n -= tocopy; - pte_unmap_unlock(pte, ptl); + if (pte) + pte_unmap_unlock(pte, ptl); + else + spin_unlock(ptl); } up_read(¤t->mm->mmap_sem);