mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
![Cory Snider](/assets/img/avatar_default.png)
On Linux, when (os/exec.Cmd).SysProcAttr.Pdeathsig is set, the signal will be sent to the process when the OS thread on which cmd.Start() was executed dies. The runtime terminates an OS thread when a goroutine exits after being wired to the thread with runtime.LockOSThread(). If other goroutines are allowed to be scheduled onto a thread which called cmd.Start(), an unrelated goroutine could cause the thread to be terminated and prematurely signal the command. See https://github.com/golang/go/issues/27505 for more information. Prevent started subprocesses with Pdeathsig from getting signaled prematurely by wiring the starting goroutine to the OS thread until the subprocess has exited. No other goroutines can be scheduled onto a locked thread so it will remain alive until unlocked or the daemon process exits. Signed-off-by: Cory Snider <csnider@mirantis.com>
97 lines
2.1 KiB
Go
97 lines
2.1 KiB
Go
//go:build linux
|
|
// +build linux
|
|
|
|
package overlay2 // import "github.com/docker/docker/daemon/graphdriver/overlay2"
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"runtime"
|
|
|
|
"github.com/docker/docker/pkg/reexec"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
func init() {
|
|
reexec.Register("docker-mountfrom", mountFromMain)
|
|
}
|
|
|
|
func fatal(err error) {
|
|
fmt.Fprint(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
type mountOptions struct {
|
|
Device string
|
|
Target string
|
|
Type string
|
|
Label string
|
|
Flag uint32
|
|
}
|
|
|
|
func mountFrom(dir, device, target, mType string, flags uintptr, label string) error {
|
|
options := &mountOptions{
|
|
Device: device,
|
|
Target: target,
|
|
Type: mType,
|
|
Flag: uint32(flags),
|
|
Label: label,
|
|
}
|
|
|
|
cmd := reexec.Command("docker-mountfrom", dir)
|
|
w, err := cmd.StdinPipe()
|
|
if err != nil {
|
|
return fmt.Errorf("mountfrom error on pipe creation: %v", err)
|
|
}
|
|
|
|
output := bytes.NewBuffer(nil)
|
|
cmd.Stdout = output
|
|
cmd.Stderr = output
|
|
|
|
// reexec.Command() sets cmd.SysProcAttr.Pdeathsig on Linux, which
|
|
// causes the started process to be signaled when the creating OS thread
|
|
// dies. Ensure that the reexec is not prematurely signaled. See
|
|
// https://go.dev/issue/27505 for more information.
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
if err := cmd.Start(); err != nil {
|
|
w.Close()
|
|
return fmt.Errorf("mountfrom error on re-exec cmd: %v", err)
|
|
}
|
|
// write the options to the pipe for the untar exec to read
|
|
if err := json.NewEncoder(w).Encode(options); err != nil {
|
|
w.Close()
|
|
return fmt.Errorf("mountfrom json encode to pipe failed: %v", err)
|
|
}
|
|
w.Close()
|
|
|
|
if err := cmd.Wait(); err != nil {
|
|
return fmt.Errorf("mountfrom re-exec error: %v: output: %v", err, output)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// mountfromMain is the entry-point for docker-mountfrom on re-exec.
|
|
func mountFromMain() {
|
|
runtime.LockOSThread()
|
|
flag.Parse()
|
|
|
|
var options *mountOptions
|
|
|
|
if err := json.NewDecoder(os.Stdin).Decode(&options); err != nil {
|
|
fatal(err)
|
|
}
|
|
|
|
if err := os.Chdir(flag.Arg(0)); err != nil {
|
|
fatal(err)
|
|
}
|
|
|
|
if err := unix.Mount(options.Device, options.Target, options.Type, uintptr(options.Flag), options.Label); err != nil {
|
|
fatal(err)
|
|
}
|
|
|
|
os.Exit(0)
|
|
}
|