From e9a6f3214cd54aba2dfe697bef6db74ea4af1fec Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Fri, 31 Jan 2025 09:58:31 +0000 Subject: [PATCH 1/4] Add comment for where to apply the fix --- volatility3/framework/layers/resources.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/volatility3/framework/layers/resources.py b/volatility3/framework/layers/resources.py index 236d256f1..bf78f1c92 100644 --- a/volatility3/framework/layers/resources.py +++ b/volatility3/framework/layers/resources.py @@ -204,6 +204,8 @@ class ResourceAccessor: # open it in read mode only and allow breakages to happen if they wanted to write curfile = open(temp_filename, mode="rb") + # Validate the hash or delete the temp_filename and report an error + # Determine whether the file is a particular type of file, and if so, open it as such IMPORTED_MAGIC = False if HAS_MAGIC: From c269086402a0f9cc44aae11228d92ada515d0839 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 23 Feb 2025 20:31:07 +0000 Subject: [PATCH 2/4] Core: Improve caching to only allow one open file write at a time --- volatility3/framework/constants/__init__.py | 3 +++ volatility3/framework/layers/resources.py | 18 +++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/constants/__init__.py b/volatility3/framework/constants/__init__.py index 2e6ae0261..429b79c3c 100644 --- a/volatility3/framework/constants/__init__.py +++ b/volatility3/framework/constants/__init__.py @@ -120,6 +120,9 @@ OFFLINE = False REMOTE_ISF_URL = None # 'http://localhost:8000/banners.json' """Remote URL to query for a list of ISF addresses""" +DOWNLOAD_TIMEOUT = 30 +"""Length of time (in seconds) to wait for another process to download a resource before using it""" + ### # DEPRECATED VALUES ### diff --git a/volatility3/framework/layers/resources.py b/volatility3/framework/layers/resources.py index bf78f1c92..5b2e20bfd 100644 --- a/volatility3/framework/layers/resources.py +++ b/volatility3/framework/layers/resources.py @@ -2,6 +2,7 @@ # which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 # +import time import bz2 import contextlib import gzip @@ -198,7 +199,22 @@ class ResourceAccessor: cache_file.write(block) block = fp.read(block_size) else: - vollog.debug(f"Using already cached file at: {temp_filename}") + vollog.debug( + f"Trying to use already cached file at: {temp_filename}" + ) + count = 0 + fp.seek(0, os.SEEK_END) + expected_filesize = fp.tell() + stop = False + while count < constants.DOWNLAOD_TIMEOUT and not stop: + time.sleep(1) + if os.stat(temp_filename).st_size == expected_filesize: + stop = True + if not stop: + raise ValueError( + f"Cached file existed, but was not the correct filesize, even after {constants.DOWNLOAD_TIMEOUT} seconds" + ) + # Re-open the cache with a different mode # Since we don't want people thinking they're able to save to the cache file, # open it in read mode only and allow breakages to happen if they wanted to write From 2ff83c4434782872a25e7f152cf1283d213762c3 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 23 Feb 2025 20:50:35 +0000 Subject: [PATCH 3/4] Core: Ensure cached files aren't saved if incomplete --- volatility3/framework/layers/resources.py | 51 +++++++++++------------ 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/volatility3/framework/layers/resources.py b/volatility3/framework/layers/resources.py index 5b2e20bfd..273f44cd7 100644 --- a/volatility3/framework/layers/resources.py +++ b/volatility3/framework/layers/resources.py @@ -178,42 +178,39 @@ class ResourceAccessor: + ".cache", ) + try: + content_length = int(fp.info().get("Content-Length", -1)) + except (AttributeError, ValueError): + # If our fp doesn't have an info member, carry on gracefully + content_length = -1 + if not os.path.exists(temp_filename): vollog.debug(f"Caching file at: {temp_filename}") + cache_file_size = -1 try: - content_length = fp.info().get("Content-Length", -1) - except AttributeError: - # If our fp doesn't have an info member, carry on gracefully - content_length = -1 - with open(temp_filename, "wb") as cache_file: - count = 0 - block = fp.read(block_size) - while block: - count += len(block) - if self._progress_callback: - self._progress_callback( - count * 100 / max(count, int(content_length)), - f"Reading file {url}", - ) - cache_file.write(block) + with open(temp_filename, "wb") as cache_file: + count = 0 block = fp.read(block_size) + while block: + count += len(block) + if self._progress_callback: + self._progress_callback( + count * 100 / max(count, int(content_length)), + f"Reading file {url}", + ) + cache_file.write(block) + block = fp.read(block_size) + cache_file.seek(0, os.SEEK_END) + cache_file_size = cache_file.tell() + finally: + if cache_file_size < content_length: + os.remove(temp_filename) + raise ValueError("Cached file did not download completely") else: vollog.debug( f"Trying to use already cached file at: {temp_filename}" ) - count = 0 - fp.seek(0, os.SEEK_END) - expected_filesize = fp.tell() - stop = False - while count < constants.DOWNLAOD_TIMEOUT and not stop: - time.sleep(1) - if os.stat(temp_filename).st_size == expected_filesize: - stop = True - if not stop: - raise ValueError( - f"Cached file existed, but was not the correct filesize, even after {constants.DOWNLOAD_TIMEOUT} seconds" - ) # Re-open the cache with a different mode # Since we don't want people thinking they're able to save to the cache file, From 48894dae0c7b9164c24e8a9094135677877711ad Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 23 Feb 2025 20:56:17 +0000 Subject: [PATCH 4/4] Core: Fix up ruff issue from #1631 --- volatility3/framework/layers/resources.py | 1 - 1 file changed, 1 deletion(-) diff --git a/volatility3/framework/layers/resources.py b/volatility3/framework/layers/resources.py index 273f44cd7..6121c2cff 100644 --- a/volatility3/framework/layers/resources.py +++ b/volatility3/framework/layers/resources.py @@ -2,7 +2,6 @@ # which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 # -import time import bz2 import contextlib import gzip