From 766cfece3701d0b1737681ffb5e6e40b628b664d Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 10 Mar 2023 13:45:41 -0800 Subject: [PATCH 1/2] This commit fixes a potential denial of service vulnerability in logrus.Writer() that could be triggered by logging text longer than 64kb without newlines. Previously, the bufio.Scanner used by Writer() would hang indefinitely when reading such text without newlines, causing the application to become unresponsive. --- writer.go | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/writer.go b/writer.go index 72e8e3a1b..36032d069 100644 --- a/writer.go +++ b/writer.go @@ -4,6 +4,7 @@ import ( "bufio" "io" "runtime" + "strings" ) // Writer at INFO level. See WriterLevel for details. @@ -20,15 +21,18 @@ func (logger *Logger) WriterLevel(level Level) *io.PipeWriter { return NewEntry(logger).WriterLevel(level) } +// Writer returns an io.Writer that writes to the logger at the info log level func (entry *Entry) Writer() *io.PipeWriter { return entry.WriterLevel(InfoLevel) } +// WriterLevel returns an io.Writer that writes to the logger at the given log level func (entry *Entry) WriterLevel(level Level) *io.PipeWriter { reader, writer := io.Pipe() var printFunc func(args ...interface{}) + // Determine which log function to use based on the specified log level switch level { case TraceLevel: printFunc = entry.Trace @@ -48,23 +52,50 @@ func (entry *Entry) WriterLevel(level Level) *io.PipeWriter { printFunc = entry.Print } + // Start a new goroutine to scan the input and write it to the logger using the specified print function. + // It splits the input into chunks of up to 64KB to avoid buffer overflows. go entry.writerScanner(reader, printFunc) + + // Set a finalizer function to close the writer when it is garbage collected runtime.SetFinalizer(writer, writerFinalizer) return writer } +// writerScanner scans the input from the reader and writes it to the logger func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) { scanner := bufio.NewScanner(reader) + + // Set the buffer size to the maximum token size to avoid buffer overflows + scanner.Buffer(make([]byte, bufio.MaxScanTokenSize), bufio.MaxScanTokenSize) + + // Define a split function to split the input into chunks of up to 64KB + chunkSize := 64 * 1024 // 64KB + splitFunc := func(data []byte, atEOF bool) (int, []byte, error) { + if len(data) > chunkSize { + return chunkSize, data[:chunkSize], nil + } + return 0, nil, nil + } + + //Use the custom split function to split the input + scanner.Split(splitFunc) + + // Scan the input and write it to the logger using the specified print function for scanner.Scan() { - printFunc(scanner.Text()) + printFunc(strings.TrimRight(scanner.Text(), "\r\n")) } + + // If there was an error while scanning the input, log an error if err := scanner.Err(); err != nil { entry.Errorf("Error while reading from Writer: %s", err) } + + // Close the reader when we are done reader.Close() } +// WriterFinalizer is a finalizer function that closes then given writer when it is garbage collected func writerFinalizer(writer *io.PipeWriter) { writer.Close() } From c052ba6a076b368de89029949f68b3b8ccd8e058 Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 10 Mar 2023 13:45:41 -0800 Subject: [PATCH 2/2] Scan text in 64KB chunks This commit fixes a potential denial of service vulnerability in logrus.Writer() that could be triggered by logging text longer than 64KB without newlines. Previously, the bufio.Scanner used by Writer() would hang indefinitely when reading such text without newlines, causing the application to become unresponsive. --- writer.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/writer.go b/writer.go index 36032d069..7e7703c72 100644 --- a/writer.go +++ b/writer.go @@ -75,7 +75,8 @@ func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ... if len(data) > chunkSize { return chunkSize, data[:chunkSize], nil } - return 0, nil, nil + + return len(data), data, nil } //Use the custom split function to split the input