From 09b0469fafc57e4173532d600f1f7c445cc73f94 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Wed, 22 Apr 2026 12:43:16 +0200 Subject: [PATCH] Fixed bz2 decompression bomb vulnerability in Resource transfer assembly and Buffer StreamDataMessage unpacking. --- RNS/Buffer.py | 4 +++- RNS/Resource.py | 20 +++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/RNS/Buffer.py b/RNS/Buffer.py index 20780e2..b128a6c 100644 --- a/RNS/Buffer.py +++ b/RNS/Buffer.py @@ -92,7 +92,9 @@ class StreamDataMessage(MessageBase): self.data = raw[2:] if self.compressed: - self.data = bz2.decompress(self.data) + decompressor = bz2.BZ2Decompressor() + self.data = decompressor.decompress(self.data, max_length=RawChannelWriter.MAX_CHUNK_LEN) + if not decompressor.eof: raise IOError("Decompressed buffer chunk exceeds maximum legitimate size") class RawChannelReader(RawIOBase, AbstractContextManager): diff --git a/RNS/Resource.py b/RNS/Resource.py index 975734b..18daf2d 100644 --- a/RNS/Resource.py +++ b/RNS/Resource.py @@ -194,6 +194,7 @@ class Resource: resource.window_flexibility = Resource.WINDOW_FLEXIBILITY resource.last_activity = time.time() resource.started_transferring = resource.last_activity + resource.advertisement_packet = advertisement_packet resource.storagepath = RNS.Reticulum.resourcepath+"/"+resource.original_hash.hex() resource.meta_storagepath = resource.storagepath+".meta" @@ -360,6 +361,7 @@ class Resource: self.request_id = request_id self.started_transferring = None self.is_response = is_response + self.max_decompressed_size = Resource.AUTO_COMPRESS_MAX_SIZE self.auto_compress_limit = Resource.AUTO_COMPRESS_MAX_SIZE self.auto_compress_option = auto_compress @@ -679,6 +681,16 @@ class Resource: # Strip off random hash data = data[Resource.RANDOM_HASH_SIZE:] + if not self.compressed: self.data = data + else: + decompressor = bz2.BZ2Decompressor() + self.data = decompressor.decompress(data, max_length=self.max_decompressed_size) + if not decompressor.eof: + self.status = Resource.CORRUPT + self.cancel() + RNS.log(f"Decompressed resource exceeded maximum decompressed size. The resource was rejected.", RNS.LOG_ERROR) + return + if self.compressed: self.data = bz2.decompress(data) else: self.data = data @@ -1069,7 +1081,13 @@ class Resource: Cancels transferring the resource. """ if self.next_segment: self.next_segment.cancel() - if self.status < Resource.COMPLETE: + + if self.status == Resource.CORRUPT: + self.link.cancel_incoming_resource(self) + self.reject(self.advertisement_packet) + self.link.teardown() + + elif self.status < Resource.COMPLETE: self.status = Resource.FAILED if self.initiator: if self.link.status == RNS.Link.ACTIVE: