diff options
author | Kevin Krakauer <krakauer@google.com> | 2018-08-14 16:21:38 -0700 |
---|---|---|
committer | Shentubot <shentubot@google.com> | 2018-08-14 16:22:56 -0700 |
commit | d4939f6dc22e5607cf2ff8d2a9eb1178e47b0a22 (patch) | |
tree | c608c0bb4738d3626d7e71db0e32d4cb0bd781fe /pkg/sentry/fs/tty/queue.go | |
parent | 12a4912aedc834fc8f404dc1ffeaa37088dd2d6b (diff) |
TTY: Fix data race where calls into tty.queue's waiter were not synchronized.
Now, there's a waiter for each end (master and slave) of the TTY, and each
waiter.Entry is only enqueued in one of the waiters.
PiperOrigin-RevId: 208734483
Change-Id: I06996148f123075f8dd48cde5a553e2be74c6dce
Diffstat (limited to 'pkg/sentry/fs/tty/queue.go')
-rw-r--r-- | pkg/sentry/fs/tty/queue.go | 55 |
1 files changed, 22 insertions, 33 deletions
diff --git a/pkg/sentry/fs/tty/queue.go b/pkg/sentry/fs/tty/queue.go index 42c105abc..01dc8d1ac 100644 --- a/pkg/sentry/fs/tty/queue.go +++ b/pkg/sentry/fs/tty/queue.go @@ -38,8 +38,6 @@ type queue struct { // mu protects everything in queue. mu sync.Mutex `state:"nosave"` - waiter.Queue `state:"zerovalue"` - // readBuf is buffer of data ready to be read when readable is true. // This data has been processed. readBuf bytes.Buffer `state:".([]byte)"` @@ -112,15 +110,17 @@ func (q *queue) readableSize(ctx context.Context, io usermem.IO, args arch.Sysca } -// read reads from q to userspace. +// read reads from q to userspace. It returns the number of bytes read as well +// as whether the read caused more readable data to become available (whether +// data was pushed from the wait buffer to the read buffer). // // Preconditions: // * l.termiosMu must be held for reading. -func (q *queue) read(ctx context.Context, dst usermem.IOSequence, l *lineDiscipline) (int64, error) { +func (q *queue) read(ctx context.Context, dst usermem.IOSequence, l *lineDiscipline) (int64, bool, error) { q.mu.Lock() defer q.mu.Unlock() if !q.readable { - return 0, syserror.ErrWouldBlock + return 0, false, syserror.ErrWouldBlock } // Read out from the read buffer. @@ -133,7 +133,7 @@ func (q *queue) read(ctx context.Context, dst usermem.IOSequence, l *lineDiscipl } n, err := dst.Writer(ctx).Write(q.readBuf.Bytes()[:n]) if err != nil { - return 0, err + return 0, false, err } // Discard bytes read out. q.readBuf.Next(n) @@ -144,16 +144,9 @@ func (q *queue) read(ctx context.Context, dst usermem.IOSequence, l *lineDiscipl } // Move data from the queue's wait buffer to its read buffer. - q.pushWaitBufLocked(l) - - // If state changed, notify any waiters. If nothing was available to - // read, let the caller know we could block. - if n > 0 { - q.Notify(waiter.EventOut) - } else { - return 0, syserror.ErrWouldBlock - } - return int64(n), nil + nPushed := q.pushWaitBufLocked(l) + + return int64(n), nPushed > 0, nil } // write writes to q from userspace. @@ -169,14 +162,20 @@ func (q *queue) write(ctx context.Context, src usermem.IOSequence, l *lineDiscip return 0, err } b = b[:n] - return q.writeBytes(b, l) + + // If state changed, notify any waiters. If we were unable to write + // anything, let the caller know we could block. + if c := q.writeBytes(b, l); c > 0 { + return c, nil + } + return 0, syserror.ErrWouldBlock } // writeBytes writes to q from b. // // Preconditions: // * l.termiosMu must be held for reading. -func (q *queue) writeBytes(b []byte, l *lineDiscipline) (int64, error) { +func (q *queue) writeBytes(b []byte, l *lineDiscipline) int64 { q.mu.Lock() defer q.mu.Unlock() // Write as much as possible to the read buffer. @@ -185,36 +184,26 @@ func (q *queue) writeBytes(b []byte, l *lineDiscipline) (int64, error) { // Write remaining data to the wait buffer. nWaiting, _ := q.waitBuf.Write(b[n:]) - // If state changed, notify any waiters. If we were unable to write - // anything, let the caller know we could block. - if n > 0 { - q.Notify(waiter.EventIn) - } else if nWaiting == 0 { - return 0, syserror.ErrWouldBlock - } - return int64(n + nWaiting), nil + return int64(n + nWaiting) } // pushWaitBuf fills the queue's read buffer with data from the wait buffer. // // Preconditions: // * l.termiosMu must be held for reading. -func (q *queue) pushWaitBuf(l *lineDiscipline) { +func (q *queue) pushWaitBuf(l *lineDiscipline) int { q.mu.Lock() defer q.mu.Unlock() - q.pushWaitBufLocked(l) + return q.pushWaitBufLocked(l) } // Preconditions: // * l.termiosMu must be held for reading. // * q.mu must be locked. -func (q *queue) pushWaitBufLocked(l *lineDiscipline) { +func (q *queue) pushWaitBufLocked(l *lineDiscipline) int { // Remove bytes from the wait buffer and move them to the read buffer. n := q.transform(l, q, q.waitBuf.Bytes()) q.waitBuf.Next(n) - // If state changed, notify any waiters. - if n > 0 { - q.Notify(waiter.EventIn) - } + return n } |