r278882 - If possible, set the stack rlimit to at least 8MiB on cc1 startup, and work

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Tue Aug 16 18:05:08 PDT 2016


Author: rsmith
Date: Tue Aug 16 20:05:07 2016
New Revision: 278882

URL: http://llvm.org/viewvc/llvm-project?rev=278882&view=rev
Log:
If possible, set the stack rlimit to at least 8MiB on cc1 startup, and work
around a Linux kernel bug where the actual amount of available stack may be a
*lot* lower than the rlimit.

GCC also sets a higher stack rlimit on startup, but it goes all the way to
64MiB. We can increase this limit if it proves necessary.

The kernel bug is as follows: Linux kernels prior to version 4.1 may choose to
map the process's heap as little as 128MiB before the process's stack for a PIE
binary, even in a 64-bit virtual address space. This means that allocating more
than 128MiB before you reach the process's stack high water mark can lead to
crashes, even if you don't recurse particularly deeply.

We work around the kernel bug by touching a page deep within the stack (after
ensuring that we know how big it is), to preallocate virtual address space for
the stack so that the kernel doesn't allow the brk() area to wander into it,
when building clang as a Linux PIE binary.

Modified:
    cfe/trunk/tools/driver/cc1_main.cpp

Modified: cfe/trunk/tools/driver/cc1_main.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/driver/cc1_main.cpp?rev=278882&r1=278881&r2=278882&view=diff
==============================================================================
--- cfe/trunk/tools/driver/cc1_main.cpp (original)
+++ cfe/trunk/tools/driver/cc1_main.cpp Tue Aug 16 20:05:07 2016
@@ -25,9 +25,11 @@
 #include "clang/Frontend/Utils.h"
 #include "clang/FrontendTool/Utils.h"
 #include "llvm/ADT/Statistic.h"
+#include "llvm/Config/config.h"
 #include "llvm/LinkAllPasses.h"
 #include "llvm/Option/ArgList.h"
 #include "llvm/Option/OptTable.h"
+#include "llvm/Support/Compiler.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/ManagedStatic.h"
 #include "llvm/Support/Signals.h"
@@ -35,6 +37,9 @@
 #include "llvm/Support/Timer.h"
 #include "llvm/Support/raw_ostream.h"
 #include <cstdio>
+#if HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
 using namespace clang;
 using namespace llvm::opt;
 
@@ -64,7 +69,67 @@ void initializePollyPasses(llvm::PassReg
 }
 #endif
 
+#if HAVE_SYS_RESOURCE_H && HAVE_GETRLIMIT && HAVE_SETRLIMIT
+// The amount of stack we think is "sufficient". If less than this much is
+// available, we may be unable to reach our template instantiation depth
+// limit and other similar limits.
+// FIXME: Unify this with the stack we request when spawning a thread to build
+// a module.
+static const int kSufficientStack = 8 << 20;
+
+#if defined(__linux__) && defined(__PIE__)
+LLVM_ATTRIBUTE_NOINLINE
+static void ensureStackAddressSpace() {
+  // Linux kernels prior to 4.1 will sometimes locate the heap of a PIE binary
+  // relatively close to the stack (they are only guaranteed to be 128MiB
+  // apart). This results in crashes if we happen to heap-allocate more than
+  // 128MiB before we reach our stack high-water mark.
+  //
+  // To avoid these crashes, ensure that we have sufficient virtual memory
+  // pages allocated before we start running by touching an early page. (We
+  // allow 512KiB for kernel/libc-provided data such as command-line arguments
+  // and environment variables, and for main and cc1_main)
+  volatile char ReservedStack[kSufficientStack - 512 * 1024];
+  volatile int N = 0;
+  (void)+ReservedStack[N];
+}
+#else
+static void ensureStackAddressSpace() {}
+#endif
+
+/// Attempt to ensure that we have at least 8MiB of usable stack space.
+static void ensureSufficientStack() {
+  struct rlimit rlim;
+  if (getrlimit(RLIMIT_STACK, &rlim) != 0)
+    return;
+
+  // Increase the soft stack limit to our desired level, if necessary and
+  // possible.
+  if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < kSufficientStack) {
+    // Try to allocate sufficient stack.
+    if (rlim.rlim_max == RLIM_INFINITY || rlim.rlim_max >= kSufficientStack)
+      rlim.rlim_cur = kSufficientStack;
+    else if (rlim.rlim_cur == rlim.rlim_max)
+      return;
+    else
+      rlim.rlim_cur = rlim.rlim_max;
+
+    if (setrlimit(RLIMIT_STACK, &rlim) != 0 ||
+        rlim.rlim_cur != kSufficientStack)
+      return;
+  }
+
+  // We should now have a stack of size at least kSufficientStack. Ensure
+  // that we can actually use that much, if necessary.
+  ensureStackAddressSpace();
+}
+#else
+static void ensureSufficientStack() {
+#endif
+
 int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
+  ensureSufficientStack();
+
   std::unique_ptr<CompilerInstance> Clang(new CompilerInstance());
   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
 




More information about the cfe-commits mailing list