Fix alternate stack for Alpine docker on SELinux (#17936) (#17975)
authorkasper3 <33230602+kasper3@users.noreply.github.com>
Fri, 1 Jun 2018 12:22:43 +0000 (15:22 +0300)
committerJan Vorlicek <janvorli@microsoft.com>
Fri, 1 Jun 2018 12:22:43 +0000 (14:22 +0200)
For some reason, the Alpine docker container running on a SELinux host maps
heap as RWX. When we allocate alternate stack from the heap, we also
change the protection of the first page to PROT_NONE so that it can
serve as a guard page to catch stack overflow. And when we free the
alternate stack, we restore the protection back to PROT_READ |
PROT_WRITE. The restoration fails in Alpine docker container running on
a SELinux host with EPROT failure and the SELinux log reports that an
attempt to change heap to executable was made. So it looks like the
kernel has added the PERM_EXEC to the permissions we have passed to the
mprotect call. There is a code in the mprotect implementation that can
do that, although I don't fully understand the conditions under which it
happens. This is driven by the VM_MAYEXEC flag in the internal VMA block
structure.
To fix that, I've modified the alternate stack allocation to use mmap /
munmap instead of C heap allocation.

src/pal/src/exception/signal.cpp

index 0a3840a..458e331 100644 (file)
@@ -158,9 +158,9 @@ BOOL EnsureSignalAlternateStack()
         // (see kAltStackSize in compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cc)
         altStackSize += SIGSTKSZ * 4;
 #endif
-        void* altStack;
-        int st = posix_memalign(&altStack, GetVirtualPageSize(), altStackSize);
-        if (st == 0)
+        altStackSize = ALIGN_UP(altStackSize, GetVirtualPageSize());
+        void* altStack = mmap(NULL, altStackSize, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_STACK | MAP_PRIVATE, -1, 0);
+        if (altStack != MAP_FAILED)
         {
             // create a guard page for the alternate stack
             st = mprotect(altStack, GetVirtualPageSize(), PROT_NONE);
@@ -171,17 +171,12 @@ BOOL EnsureSignalAlternateStack()
                 ss.ss_size = altStackSize;
                 ss.ss_flags = 0;
                 st = sigaltstack(&ss, NULL);
-                if (st != 0)
-                {
-                    // Installation of the alternate stack failed, so revert the guard page protection
-                    int st2 = mprotect(altStack, GetVirtualPageSize(), PROT_READ | PROT_WRITE);
-                    _ASSERTE(st2 == 0);
-                }
             }
 
             if (st != 0)
             {
-                free(altStack);
+               int st2 = munmap(altStack, altStackSize);
+               _ASSERTE(st2 == 0);
             }
         }
     }
@@ -208,9 +203,8 @@ void FreeSignalAlternateStack()
     int st = sigaltstack(&ss, &oss);
     if ((st == 0) && (oss.ss_flags != SS_DISABLE))
     {
-        int st = mprotect(oss.ss_sp, GetVirtualPageSize(), PROT_READ | PROT_WRITE);
+        int st = munmap(oss.ss_sp, oss.ss_size);
         _ASSERTE(st == 0);
-        free(oss.ss_sp);
     }
 }
 #endif // !HAVE_MACH_EXCEPTIONS