Skip to content

Commit 341b99d

Browse files
Merge pull request containerd#3153 from thepwagner/issue-3118
runtime/v1/linux/proc/io: io race
2 parents 2d0a06d + ae04c16 commit 341b99d

File tree

1 file changed

+27
-4
lines changed
  • runtime/v1/linux/proc

1 file changed

+27
-4
lines changed

runtime/v1/linux/proc/io.go

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@ import (
2727
"os/exec"
2828
"path/filepath"
2929
"sync"
30+
"sync/atomic"
3031
"syscall"
3132

33+
"github.com/containerd/containerd/log"
3234
"github.com/containerd/containerd/namespaces"
3335
"github.com/containerd/containerd/runtime/proc"
3436
"github.com/containerd/fifo"
@@ -122,7 +124,7 @@ func createIO(ctx context.Context, id string, ioUID, ioGID int, stdio proc.Stdio
122124
}
123125

124126
func copyPipes(ctx context.Context, rio runc.IO, stdin, stdout, stderr string, wg, cwg *sync.WaitGroup) error {
125-
var sameFile io.WriteCloser
127+
var sameFile *countingWriteCloser
126128
for _, i := range []struct {
127129
name string
128130
dest func(wc io.WriteCloser, rc io.Closer)
@@ -136,7 +138,9 @@ func copyPipes(ctx context.Context, rio runc.IO, stdin, stdout, stderr string, w
136138
cwg.Done()
137139
p := bufPool.Get().(*[]byte)
138140
defer bufPool.Put(p)
139-
io.CopyBuffer(wc, rio.Stdout(), *p)
141+
if _, err := io.CopyBuffer(wc, rio.Stdout(), *p); err != nil {
142+
log.G(ctx).Warn("error copying stdout")
143+
}
140144
wg.Done()
141145
wc.Close()
142146
if rc != nil {
@@ -153,7 +157,9 @@ func copyPipes(ctx context.Context, rio runc.IO, stdin, stdout, stderr string, w
153157
cwg.Done()
154158
p := bufPool.Get().(*[]byte)
155159
defer bufPool.Put(p)
156-
io.CopyBuffer(wc, rio.Stderr(), *p)
160+
if _, err := io.CopyBuffer(wc, rio.Stderr(), *p); err != nil {
161+
log.G(ctx).Warn("error copying stderr")
162+
}
157163
wg.Done()
158164
wc.Close()
159165
if rc != nil {
@@ -180,14 +186,18 @@ func copyPipes(ctx context.Context, rio runc.IO, stdin, stdout, stderr string, w
180186
}
181187
} else {
182188
if sameFile != nil {
189+
sameFile.count++
183190
i.dest(sameFile, nil)
184191
continue
185192
}
186193
if fw, err = os.OpenFile(i.name, syscall.O_WRONLY|syscall.O_APPEND, 0); err != nil {
187194
return fmt.Errorf("containerd-shim: opening %s failed: %s", i.name, err)
188195
}
189196
if stdout == stderr {
190-
sameFile = fw
197+
sameFile = &countingWriteCloser{
198+
WriteCloser: fw,
199+
count: 1,
200+
}
191201
}
192202
}
193203
i.dest(fw, fr)
@@ -212,6 +222,19 @@ func copyPipes(ctx context.Context, rio runc.IO, stdin, stdout, stderr string, w
212222
return nil
213223
}
214224

225+
// countingWriteCloser masks io.Closer() until close has been invoked a certain number of times.
226+
type countingWriteCloser struct {
227+
io.WriteCloser
228+
count int64
229+
}
230+
231+
func (c *countingWriteCloser) Close() error {
232+
if atomic.AddInt64(&c.count, -1) > 0 {
233+
return nil
234+
}
235+
return c.WriteCloser.Close()
236+
}
237+
215238
// isFifo checks if a file is a fifo
216239
// if the file does not exist then it returns false
217240
func isFifo(path string) (bool, error) {

0 commit comments

Comments
 (0)