summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/fs/tty/queue.go
diff options
context:
space:
mode:
authorKevin Krakauer <krakauer@google.com>2018-08-14 16:21:38 -0700
committerShentubot <shentubot@google.com>2018-08-14 16:22:56 -0700
commitd4939f6dc22e5607cf2ff8d2a9eb1178e47b0a22 (patch)
treec608c0bb4738d3626d7e71db0e32d4cb0bd781fe /pkg/sentry/fs/tty/queue.go
parent12a4912aedc834fc8f404dc1ffeaa37088dd2d6b (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.go55
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
}