You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nodejs/SOURCES/0008-zlib-pause-stream-if-o...

151 lines
30 KiB

From 58a980141be5d4323cd8ba18f1e285cb25c9d547 Mon Sep 17 00:00:00 2001
From: Matteo Collina <hello@matteocollina.com>
Date: Tue, 6 Feb 2024 16:47:20 +0100
Subject: [PATCH] zlib: pause stream if outgoing buffer is full
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Matteo Collina <hello@matteocollina.com>
PR-URL: https://github.com/nodejs-private/node-private/pull/540
Reviewed-By: Robert Nagy <ronagy@icloud.com>
Ref: https://hackerone.com/reports/2284065
CVE-ID: CVE-2024-22025
Signed-off-by: Jan Staněk <jstanek@redhat.com>
Signed-off-by: rpm-build <rpm-build>
---
lib/zlib.js | 33 +++++++++++++++++++-------
test/parallel/test-zlib-brotli-16GB.js | 22 +++++++++++++++++
test/parallel/test-zlib-params.js | 24 ++++++++++++-------
3 files changed, 62 insertions(+), 17 deletions(-)
create mode 100644 test/parallel/test-zlib-brotli-16GB.js
diff --git a/lib/zlib.js b/lib/zlib.js
index 9bde199..efe77bc 100644
--- a/lib/zlib.js
+++ b/lib/zlib.js
@@ -560,10 +560,11 @@ function processCallback() {
self.bytesWritten += inDelta;
const have = handle.availOutBefore - availOutAfter;
+ let streamBufferIsFull = false;
if (have > 0) {
const out = self._outBuffer.slice(self._outOffset, self._outOffset + have);
self._outOffset += have;
- self.push(out);
+ streamBufferIsFull = !self.push(out);
} else {
assert(have === 0, 'have should not go down');
}
@@ -588,13 +589,29 @@ function processCallback() {
handle.inOff += inDelta;
handle.availInBefore = availInAfter;
- this.write(handle.flushFlag,
- this.buffer, // in
- handle.inOff, // in_off
- handle.availInBefore, // in_len
- self._outBuffer, // out
- self._outOffset, // out_off
- self._chunkSize); // out_len
+
+ if (!streamBufferIsFull) {
+ this.write(handle.flushFlag,
+ this.buffer, // in
+ handle.inOff, // in_off
+ handle.availInBefore, // in_len
+ self._outBuffer, // out
+ self._outOffset, // out_off
+ self._chunkSize); // out_len
+ } else {
+ const oldRead = self._read;
+ self._read = (n) => {
+ self._read = oldRead;
+ this.write(handle.flushFlag,
+ this.buffer, // in
+ handle.inOff, // in_off
+ handle.availInBefore, // in_len
+ self._outBuffer, // out
+ self._outOffset, // out_off
+ self._chunkSize); // out_len
+ self._read(n);
+ };
+ }
return;
}
diff --git a/test/parallel/test-zlib-brotli-16GB.js b/test/parallel/test-zlib-brotli-16GB.js
new file mode 100644
index 0000000..ba4f7ef
--- /dev/null
+++ b/test/parallel/test-zlib-brotli-16GB.js
@@ -0,0 +1,22 @@
+'use strict';
+
+const common = require('../common');
+const { createBrotliDecompress } = require('node:zlib');
+const strictEqual = require('node:assert').strictEqual;
+
+// This tiny HEX string is a 16GB file.
+// This test verifies that the stream actually stops.
+/* eslint-disable max-len */
+const content = 'cfffff7ff82700e2b14020f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c32200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc
+
+const buf = Buffer.from(content, 'hex');
+
+const decoder = createBrotliDecompress();
+decoder.end(buf);
+
+// We need to wait to verify that the libuv thread pool had time
+// to process the data and the buffer is not empty.
+setTimeout(common.mustCall(() => {
+ // There is only one chunk in the buffer
+ strictEqual(decoder._readableState.buffer.length, 1);
+}), common.platformTimeout(100));
diff --git a/test/parallel/test-zlib-params.js b/test/parallel/test-zlib-params.js
index 30d4f13..18271fe 100644
--- a/test/parallel/test-zlib-params.js
+++ b/test/parallel/test-zlib-params.js
@@ -12,23 +12,29 @@ const deflater = zlib.createDeflate(opts);
const chunk1 = file.slice(0, chunkSize);
const chunk2 = file.slice(chunkSize);
const blkhdr = Buffer.from([0x00, 0x5a, 0x82, 0xa5, 0x7d]);
-const expected = Buffer.concat([blkhdr, chunk2]);
-let actual;
+const blkftr = Buffer.from('010000ffff7dac3072', 'hex');
+const expected = Buffer.concat([blkhdr, chunk2, blkftr]);
+const bufs = [];
+
+function read() {
+ let buf;
+ while ((buf = deflater.read()) !== null) {
+ bufs.push(buf);
+ }
+}
deflater.write(chunk1, function() {
deflater.params(0, zlib.constants.Z_DEFAULT_STRATEGY, function() {
while (deflater.read());
- deflater.end(chunk2, function() {
- const bufs = [];
- let buf;
- while ((buf = deflater.read()) !== null)
- bufs.push(buf);
- actual = Buffer.concat(bufs);
- });
+
+ deflater.on('readable', read);
+
+ deflater.end(chunk2);
});
while (deflater.read());
});
process.once('exit', function() {
+ const actual = Buffer.concat(bufs);
assert.deepStrictEqual(actual, expected);
});
--
2.44.0