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.
151 lines
30 KiB
151 lines
30 KiB
6 months ago
|
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
|
||
|
|