Skip to content

Commit 766cfec

Browse files
committed
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.
1 parent a448f82 commit 766cfec

File tree

1 file changed

+32
-1
lines changed

1 file changed

+32
-1
lines changed

writer.go

+32-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bufio"
55
"io"
66
"runtime"
7+
"strings"
78
)
89

910
// Writer at INFO level. See WriterLevel for details.
@@ -20,15 +21,18 @@ func (logger *Logger) WriterLevel(level Level) *io.PipeWriter {
2021
return NewEntry(logger).WriterLevel(level)
2122
}
2223

24+
// Writer returns an io.Writer that writes to the logger at the info log level
2325
func (entry *Entry) Writer() *io.PipeWriter {
2426
return entry.WriterLevel(InfoLevel)
2527
}
2628

29+
// WriterLevel returns an io.Writer that writes to the logger at the given log level
2730
func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
2831
reader, writer := io.Pipe()
2932

3033
var printFunc func(args ...interface{})
3134

35+
// Determine which log function to use based on the specified log level
3236
switch level {
3337
case TraceLevel:
3438
printFunc = entry.Trace
@@ -48,23 +52,50 @@ func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
4852
printFunc = entry.Print
4953
}
5054

55+
// Start a new goroutine to scan the input and write it to the logger using the specified print function.
56+
// It splits the input into chunks of up to 64KB to avoid buffer overflows.
5157
go entry.writerScanner(reader, printFunc)
58+
59+
// Set a finalizer function to close the writer when it is garbage collected
5260
runtime.SetFinalizer(writer, writerFinalizer)
5361

5462
return writer
5563
}
5664

65+
// writerScanner scans the input from the reader and writes it to the logger
5766
func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
5867
scanner := bufio.NewScanner(reader)
68+
69+
// Set the buffer size to the maximum token size to avoid buffer overflows
70+
scanner.Buffer(make([]byte, bufio.MaxScanTokenSize), bufio.MaxScanTokenSize)
71+
72+
// Define a split function to split the input into chunks of up to 64KB
73+
chunkSize := 64 * 1024 // 64KB
74+
splitFunc := func(data []byte, atEOF bool) (int, []byte, error) {
75+
if len(data) > chunkSize {
76+
return chunkSize, data[:chunkSize], nil
77+
}
78+
return 0, nil, nil
79+
}
80+
81+
//Use the custom split function to split the input
82+
scanner.Split(splitFunc)
83+
84+
// Scan the input and write it to the logger using the specified print function
5985
for scanner.Scan() {
60-
printFunc(scanner.Text())
86+
printFunc(strings.TrimRight(scanner.Text(), "\r\n"))
6187
}
88+
89+
// If there was an error while scanning the input, log an error
6290
if err := scanner.Err(); err != nil {
6391
entry.Errorf("Error while reading from Writer: %s", err)
6492
}
93+
94+
// Close the reader when we are done
6595
reader.Close()
6696
}
6797

98+
// WriterFinalizer is a finalizer function that closes then given writer when it is garbage collected
6899
func writerFinalizer(writer *io.PipeWriter) {
69100
writer.Close()
70101
}

0 commit comments

Comments
 (0)