| From torvalds@linux-foundation.org Tue Jul 28 11:13:51 2009 |
| From: Linus Torvalds <torvalds@linux-foundation.org> |
| Date: Mon, 22 Jun 2009 10:25:25 -0700 (PDT) |
| Subject: x86: don't use 'access_ok()' as a range check in get_user_pages_fast() |
| To: Greg KH <gregkh@suse.de> |
| Cc: Ingo Molnar <mingo@elte.hu>, Andrew Morton <akpm@linux-foundation.org>, Hugh Dickins <hugh.dickins@tiscali.co.uk>, Chris Wright <chrisw@sous-sol.org>, Nick Piggin <npiggin@suse.de>, "H. Peter Anvin" <hpa@zytor.com>, Thomas Gleixner <tglx@linutronix.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Peter Zijlstra <a.p.zijlstra@chello.nl> |
| Message-ID: <alpine.LFD.2.01.0906221024140.3240@localhost.localdomain> |
| |
| From: Linus Torvalds <torvalds@linux-foundation.org> |
| |
| [ Upstream commit 7f8189068726492950bf1a2dcfd9b51314560abf - modified |
| for stable to not use the sloppy __VIRTUAL_MASK_SHIFT ] |
| |
| It's really not right to use 'access_ok()', since that is meant for the |
| normal "get_user()" and "copy_from/to_user()" accesses, which are done |
| through the TLB, rather than through the page tables. |
| |
| Why? access_ok() does both too few, and too many checks. Too many, |
| because it is meant for regular kernel accesses that will not honor the |
| 'user' bit in the page tables, and because it honors the USER_DS vs |
| KERNEL_DS distinction that we shouldn't care about in GUP. And too few, |
| because it doesn't do the 'canonical' check on the address on x86-64, |
| since the TLB will do that for us. |
| |
| So instead of using a function that isn't meant for this, and does |
| something else and much more complicated, just do the real rules: we |
| don't want the range to overflow, and on x86-64, we want it to be a |
| canonical low address (on 32-bit, all addresses are canonical). |
| |
| Acked-by: Ingo Molnar <mingo@elte.hu> |
| Cc: H. Peter Anvin <hpa@zytor.com> |
| Cc: Thomas Gleixner <tglx@linutronix.de> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| arch/x86/mm/gup.c | 9 +++++++-- |
| 1 file changed, 7 insertions(+), 2 deletions(-) |
| |
| --- a/arch/x86/mm/gup.c |
| +++ b/arch/x86/mm/gup.c |
| @@ -247,10 +247,15 @@ int get_user_pages_fast(unsigned long st |
| start &= PAGE_MASK; |
| addr = start; |
| len = (unsigned long) nr_pages << PAGE_SHIFT; |
| + |
| end = start + len; |
| - if (unlikely(!access_ok(write ? VERIFY_WRITE : VERIFY_READ, |
| - (void __user *)start, len))) |
| + if (end < start) |
| + goto slow_irqon; |
| + |
| +#ifdef CONFIG_X86_64 |
| + if (end >> 47) |
| goto slow_irqon; |
| +#endif |
| |
| /* |
| * XXX: batch / limit 'nr', to avoid large irq off latency |