diff --git a/src/runtime/proc.go b/src/runtime/proc.go index f6e07f8ec0..b360f20ee7 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -1615,6 +1615,12 @@ func unlockextra(mp *m) { atomic.Storeuintptr(&extram, uintptr(unsafe.Pointer(mp))) } +// execLock serializes exec and clone to avoid bugs or unspecified behaviour +// around exec'ing while creating/destroying threads. See issue #19546. +// +// TODO: look into using a rwmutex, to avoid serializing thread creation. +var execLock mutex + // Create a new m. It will start off with a call to fn, or else the scheduler. // fn needs to be static and not a heap allocated closure. // May run with m.p==nil, so write barriers are not allowed. @@ -1634,10 +1640,14 @@ func newm(fn func(), _p_ *p) { if msanenabled { msanwrite(unsafe.Pointer(&ts), unsafe.Sizeof(ts)) } + lock(&execLock) asmcgocall(_cgo_thread_start, unsafe.Pointer(&ts)) + unlock(&execLock) return } + lock(&execLock) newosproc(mp, unsafe.Pointer(mp.g0.stack.hi)) + unlock(&execLock) } // Stops execution of the current m until new work is available. @@ -2857,6 +2867,18 @@ func syscall_runtime_AfterForkInChild() { msigrestore(getg().m.sigmask) } +// Called from syscall package before Exec. +//go:linkname syscall_runtime_BeforeExec syscall.runtime_BeforeExec +func syscall_runtime_BeforeExec() { + lock(&execLock) +} + +// Called from syscall package after Exec. +//go:linkname syscall_runtime_AfterExec syscall.runtime_AfterExec +func syscall_runtime_AfterExec() { + unlock(&execLock) +} + // Allocate a new g, with a stack big enough for stacksize bytes. func malg(stacksize int32) *g { newg := new(g) diff --git a/src/syscall/exec_unix.go b/src/syscall/exec_unix.go index e4f047f3f4..e8ca29ab40 100644 --- a/src/syscall/exec_unix.go +++ b/src/syscall/exec_unix.go @@ -242,6 +242,10 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle return pid, 0, err } +// Implemented in runtime package. +func runtime_BeforeExec() +func runtime_AfterExec() + // Exec invokes the execve(2) system call. func Exec(argv0 string, argv []string, envv []string) (err error) { argv0p, err := BytePtrFromString(argv0) @@ -256,9 +260,11 @@ func Exec(argv0 string, argv []string, envv []string) (err error) { if err != nil { return err } + runtime_BeforeExec() _, _, err1 := RawSyscall(SYS_EXECVE, uintptr(unsafe.Pointer(argv0p)), uintptr(unsafe.Pointer(&argvp[0])), uintptr(unsafe.Pointer(&envvp[0]))) + runtime_AfterExec() return Errno(err1) }