changeset 6615:b7243c4efe18 limited-memory

limit memory when uploading zip
author Alain Mazy <am@orthanc.team>
date Thu, 05 Mar 2026 15:40:24 +0100
parents f269e4778b59
children a0bbb4d460b8
files OrthancFramework/Sources/Compression/ZipReader.cpp OrthancFramework/Sources/Compression/ZipReader.h OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp OrthancFramework/Sources/SystemToolbox.cpp OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp OrthancServer/Sources/OrthancWebDav.cpp OrthancServer/Sources/main.cpp
diffstat 7 files changed, 71 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancFramework/Sources/Compression/ZipReader.cpp	Wed Mar 04 16:28:51 2026 +0100
+++ b/OrthancFramework/Sources/Compression/ZipReader.cpp	Thu Mar 05 15:40:24 2026 +0100
@@ -365,6 +365,59 @@
     }
   }    
 
+  // TODO-MEM: find a way to share code between these 2 overloads
+  bool ZipReader::ReadNextFile(std::string& filename,
+                               MemoryManagedString& content)
+  {
+    assert(pimpl_->unzip_ != NULL);
+
+    if (pimpl_->done_)
+    {
+      return false;
+    }
+    else
+    {
+      unz_file_info64_s info;
+      if (unzGetCurrentFileInfo64(pimpl_->unzip_, &info, NULL, 0, NULL, 0, NULL, 0) != 0)
+      {
+        throw OrthancException(ErrorCode_BadFileFormat);
+      }
+
+      filename.resize(info.size_filename);
+      if (!filename.empty() &&
+          unzGetCurrentFileInfo64(pimpl_->unzip_, &info, &filename[0],
+                                  static_cast<uLong>(filename.size()), NULL, 0, NULL, 0) != 0)
+      {
+        throw OrthancException(ErrorCode_BadFileFormat);
+      }
+
+      content.resize(info.uncompressed_size);
+
+      if (!content.empty())
+      {
+        if (unzOpenCurrentFile(pimpl_->unzip_) == 0)
+        {
+          bool success = (unzReadCurrentFile(pimpl_->unzip_, &content[0],
+                                             static_cast<uLong>(content.size())) != 0);
+                          
+          if (unzCloseCurrentFile(pimpl_->unzip_) != 0 ||
+              !success)
+          {
+            throw OrthancException(ErrorCode_BadFileFormat);
+          }
+        }
+        else
+        {
+          throw OrthancException(ErrorCode_BadFileFormat, "Invalid file or unsupported compression method (e.g. Deflate64)");
+        }
+      }
+      
+      pimpl_->done_ = (unzGoToNextFile(pimpl_->unzip_) != 0);
+ 
+      return true;
+    }
+  }    
+
   
   ZipReader* ZipReader::CreateFromMemory(const void* buffer,
                                          size_t size)
--- a/OrthancFramework/Sources/Compression/ZipReader.h	Wed Mar 04 16:28:51 2026 +0100
+++ b/OrthancFramework/Sources/Compression/ZipReader.h	Thu Mar 05 15:40:24 2026 +0100
@@ -43,6 +43,7 @@
 #include <string>
 #include <boost/noncopyable.hpp>
 #include <boost/shared_ptr.hpp>
+#include "../MemoryManagedString.h"
 
 #if ORTHANC_SANDBOXED != 1
 #  include <boost/filesystem.hpp>
@@ -69,7 +70,10 @@
 
     bool ReadNextFile(std::string& filename,
                       std::string& content);
-    
+
+    bool ReadNextFile(std::string& filename,
+                      MemoryManagedString& content);
+                      
     static ZipReader* CreateFromMemory(const void* buffer,
                                        size_t size);
 
--- a/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp	Wed Mar 04 16:28:51 2026 +0100
+++ b/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp	Thu Mar 05 15:40:24 2026 +0100
@@ -2503,6 +2503,11 @@
   DcmFileFormat* FromDcmtkBridge::LoadFromMemoryBuffer(const void* buffer,
                                                        size_t size)
   {
+    if (!DicomMap::IsDicomFile(buffer, size))
+    {
+      throw OrthancException(ErrorCode_BadFileFormat, "Not a DICOM file");
+    }
+
     DcmInputBufferStream is;
     if (size > 0)
     {
--- a/OrthancFramework/Sources/SystemToolbox.cpp	Wed Mar 04 16:28:51 2026 +0100
+++ b/OrthancFramework/Sources/SystemToolbox.cpp	Thu Mar 05 15:40:24 2026 +0100
@@ -266,7 +266,7 @@
     }
   }
 
-
+  // TODO-MEM: find a way to share code between these 2 overloads
   void SystemToolbox::ReadFile(MemoryManagedString& content,
                                const boost::filesystem::path& path,
                                bool log)
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp	Wed Mar 04 16:28:51 2026 +0100
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp	Thu Mar 05 15:40:24 2026 +0100
@@ -177,14 +177,15 @@
 
       Json::Value answer = Json::arrayValue;
       
-      std::string filename, content;
+      std::string filename;
+      MemoryManagedString content;
       while (reader->ReadNextFile(filename, content))
       {
         if (!content.empty())
         {
           LOG(INFO) << "Uploading DICOM file from ZIP archive: " << filename;
 
-          std::unique_ptr<DicomInstanceToStore> toStore(DicomInstanceToStore::CreateFromBuffer(content));
+          std::unique_ptr<DicomInstanceToStore> toStore(DicomInstanceToStore::CreateFromBuffer(content.c_str(), content.size()));
           toStore->SetOrigin(DicomInstanceOrigin::FromRest(call));
 
           try
--- a/OrthancServer/Sources/OrthancWebDav.cpp	Wed Mar 04 16:28:51 2026 +0100
+++ b/OrthancServer/Sources/OrthancWebDav.cpp	Thu Mar 05 15:40:24 2026 +0100
@@ -1213,7 +1213,7 @@
         std::unique_ptr<ZipReader> reader(ZipReader::CreateFromMemory(content));
 
         std::string filename, uncompressedFile;
-        while (reader->ReadNextFile(filename, uncompressedFile))
+        while (reader->ReadNextFile(filename, uncompressedFile))  // TODO-MEM: use MemoryManagedString
         {
           if (!uncompressedFile.empty())
           {
--- a/OrthancServer/Sources/main.cpp	Wed Mar 04 16:28:51 2026 +0100
+++ b/OrthancServer/Sources/main.cpp	Thu Mar 05 15:40:24 2026 +0100
@@ -1949,7 +1949,9 @@
   Logging::SetCurrentThreadName("MAIN");
   SetGlobalVerbosity(Verbosity_Default);
 
-  LimitedMemoryAllocator::Initialize(8ul * 1024ul * 1024ul * 1024ul);  // TODO-MEM: get this value from the configuration file
+  uint64_t maxMemorySize = 8ul * 1024ul * 1024ul * 1024ul;  // TODO-MEM: get this value from the configuration file
+  maxMemorySize = std::min(maxMemorySize, std::numeric_limits<size_t>::max()); // on 32 bits system, limit the value to 4GB
+  LimitedMemoryAllocator::Initialize(static_cast<size_t>(maxMemorySize));
 
   bool upgradeDatabase = false;
   bool loadJobsFromDatabase = true;