Jelajahi Sumber

daemon/logger: fix data race in LogFile

The log message's timestamp was being read after it was returned to the
pool. By coincidence the timestamp field happened to not be zeroed on
reset so much of the time things would work as expected. But if the
message value was to be taken back out of the pool before WriteLogEntry
returned, the timestamp recorded in the gzip header of compressed
rotated log files would be incorrect.

Make future use-after-put bugs fail fast by zeroing all fields of the
Message value, including the timestamp, when it is put into the pool.

Signed-off-by: Cory Snider <csnider@mirantis.com>
Cory Snider 3 tahun lalu
induk
melakukan
90c54320c8

+ 0 - 2
api/types/backend/backend.go

@@ -38,8 +38,6 @@ type PartialLogMetaData struct {
 // LogMessage is datastructure that represents piece of output produced by some
 // LogMessage is datastructure that represents piece of output produced by some
 // container.  The Line member is a slice of an array whose contents can be
 // container.  The Line member is a slice of an array whose contents can be
 // changed after a log driver's Log() method returns.
 // changed after a log driver's Log() method returns.
-// changes to this struct need to be reflect in the reset method in
-// daemon/logger/logger.go
 type LogMessage struct {
 type LogMessage struct {
 	Line         []byte
 	Line         []byte
 	Source       string
 	Source       string

+ 1 - 9
daemon/logger/logger.go

@@ -49,20 +49,12 @@ func PutMessage(msg *Message) {
 // Message is subtyped from backend.LogMessage because there is a lot of
 // Message is subtyped from backend.LogMessage because there is a lot of
 // internal complexity around the Message type that should not be exposed
 // internal complexity around the Message type that should not be exposed
 // to any package not explicitly importing the logger type.
 // to any package not explicitly importing the logger type.
-//
-// Any changes made to this struct must also be updated in the `reset` function
 type Message backend.LogMessage
 type Message backend.LogMessage
 
 
 // reset sets the message back to default values
 // reset sets the message back to default values
 // This is used when putting a message back into the message pool.
 // This is used when putting a message back into the message pool.
-// Any changes to the `Message` struct should be reflected here.
 func (m *Message) reset() {
 func (m *Message) reset() {
-	m.Line = m.Line[:0]
-	m.Source = ""
-	m.Attrs = nil
-	m.PLogMetaData = nil
-
-	m.Err = nil
+	*m = Message{Line: m.Line[:0]}
 }
 }
 
 
 // AsLogMessage returns a pointer to the message as a pointer to
 // AsLogMessage returns a pointer to the message as a pointer to

+ 3 - 1
daemon/logger/loggerutils/logfile.go

@@ -156,7 +156,9 @@ func (w *LogFile) WriteLogEntry(msg *logger.Message) error {
 		return errors.Wrap(err, "error marshalling log message")
 		return errors.Wrap(err, "error marshalling log message")
 	}
 	}
 
 
+	ts := msg.Timestamp
 	logger.PutMessage(msg)
 	logger.PutMessage(msg)
+	msg = nil // Turn use-after-put bugs into panics.
 
 
 	w.mu.Lock()
 	w.mu.Lock()
 	if w.closed {
 	if w.closed {
@@ -172,7 +174,7 @@ func (w *LogFile) WriteLogEntry(msg *logger.Message) error {
 	n, err := w.f.Write(b)
 	n, err := w.f.Write(b)
 	if err == nil {
 	if err == nil {
 		w.currentSize += int64(n)
 		w.currentSize += int64(n)
-		w.lastTimestamp = msg.Timestamp
+		w.lastTimestamp = ts
 	}
 	}
 
 
 	w.mu.Unlock()
 	w.mu.Unlock()