Merge pull request #2970 from Random-Liu/fix-exec-race-condition
Fix exec race condition
This commit is contained in:
		| @@ -69,7 +69,3 @@ func (s *deletedState) SetExited(status int) { | |||||||
| func (s *deletedState) Exec(ctx context.Context, path string, r *ExecConfig) (proc.Process, error) { | func (s *deletedState) Exec(ctx context.Context, path string, r *ExecConfig) (proc.Process, error) { | ||||||
| 	return nil, errors.Errorf("cannot exec in a deleted state") | 	return nil, errors.Errorf("cannot exec in a deleted state") | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *deletedState) Pid() int { |  | ||||||
| 	return -1 |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -49,7 +49,7 @@ type execProcess struct { | |||||||
| 	io      runc.IO | 	io      runc.IO | ||||||
| 	status  int | 	status  int | ||||||
| 	exited  time.Time | 	exited  time.Time | ||||||
| 	pid     int | 	pid     *safePid | ||||||
| 	closers []io.Closer | 	closers []io.Closer | ||||||
| 	stdin   io.Closer | 	stdin   io.Closer | ||||||
| 	stdio   proc.Stdio | 	stdio   proc.Stdio | ||||||
| @@ -69,11 +69,7 @@ func (e *execProcess) ID() string { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (e *execProcess) Pid() int { | func (e *execProcess) Pid() int { | ||||||
| 	return e.execState.Pid() | 	return e.pid.get() | ||||||
| } |  | ||||||
|  |  | ||||||
| func (e *execProcess) pidv() int { |  | ||||||
| 	return e.pid |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (e *execProcess) ExitStatus() int { | func (e *execProcess) ExitStatus() int { | ||||||
| @@ -145,7 +141,7 @@ func (e *execProcess) Kill(ctx context.Context, sig uint32, _ bool) error { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (e *execProcess) kill(ctx context.Context, sig uint32, _ bool) error { | func (e *execProcess) kill(ctx context.Context, sig uint32, _ bool) error { | ||||||
| 	pid := e.pid | 	pid := e.pid.get() | ||||||
| 	if pid != 0 { | 	if pid != 0 { | ||||||
| 		if err := unix.Kill(pid, syscall.Signal(sig)); err != nil { | 		if err := unix.Kill(pid, syscall.Signal(sig)); err != nil { | ||||||
| 			return errors.Wrapf(checkKillError(err), "exec kill error") | 			return errors.Wrapf(checkKillError(err), "exec kill error") | ||||||
| @@ -170,6 +166,12 @@ func (e *execProcess) Start(ctx context.Context) error { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (e *execProcess) start(ctx context.Context) (err error) { | func (e *execProcess) start(ctx context.Context) (err error) { | ||||||
|  | 	// The reaper may receive exit signal right after | ||||||
|  | 	// the container is started, before the e.pid is updated. | ||||||
|  | 	// In that case, we want to block the signal handler to | ||||||
|  | 	// access e.pid until it is updated. | ||||||
|  | 	e.pid.Lock() | ||||||
|  | 	defer e.pid.Unlock() | ||||||
| 	var ( | 	var ( | ||||||
| 		socket  *runc.Socket | 		socket  *runc.Socket | ||||||
| 		pidfile = filepath.Join(e.path, fmt.Sprintf("%s.pid", e.id)) | 		pidfile = filepath.Join(e.path, fmt.Sprintf("%s.pid", e.id)) | ||||||
| @@ -229,7 +231,7 @@ func (e *execProcess) start(ctx context.Context) (err error) { | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return errors.Wrap(err, "failed to retrieve OCI runtime exec pid") | 		return errors.Wrap(err, "failed to retrieve OCI runtime exec pid") | ||||||
| 	} | 	} | ||||||
| 	e.pid = pid | 	e.pid.pid = pid | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -247,11 +249,11 @@ func (e *execProcess) Status(ctx context.Context) (string, error) { | |||||||
| 	e.mu.Lock() | 	e.mu.Lock() | ||||||
| 	defer e.mu.Unlock() | 	defer e.mu.Unlock() | ||||||
| 	// if we don't have a pid then the exec process has just been created | 	// if we don't have a pid then the exec process has just been created | ||||||
| 	if e.pid == 0 { | 	if e.pid.get() == 0 { | ||||||
| 		return "created", nil | 		return "created", nil | ||||||
| 	} | 	} | ||||||
| 	// if we have a pid and it can be signaled, the process is running | 	// if we have a pid and it can be signaled, the process is running | ||||||
| 	if err := unix.Kill(e.pid, 0); err == nil { | 	if err := unix.Kill(e.pid.get(), 0); err == nil { | ||||||
| 		return "running", nil | 		return "running", nil | ||||||
| 	} | 	} | ||||||
| 	// else if we have a pid but it can nolonger be signaled, it has stopped | 	// else if we have a pid but it can nolonger be signaled, it has stopped | ||||||
|   | |||||||
| @@ -31,7 +31,6 @@ type execState interface { | |||||||
| 	Delete(context.Context) error | 	Delete(context.Context) error | ||||||
| 	Kill(context.Context, uint32, bool) error | 	Kill(context.Context, uint32, bool) error | ||||||
| 	SetExited(int) | 	SetExited(int) | ||||||
| 	Pid() int |  | ||||||
| } | } | ||||||
|  |  | ||||||
| type execCreatedState struct { | type execCreatedState struct { | ||||||
| @@ -83,12 +82,6 @@ func (s *execCreatedState) SetExited(status int) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *execCreatedState) Pid() int { |  | ||||||
| 	s.p.mu.Lock() |  | ||||||
| 	defer s.p.mu.Unlock() |  | ||||||
| 	return s.p.pidv() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type execRunningState struct { | type execRunningState struct { | ||||||
| 	p *execProcess | 	p *execProcess | ||||||
| } | } | ||||||
| @@ -127,12 +120,6 @@ func (s *execRunningState) SetExited(status int) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *execRunningState) Pid() int { |  | ||||||
| 	s.p.mu.Lock() |  | ||||||
| 	defer s.p.mu.Unlock() |  | ||||||
| 	return s.p.pidv() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type execStoppedState struct { | type execStoppedState struct { | ||||||
| 	p *execProcess | 	p *execProcess | ||||||
| } | } | ||||||
| @@ -170,7 +157,3 @@ func (s *execStoppedState) Kill(ctx context.Context, sig uint32, all bool) error | |||||||
| func (s *execStoppedState) SetExited(status int) { | func (s *execStoppedState) SetExited(status int) { | ||||||
| 	// no op | 	// no op | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *execStoppedState) Pid() int { |  | ||||||
| 	return s.p.pidv() |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -407,6 +407,7 @@ func (p *Init) exec(ctx context.Context, path string, r *ExecConfig) (proc.Proce | |||||||
| 			Terminal: r.Terminal, | 			Terminal: r.Terminal, | ||||||
| 		}, | 		}, | ||||||
| 		waitBlock: make(chan struct{}), | 		waitBlock: make(chan struct{}), | ||||||
|  | 		pid:       &safePid{}, | ||||||
| 	} | 	} | ||||||
| 	e.execState = &execCreatedState{p: e} | 	e.execState = &execCreatedState{p: e} | ||||||
| 	return e, nil | 	return e, nil | ||||||
|   | |||||||
| @@ -23,6 +23,7 @@ import ( | |||||||
| 	"io" | 	"io" | ||||||
| 	"os" | 	"os" | ||||||
| 	"strings" | 	"strings" | ||||||
|  | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/containerd/containerd/errdefs" | 	"github.com/containerd/containerd/errdefs" | ||||||
| @@ -31,6 +32,18 @@ import ( | |||||||
| 	"golang.org/x/sys/unix" | 	"golang.org/x/sys/unix" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | // safePid is a thread safe wrapper for pid. | ||||||
|  | type safePid struct { | ||||||
|  | 	sync.Mutex | ||||||
|  | 	pid int | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *safePid) get() int { | ||||||
|  | 	s.Lock() | ||||||
|  | 	defer s.Unlock() | ||||||
|  | 	return s.pid | ||||||
|  | } | ||||||
|  |  | ||||||
| // TODO(mlaventure): move to runc package? | // TODO(mlaventure): move to runc package? | ||||||
| func getLastRuntimeError(r *runc.Runc) (string, error) { | func getLastRuntimeError(r *runc.Runc) (string, error) { | ||||||
| 	if r.Log == "" { | 	if r.Log == "" { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Michael Crosby
					Michael Crosby