From 917bee5b6d9189b9bc22bd8e2b447418e75f2bc4 Mon Sep 17 00:00:00 2001 From: Etienne Perot Date: Thu, 9 Nov 2023 14:57:32 -0800 Subject: [PATCH] Add a size limit to `outputQueueTransformer.transform`. This applies a similar limit as `inputQueueTransformer.transform`. The goal is to avoid large allocations of memory. I'm not sure if this is the right thing to do, but it seems better than the status quo of not having any limits. PiperOrigin-RevId: 581034478 --- pkg/sentry/fsimpl/devpts/line_discipline.go | 37 ++++++++++++++++----- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/pkg/sentry/fsimpl/devpts/line_discipline.go b/pkg/sentry/fsimpl/devpts/line_discipline.go index 018cb930a..1f880265d 100644 --- a/pkg/sentry/fsimpl/devpts/line_discipline.go +++ b/pkg/sentry/fsimpl/devpts/line_discipline.go @@ -314,49 +314,69 @@ type outputQueueTransformer struct{} func (*outputQueueTransformer) transform(l *lineDiscipline, q *queue, buf []byte) (int, bool) { // transformOutput is effectively always in noncanonical mode, as the // master termios never has ICANON set. + sizeBudget := nonCanonMaxBytes - len(q.readBuf) + if sizeBudget <= 0 { + return 0, false + } if !l.termios.OEnabled(linux.OPOST) { - q.readBuf = append(q.readBuf, buf...) + copySize := min(len(buf), sizeBudget) + q.readBuf = append(q.readBuf, buf[:copySize]...) if len(q.readBuf) > 0 { q.readable = true } - return len(buf), false + return copySize, false } var ret int - for len(buf) > 0 { + +Outer: + for ; len(buf) > 0 && sizeBudget > 0; sizeBudget = nonCanonMaxBytes - len(q.readBuf) { size := l.peek(buf) + if size > sizeBudget { + break Outer + } cBytes := append([]byte{}, buf[:size]...) - ret += size buf = buf[size:] // We're guaranteed that cBytes has at least one element. + cByteSwitch: switch cBytes[0] { case '\n': if l.termios.OEnabled(linux.ONLRET) { l.column = 0 } if l.termios.OEnabled(linux.ONLCR) { + if sizeBudget < 2 { + break Outer + } + ret += size q.readBuf = append(q.readBuf, '\r', '\n') - continue + continue Outer } case '\r': if l.termios.OEnabled(linux.ONOCR) && l.column == 0 { - continue + // Treat the carriage return as processed, since it's a no-op. + ret += size + continue Outer } if l.termios.OEnabled(linux.OCRNL) { cBytes[0] = '\n' if l.termios.OEnabled(linux.ONLRET) { l.column = 0 } - break + break cByteSwitch } l.column = 0 case '\t': spaces := spacesPerTab - l.column%spacesPerTab if l.termios.OutputFlags&linux.TABDLY == linux.XTABS { + if sizeBudget < spacesPerTab { + break Outer + } + ret += size l.column += spaces q.readBuf = append(q.readBuf, bytes.Repeat([]byte{' '}, spacesPerTab)...) - continue + continue Outer } l.column += spaces case '\b': @@ -366,6 +386,7 @@ func (*outputQueueTransformer) transform(l *lineDiscipline, q *queue, buf []byte default: l.column++ } + ret += size q.readBuf = append(q.readBuf, cBytes...) } if len(q.readBuf) > 0 {