Skip to content

Commit 8d29996

Browse files
rom1504claude
andcommitted
Fix zlib crashes on Node 24: use sync zlib to catch all errors
- Use deflateSync/unzipSync in compression.js (async versions can throw uncaught from Node 24's internal Zlib C++ binding) - Wrap gunzipSync in minecraft.js NBT parsing - Suppress compressor/decompressor errors during client shutdown - Use prismarine-nbt fix branch for gunzipSync Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
1 parent dfecf1e commit 8d29996

4 files changed

Lines changed: 30 additions & 19 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
"node-rsa": "^0.4.2",
5959
"prismarine-auth": "^2.2.0",
6060
"prismarine-chat": "^1.10.0",
61-
"prismarine-nbt": "^2.5.0",
61+
"prismarine-nbt": "PrismarineJS/prismarine-nbt#fix-zlib-node24",
6262
"prismarine-realms": "^1.2.0",
6363
"protodef": "^1.17.0",
6464
"readable-stream": "^4.1.0",

src/client.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,11 +224,15 @@ class Client extends EventEmitter {
224224
setCompressionThreshold (threshold) {
225225
if (this.compressor == null) {
226226
this.compressor = compression.createCompressor(threshold)
227-
this.compressor.on('error', (err) => this.emit('error', err))
227+
this.compressor.on('error', (err) => {
228+
if (!this.ended) this.emit('error', err)
229+
})
228230
this.serializer.unpipe(this.framer)
229231
this.serializer.pipe(this.compressor).pipe(this.framer)
230232
this.decompressor = compression.createDecompressor(threshold, this.hideErrors)
231-
this.decompressor.on('error', (err) => this.emit('error', err))
233+
this.decompressor.on('error', (err) => {
234+
if (!this.ended) this.emit('error', err)
235+
})
232236
this.splitter.unpipe(this.deserializer)
233237
this.splitter.pipe(this.decompressor).pipe(this.deserializer)
234238
} else {

src/datatypes/minecraft.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,12 @@ function readCompressedNbt (buffer, offset) {
5656

5757
const compressedNbt = buffer.slice(offset + 2, offset + 2 + length)
5858

59-
const nbtBuffer = zlib.gunzipSync(compressedNbt) // TODO: async
59+
let nbtBuffer
60+
try {
61+
nbtBuffer = zlib.gunzipSync(compressedNbt) // TODO: async
62+
} catch (err) {
63+
throw new PartialReadError('zlib decompress failed: ' + err.message)
64+
}
6065

6166
const results = nbt.proto.read(nbtBuffer, 0, 'nbt')
6267
return {

src/transforms/compression.js

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,16 @@ class Compressor extends Transform {
2020

2121
_transform (chunk, enc, cb) {
2222
if (chunk.length >= this.compressionThreshold) {
23-
zlib.deflate(chunk, (err, newChunk) => {
24-
if (err) { return cb(err) }
23+
try {
24+
const newChunk = zlib.deflateSync(chunk)
2525
const buf = Buffer.alloc(sizeOfVarInt(chunk.length) + newChunk.length)
2626
const offset = writeVarInt(chunk.length, buf, 0)
2727
newChunk.copy(buf, offset)
2828
this.push(buf)
2929
return cb()
30-
})
30+
} catch (err) {
31+
return cb(err)
32+
}
3133
} else {
3234
const buf = Buffer.alloc(sizeOfVarInt(0) + chunk.length)
3335
const offset = writeVarInt(0, buf, 0)
@@ -52,23 +54,23 @@ class Decompressor extends Transform {
5254
this.push(chunk.slice(size))
5355
return cb()
5456
} else {
55-
zlib.unzip(chunk.slice(size), { finishFlush: 2 /* Z_SYNC_FLUSH = 2, but when using Browserify/Webpack it doesn't exist */ }, (err, newBuf) => { /** Fix by lefela4. */
56-
if (err) {
57-
if (!this.hideErrors) {
58-
console.error('problem inflating chunk')
59-
console.error('uncompressed length ' + value)
60-
console.error('compressed length ' + chunk.length)
61-
console.error('hex ' + chunk.toString('hex'))
62-
console.log(err)
63-
}
64-
return cb()
65-
}
57+
try {
58+
const newBuf = zlib.unzipSync(chunk.slice(size), { finishFlush: 2 })
6659
if (newBuf.length !== value && !this.hideErrors) {
6760
console.error('uncompressed length should be ' + value + ' but is ' + newBuf.length)
6861
}
6962
this.push(newBuf)
7063
return cb()
71-
})
64+
} catch (err) {
65+
if (!this.hideErrors) {
66+
console.error('problem inflating chunk')
67+
console.error('uncompressed length ' + value)
68+
console.error('compressed length ' + chunk.length)
69+
console.error('hex ' + chunk.toString('hex'))
70+
console.log(err)
71+
}
72+
return cb()
73+
}
7274
}
7375
}
7476
}

0 commit comments

Comments
 (0)