Commit c1cbf1f7780 for nodejs
commit c1cbf1f7780cca3c5dfebdd4b0b5a414134817d4
Author: Marco <marco.piraccini@gmail.com>
Date: Thu Oct 29 09:56:38 2026 +0100
zlib: restart format detection when resetting Unzip
Signed-off-by: marcopiraccini <marco.piraccini@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66169
Fixes: https://github.com/nodejs/node/issues/64619
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
diff --git a/src/node_zlib.cc b/src/node_zlib.cc
index a34905c3e3a..49e2a6e4ddf 100644
--- a/src/node_zlib.cc
+++ b/src/node_zlib.cc
@@ -204,7 +204,10 @@ class ZlibContext final : public MemoryRetainer {
void SetFlush(int flush);
void GetAfterWriteOffsets(uint32_t* avail_in, uint32_t* avail_out) const;
CompressionError GetErrorInfo() const;
- inline void SetMode(node_zlib_mode mode) { mode_ = mode; }
+ inline void SetMode(node_zlib_mode mode) {
+ mode_ = mode;
+ auto_detect_ = mode == UNZIP;
+ }
CompressionError ResetStream();
// Zlib-specific:
@@ -229,6 +232,7 @@ class ZlibContext final : public MemoryRetainer {
private:
CompressionError ErrorForMessage(const char* message) const;
CompressionError SetDictionary();
+ CompressionError ResetZStream();
bool InitZlib();
Mutex mutex_; // Protects zlib_init_done_.
@@ -238,6 +242,7 @@ class ZlibContext final : public MemoryRetainer {
int level_ = 0;
int mem_level_ = 0;
node_zlib_mode mode_ = NONE;
+ bool auto_detect_ = false;
int strategy_ = 0;
int window_bits_ = 0;
bool reject_garbage_after_end_ = false;
@@ -1215,7 +1220,7 @@ void ZlibContext::DoThreadPoolWork() {
// Trailing zero bytes are okay, though, since they are frequently
// used for padding.
- ResetStream();
+ ResetZStream();
err_ = inflate(&strm_, flush_);
}
break;
@@ -1294,6 +1299,14 @@ CompressionError ZlibContext::ResetStream() {
Z_STREAM_ERROR);
}
+ if (auto_detect_) {
+ mode_ = UNZIP;
+ gzip_id_bytes_read_ = 0;
+ }
+ return ResetZStream();
+}
+
+CompressionError ZlibContext::ResetZStream() {
bool first_init_call = InitZlib();
if (first_init_call && err_ != Z_OK) {
return ErrorForMessage("Failed to init stream before reset");
@@ -1310,6 +1323,7 @@ CompressionError ZlibContext::ResetStream() {
case INFLATE:
case INFLATERAW:
case GUNZIP:
+ case UNZIP:
err_ = inflateReset(&strm_);
break;
default:
diff --git a/test/parallel/test-zlib-reset-unzip.js b/test/parallel/test-zlib-reset-unzip.js
new file mode 100644
index 00000000000..6e513678262
--- /dev/null
+++ b/test/parallel/test-zlib-reset-unzip.js
@@ -0,0 +1,38 @@
+'use strict';
+
+// Refs: https://github.com/nodejs/node/issues/64619
+
+const common = require('../common');
+const assert = require('assert');
+const zlib = require('zlib');
+
+function resetAfter(prefix, input, expected) {
+ const chunks = [];
+ const unzip = zlib.createUnzip();
+ unzip.on('error', common.mustNotCall());
+ unzip.on('data', (chunk) => chunks.push(chunk));
+ unzip.on('end', common.mustCall(() => {
+ assert.strictEqual(Buffer.concat(chunks).toString(), expected);
+ }));
+ unzip.write(prefix, common.mustCall(() => {
+ unzip.reset();
+ unzip.end(input);
+ }));
+}
+
+// Reset in the middle of the gzip magic number.
+resetAfter(Buffer.from([0x1f]), zlib.gzipSync('hello'), 'hello');
+
+// Reset after deflate was detected.
+resetAfter(
+ zlib.deflateSync('discarded').subarray(0, 2),
+ Buffer.concat([zlib.gzipSync('abc'), zlib.gzipSync('def'), zlib.gzipSync('ghi')]),
+ 'abcdefghi',
+);
+
+// Reset after gzip was detected.
+resetAfter(
+ zlib.gzipSync('discarded').subarray(0, 4),
+ zlib.deflateSync('hello'),
+ 'hello',
+);