]> git.hungrycats.org Git - linux/commitdiff
vm: add VM_FAULT_SIGSEGV handling support
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 29 Jan 2015 18:51:32 +0000 (10:51 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 6 Feb 2015 06:36:01 +0000 (22:36 -0800)
commit 33692f27597fcab536d7cbbcc8f52905133e4aa7 upstream.

The core VM already knows about VM_FAULT_SIGBUS, but cannot return a
"you should SIGSEGV" error, because the SIGSEGV case was generally
handled by the caller - usually the architecture fault handler.

That results in lots of duplication - all the architecture fault
handlers end up doing very similar "look up vma, check permissions, do
retries etc" - but it generally works.  However, there are cases where
the VM actually wants to SIGSEGV, and applications _expect_ SIGSEGV.

In particular, when accessing the stack guard page, libsigsegv expects a
SIGSEGV.  And it usually got one, because the stack growth is handled by
that duplicated architecture fault handler.

However, when the generic VM layer started propagating the error return
from the stack expansion in commit fee7e49d4514 ("mm: propagate error
from stack expansion even for guard page"), that now exposed the
existing VM_FAULT_SIGBUS result to user space.  And user space really
expected SIGSEGV, not SIGBUS.

To fix that case, we need to add a VM_FAULT_SIGSEGV, and teach all those
duplicate architecture fault handlers about it.  They all already have
the code to handle SIGSEGV, so it's about just tying that new return
value to the existing code, but it's all a bit annoying.

This is the mindless minimal patch to do this.  A more extensive patch
would be to try to gather up the mostly shared fault handling logic into
one generic helper routine, and long-term we really should do that
cleanup.

Just from this patch, you can generally see that most architectures just
copied (directly or indirectly) the old x86 way of doing things, but in
the meantime that original x86 model has been improved to hold the VM
semaphore for shorter times etc and to handle VM_FAULT_RETRY and other
"newer" things, so it would be a good idea to bring all those
improvements to the generic case and teach other architectures about
them too.

Reported-and-tested-by: Takashi Iwai <tiwai@suse.de>
Tested-by: Jan Engelhardt <jengelh@inai.de>
Acked-by: Heiko Carstens <heiko.carstens@de.ibm.com> # "s390 still compiles and boots"
Cc: linux-arch@vger.kernel.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
29 files changed:
arch/alpha/mm/fault.c
arch/arc/mm/fault.c
arch/avr32/mm/fault.c
arch/cris/mm/fault.c
arch/frv/mm/fault.c
arch/ia64/mm/fault.c
arch/m32r/mm/fault.c
arch/m68k/mm/fault.c
arch/metag/mm/fault.c
arch/microblaze/mm/fault.c
arch/mips/mm/fault.c
arch/mn10300/mm/fault.c
arch/openrisc/mm/fault.c
arch/parisc/mm/fault.c
arch/powerpc/mm/copro_fault.c
arch/powerpc/mm/fault.c
arch/s390/mm/fault.c
arch/score/mm/fault.c
arch/sh/mm/fault.c
arch/sparc/mm/fault_32.c
arch/sparc/mm/fault_64.c
arch/tile/mm/fault.c
arch/um/kernel/trap.c
arch/x86/mm/fault.c
arch/xtensa/mm/fault.c
drivers/staging/lustre/lustre/llite/vvp_io.c
include/linux/mm.h
mm/gup.c
mm/ksm.c

index 98838a05ba6d89f0459742131010f57c38cbed05..9d0ac091a52a7d16cf1f78f402ab48c511924a24 100644 (file)
@@ -156,6 +156,8 @@ retry:
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
+               else if (fault & VM_FAULT_SIGSEGV)
+                       goto bad_area;
                else if (fault & VM_FAULT_SIGBUS)
                        goto do_sigbus;
                BUG();
index 6f7e3a68803a097461d27ace9fa940ba3211ab6b..0f8df3b5b1b366e6f8d78085ba4a2d96110e0038 100644 (file)
@@ -161,6 +161,8 @@ good_area:
 
        if (fault & VM_FAULT_OOM)
                goto out_of_memory;
+       else if (fault & VM_FAULT_SIGSEV)
+               goto bad_area;
        else if (fault & VM_FAULT_SIGBUS)
                goto do_sigbus;
 
index 0eca93327195077ec16bdfd99efd7294c6ab2de6..d223a8b57c1eaad282289e75089654153ab598d6 100644 (file)
@@ -142,6 +142,8 @@ good_area:
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
+               else if (fault & VM_FAULT_SIGSEGV)
+                       goto bad_area;
                else if (fault & VM_FAULT_SIGBUS)
                        goto do_sigbus;
                BUG();
index 1790f22e71a21a859b2b7b1942cbbc503c2d557e..2686a7aa8ec82c50f29592840185b519522c53a7 100644 (file)
@@ -176,6 +176,8 @@ retry:
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
+               else if (fault & VM_FAULT_SIGSEGV)
+                       goto bad_area;
                else if (fault & VM_FAULT_SIGBUS)
                        goto do_sigbus;
                BUG();
index 9a66372fc7c76019ca874a9c3780c2fc8392266c..ec4917ddf67872aa46b60c6b067b0a67ec5417a4 100644 (file)
@@ -168,6 +168,8 @@ asmlinkage void do_page_fault(int datammu, unsigned long esr0, unsigned long ear
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
+               else if (fault & VM_FAULT_SIGSEGV)
+                       goto bad_area;
                else if (fault & VM_FAULT_SIGBUS)
                        goto do_sigbus;
                BUG();
index 7225dad87094d81e89459e5a61909fa5b2d10ca0..ba5ba7accd0d6bb4dbab34f7fc307c4306347f4a 100644 (file)
@@ -172,6 +172,8 @@ retry:
                 */
                if (fault & VM_FAULT_OOM) {
                        goto out_of_memory;
+               } else if (fault & VM_FAULT_SIGSEGV) {
+                       goto bad_area;
                } else if (fault & VM_FAULT_SIGBUS) {
                        signal = SIGBUS;
                        goto bad_area;
index e9c6a8014bd647eec50a66afb5bc75b076b35e4d..e3d4d4890104cc27e2eb9de2f22cb6f53f939c90 100644 (file)
@@ -200,6 +200,8 @@ good_area:
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
+               else if (fault & VM_FAULT_SIGSEGV)
+                       goto bad_area;
                else if (fault & VM_FAULT_SIGBUS)
                        goto do_sigbus;
                BUG();
index 2bd7487440c455802dac6470ec05ac138148bfcb..b2f04aee46ecc2f7a5fb1db26d8e4279f6b6ea2e 100644 (file)
@@ -145,6 +145,8 @@ good_area:
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
+               else if (fault & VM_FAULT_SIGSEGV)
+                       goto map_err;
                else if (fault & VM_FAULT_SIGBUS)
                        goto bus_err;
                BUG();
index 332680e5ebf23c7909b796c415c2273efd77ba3c..2de5dc695a87fa96d41a83e127166a7126d10df0 100644 (file)
@@ -141,6 +141,8 @@ good_area:
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
+               else if (fault & VM_FAULT_SIGSEGV)
+                       goto bad_area;
                else if (fault & VM_FAULT_SIGBUS)
                        goto do_sigbus;
                BUG();
index fa4cf52aa7a6d386711690005a314ece7d67fc53..d46a5ebb7570e07869ea03b9b995374aa3bff82e 100644 (file)
@@ -224,6 +224,8 @@ good_area:
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
+               else if (fault & VM_FAULT_SIGSEGV)
+                       goto bad_area;
                else if (fault & VM_FAULT_SIGBUS)
                        goto do_sigbus;
                BUG();
index becc42bb18495adf98389bd039bc111c1893cedd..70ab5d664332694e92305331f13ed15a35ab1956 100644 (file)
@@ -158,6 +158,8 @@ good_area:
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
+               else if (fault & VM_FAULT_SIGSEGV)
+                       goto bad_area;
                else if (fault & VM_FAULT_SIGBUS)
                        goto do_sigbus;
                BUG();
index 3516cbdf1ee93acb82ebef6428f79df9af104514..0c2cc5d39c8e37ce1cfe5be191902bc435c41090 100644 (file)
@@ -262,6 +262,8 @@ good_area:
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
+               else if (fault & VM_FAULT_SIGSEGV)
+                       goto bad_area;
                else if (fault & VM_FAULT_SIGBUS)
                        goto do_sigbus;
                BUG();
index 0703acf7d3276811919fd3d398ada99b1b9c6d50..230ac20ae7944f71636e5083fdaf3f034eb10af2 100644 (file)
@@ -171,6 +171,8 @@ good_area:
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
+               else if (fault & VM_FAULT_SIGSEGV)
+                       goto bad_area;
                else if (fault & VM_FAULT_SIGBUS)
                        goto do_sigbus;
                BUG();
index 3ca9c1131cfe0d80b9b12fb5c0e599a3363942c0..e5120e653240c4fa52d4895c7d1d206d3d12e68c 100644 (file)
@@ -256,6 +256,8 @@ good_area:
                 */
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
+               else if (fault & VM_FAULT_SIGSEGV)
+                       goto bad_area;
                else if (fault & VM_FAULT_SIGBUS)
                        goto bad_area;
                BUG();
index 5a236f082c78386a47b9b415f98f619e8e615688..1b5305d4bdabe95c4f4430b89c0fb56512bb6fdf 100644 (file)
@@ -76,7 +76,7 @@ int copro_handle_mm_fault(struct mm_struct *mm, unsigned long ea,
                if (*flt & VM_FAULT_OOM) {
                        ret = -ENOMEM;
                        goto out_unlock;
-               } else if (*flt & VM_FAULT_SIGBUS) {
+               } else if (*flt & (VM_FAULT_SIGBUS | VM_FAULT_SIGSEGV)) {
                        ret = -EFAULT;
                        goto out_unlock;
                }
index 08d659a9fcdbf16026e7dfc2af9306416028c671..f06b56baf0b324e43a48fd8f5573ada116df506d 100644 (file)
@@ -444,6 +444,8 @@ good_area:
         */
        fault = handle_mm_fault(mm, vma, address, flags);
        if (unlikely(fault & (VM_FAULT_RETRY|VM_FAULT_ERROR))) {
+               if (fault & VM_FAULT_SIGSEGV)
+                       goto bad_area;
                rc = mm_fault_error(regs, address, fault);
                if (rc >= MM_FAULT_RETURN)
                        goto bail;
index a2b81d6ce8a5ca9cf9680c59d1a1b83e347b1621..fbe8f2cf924589c3f0e6e31e1619562e55e92586 100644 (file)
@@ -374,6 +374,12 @@ static noinline void do_fault_error(struct pt_regs *regs, int fault)
                                do_no_context(regs);
                        else
                                pagefault_out_of_memory();
+               } else if (fault & VM_FAULT_SIGSEGV) {
+                       /* Kernel mode? Handle exceptions or die */
+                       if (!user_mode(regs))
+                               do_no_context(regs);
+                       else
+                               do_sigsegv(regs, SEGV_MAPERR);
                } else if (fault & VM_FAULT_SIGBUS) {
                        /* Kernel mode? Handle exceptions or die */
                        if (!user_mode(regs))
index 52238983527d605914853fd5415ea39617944ffe..6860beb2a280d0a4a65a67c89ad2201b33513068 100644 (file)
@@ -114,6 +114,8 @@ good_area:
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
+               else if (fault & VM_FAULT_SIGSEGV)
+                       goto bad_area;
                else if (fault & VM_FAULT_SIGBUS)
                        goto do_sigbus;
                BUG();
index 541dc610150888e706977c7944c42ab1d61d7437..a58fec9b55e016df85cdfb7c214cc385e300479c 100644 (file)
@@ -353,6 +353,8 @@ mm_fault_error(struct pt_regs *regs, unsigned long error_code,
        } else {
                if (fault & VM_FAULT_SIGBUS)
                        do_sigbus(regs, error_code, address);
+               else if (fault & VM_FAULT_SIGSEGV)
+                       bad_area(regs, error_code, address);
                else
                        BUG();
        }
index 908e8c17c902bef419877cd1bedcc896b9627636..70d817154fe8bfd04aeaa71f45f15667f4962c23 100644 (file)
@@ -249,6 +249,8 @@ good_area:
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
+               else if (fault & VM_FAULT_SIGSEGV)
+                       goto bad_area;
                else if (fault & VM_FAULT_SIGBUS)
                        goto do_sigbus;
                BUG();
index 18fcd71670959291f8ef4933e37d5bc394e98f51..4798232494294a7ece0bef232216dd4a26408d88 100644 (file)
@@ -446,6 +446,8 @@ good_area:
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
+               else if (fault & VM_FAULT_SIGSEGV)
+                       goto bad_area;
                else if (fault & VM_FAULT_SIGBUS)
                        goto do_sigbus;
                BUG();
index 6c0571216a9d67f81a09ff331a601d22ae881566..c6d2a76d91a81e3c23d13d6bc101d88c7c3bc461 100644 (file)
@@ -444,6 +444,8 @@ good_area:
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
+               else if (fault & VM_FAULT_SIGSEGV)
+                       goto bad_area;
                else if (fault & VM_FAULT_SIGBUS)
                        goto do_sigbus;
                BUG();
index 5678c3571e7cb4d1572d0b16a91b0650f76095c7..209617302df89e02994b7c1e45df4340826bad05 100644 (file)
@@ -80,6 +80,8 @@ good_area:
                if (unlikely(fault & VM_FAULT_ERROR)) {
                        if (fault & VM_FAULT_OOM) {
                                goto out_of_memory;
+                       } else if (fault & VM_FAULT_SIGSEGV) {
+                               goto out;
                        } else if (fault & VM_FAULT_SIGBUS) {
                                err = -EACCES;
                                goto out;
index d973e61e450dc4f4733e838c67500aa711168302..a8612aafeca1f4007e1c5e2f78939a3747dfd64b 100644 (file)
@@ -905,6 +905,8 @@ mm_fault_error(struct pt_regs *regs, unsigned long error_code,
                if (fault & (VM_FAULT_SIGBUS|VM_FAULT_HWPOISON|
                             VM_FAULT_HWPOISON_LARGE))
                        do_sigbus(regs, error_code, address, fault);
+               else if (fault & VM_FAULT_SIGSEGV)
+                       bad_area_nosemaphore(regs, error_code, address);
                else
                        BUG();
        }
index b57c4f91f487efdc6b2f3f44fe43cfb12e9a7e0c..9e3571a6535c3b1bbc8535195ee40405fe9c42c0 100644 (file)
@@ -117,6 +117,8 @@ good_area:
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
+               else if (fault & VM_FAULT_SIGSEGV)
+                       goto bad_area;
                else if (fault & VM_FAULT_SIGBUS)
                        goto do_sigbus;
                BUG();
index d3f967a78138dfd266bdd6c2e969d18da23c7b83..1f453b275dbc4605dffbcbb25cd6ab553d3da072 100644 (file)
@@ -632,7 +632,7 @@ static int vvp_io_kernel_fault(struct vvp_fault_io *cfio)
                return 0;
        }
 
-       if (cfio->fault.ft_flags & VM_FAULT_SIGBUS) {
+       if (cfio->fault.ft_flags & (VM_FAULT_SIGBUS | VM_FAULT_SIGSEGV)) {
                CDEBUG(D_PAGE, "got addr %p - SIGBUS\n", vmf->virtual_address);
                return -EFAULT;
        }
index 5ab2da9811c1014c55ca3489d030f3ad9a33fbd5..86a977bf4f79663be4df59de39d70086ca5d3b3b 100644 (file)
@@ -1054,6 +1054,7 @@ static inline int page_mapped(struct page *page)
 #define VM_FAULT_WRITE 0x0008  /* Special case for get_user_pages */
 #define VM_FAULT_HWPOISON 0x0010       /* Hit poisoned small page */
 #define VM_FAULT_HWPOISON_LARGE 0x0020  /* Hit poisoned large page. Index encoded in upper bits */
+#define VM_FAULT_SIGSEGV 0x0040
 
 #define VM_FAULT_NOPAGE        0x0100  /* ->fault installed the pte, not return page */
 #define VM_FAULT_LOCKED        0x0200  /* ->fault locked the returned page */
@@ -1062,8 +1063,9 @@ static inline int page_mapped(struct page *page)
 
 #define VM_FAULT_HWPOISON_LARGE_MASK 0xf000 /* encodes hpage index for large hwpoison */
 
-#define VM_FAULT_ERROR (VM_FAULT_OOM | VM_FAULT_SIGBUS | VM_FAULT_HWPOISON | \
-                        VM_FAULT_FALLBACK | VM_FAULT_HWPOISON_LARGE)
+#define VM_FAULT_ERROR (VM_FAULT_OOM | VM_FAULT_SIGBUS | VM_FAULT_SIGSEGV | \
+                        VM_FAULT_HWPOISON | VM_FAULT_HWPOISON_LARGE | \
+                        VM_FAULT_FALLBACK)
 
 /* Encode hstate index for a hwpoisoned large page */
 #define VM_FAULT_SET_HINDEX(x) ((x) << 12)
index cd62c8c90d4a3584fb087b2f2e949e7004ba615c..a0d57ec055104ee3187dd7aa2d5d397fed3542b5 100644 (file)
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -296,7 +296,7 @@ static int faultin_page(struct task_struct *tsk, struct vm_area_struct *vma,
                        return -ENOMEM;
                if (ret & (VM_FAULT_HWPOISON | VM_FAULT_HWPOISON_LARGE))
                        return *flags & FOLL_HWPOISON ? -EHWPOISON : -EFAULT;
-               if (ret & VM_FAULT_SIGBUS)
+               if (ret & (VM_FAULT_SIGBUS | VM_FAULT_SIGSEGV))
                        return -EFAULT;
                BUG();
        }
@@ -571,7 +571,7 @@ int fixup_user_fault(struct task_struct *tsk, struct mm_struct *mm,
                        return -ENOMEM;
                if (ret & (VM_FAULT_HWPOISON | VM_FAULT_HWPOISON_LARGE))
                        return -EHWPOISON;
-               if (ret & VM_FAULT_SIGBUS)
+               if (ret & (VM_FAULT_SIGBUS | VM_FAULT_SIGSEGV))
                        return -EFAULT;
                BUG();
        }
index 6b2e337bc03c7d6c5fce5e33023bdb5d2f111789..a0ed043a109614d82fa983d44351447e11a64144 100644 (file)
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -376,7 +376,7 @@ static int break_ksm(struct vm_area_struct *vma, unsigned long addr)
                else
                        ret = VM_FAULT_WRITE;
                put_page(page);
-       } while (!(ret & (VM_FAULT_WRITE | VM_FAULT_SIGBUS | VM_FAULT_OOM)));
+       } while (!(ret & (VM_FAULT_WRITE | VM_FAULT_SIGBUS | VM_FAULT_SIGSEGV | VM_FAULT_OOM)));
        /*
         * We must loop because handle_mm_fault() may back out if there's
         * any difficulty e.g. if pte accessed bit gets updated concurrently.