m->locks++;
        if(m->profilehz != 0)
                runtime·resetcpuprofiler(0);
+
+       // This function is called before fork in syscall package.
+       // Code between fork and exec must not allocate memory nor even try to grow stack.
+       // Here we spoil g->stackguard to reliably detect any attempts to grow stack.
+       // runtime_AfterFork will undo this in parent process, but not in child.
+       m->forkstackguard = g->stackguard;
+       g->stackguard0 = StackPreempt-1;
+       g->stackguard = StackPreempt-1;
 }
 
 // Called from syscall package after fork in parent.
+#pragma textflag NOSPLIT
 void
 syscall·runtime_AfterFork(void)
 {
        int32 hz;
 
+       // See the comment in runtime_BeforeFork.
+       g->stackguard0 = m->forkstackguard;
+       g->stackguard = m->forkstackguard;
+       m->forkstackguard = 0;
+
        hz = runtime·sched.profilehz;
        if(hz != 0)
                runtime·resetcpuprofiler(hz);
 
        bool    needextram;
        bool    (*waitunlockf)(G*, void*);
        void*   waitlock;
+       uintptr forkstackguard;
 #ifdef GOOS_windows
        void*   thread;         // thread handle
        // these are here because they are too large to be on the stack
 
        Gobuf label;
        bool newstackcall;
 
+       if(m->forkstackguard)
+               runtime·throw("split stack after fork");
        if(m->morebuf.g != m->curg) {
                runtime·printf("runtime: newstack called from g=%p\n"
                        "\tm=%p m->curg=%p m->g0=%p m->gsignal=%p\n",