[med-svn] [Git][med-team/orthanc][master] 2 commits: fix issue #1133182

Sebastien Jodogne (@jodogne-guest) gitlab at salsa.debian.org
Sat Apr 11 13:02:47 BST 2026



Sebastien Jodogne pushed to branch master at Debian Med / orthanc


Commits:
d7e640a8 by jodogne-guest at 2026-04-11T13:38:10+02:00
fix issue #1133182

- - - - -
d1c1ce15 by jodogne-guest at 2026-04-11T14:01:33+02:00
Upload to unstable

- - - - -


4 changed files:

- debian/changelog
- debian/patches/libminizip
- + debian/patches/security
- debian/patches/series


Changes:

=====================================
debian/changelog
=====================================
@@ -1,3 +1,11 @@
+orthanc (1.12.10+dfsg-4) unstable; urgency=high
+
+  * Fix CVE-2026-5437, CVE-2026-5438, CVE-2026-5439, CVE-2026-5440,
+    CVE-2026-5441, CVE-2026-5442, CVE-2026-5443, CVE-2026-5444, and
+    CVE-2026-5445. (Closes: #1133182)
+
+ -- Sebastien Jodogne <s.jodogne at gmail.com>  Sat, 11 Apr 2026 13:35:51 +0200
+
 orthanc (1.12.10+dfsg-3) unstable; urgency=medium
 
   * Team upload.


=====================================
debian/patches/libminizip
=====================================
@@ -32,19 +32,6 @@ Index: Orthanc-1.12.10/OrthancFramework/Resources/CMake/OrthancFrameworkConfigur
  endif()
  
  
-Index: Orthanc-1.12.10/OrthancFramework/Sources/Compression/ZipReader.cpp
-===================================================================
---- Orthanc-1.12.10.orig/OrthancFramework/Sources/Compression/ZipReader.cpp
-+++ Orthanc-1.12.10/OrthancFramework/Sources/Compression/ZipReader.cpp
-@@ -31,7 +31,7 @@
- #include "ZipReader.h"
- 
- #include "../OrthancException.h"
--#include "../../Resources/ThirdParty/minizip/unzip.h"
-+#include <minizip/unzip.h>
- 
- #if ORTHANC_SANDBOXED != 1
- #  include "../SystemToolbox.h"
 Index: Orthanc-1.12.10/OrthancFramework/Sources/Compression/ZipWriter.cpp
 ===================================================================
 --- Orthanc-1.12.10.orig/OrthancFramework/Sources/Compression/ZipWriter.cpp


=====================================
debian/patches/security
=====================================
@@ -0,0 +1,1326 @@
+Description: Fix CVE-2026-5437, CVE-2026-5438, CVE-2026-5439, CVE-2026-5440, CVE-2026-5441, CVE-2026-5442, CVE-2026-5443, CVE-2026-5444, and CVE-2026-5445
+Author: Sebastien Jodogne <s.jodogne at orthanc-labs.com>
+Forwarded: https://orthanc.uclouvain.be/hg/orthanc/rev/5ce108190752
+---
+This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
+Index: Orthanc-1.12.10/OrthancFramework/Sources/Compatibility.h
+===================================================================
+--- Orthanc-1.12.10.orig/OrthancFramework/Sources/Compatibility.h
++++ Orthanc-1.12.10/OrthancFramework/Sources/Compatibility.h
+@@ -72,10 +72,12 @@
+ // The override keyword (C++11) is enabled
+ #  define ORTHANC_OVERRIDE  override 
+ #  define ORTHANC_FINAL     final
++#  define ORTHANC_NOEXCEPT  noexcept
+ #else
+ // The override keyword (C++11) is not available
+ #  define ORTHANC_OVERRIDE
+ #  define ORTHANC_FINAL
++#  define ORTHANC_NOEXCEPT
+ #endif
+ 
+ 
+Index: Orthanc-1.12.10/OrthancFramework/Sources/Compression/GzipCompressor.cpp
+===================================================================
+--- Orthanc-1.12.10.orig/OrthancFramework/Sources/Compression/GzipCompressor.cpp
++++ Orthanc-1.12.10/OrthancFramework/Sources/Compression/GzipCompressor.cpp
+@@ -29,8 +29,16 @@
+ #include <string.h>
+ #include <zlib.h>
+ 
+-#include "../OrthancException.h"
++#include "../ChunkedBuffer.h"
+ #include "../Logging.h"
++#include "../MultiThreading/ReaderWriterLock.h"
++#include "../OrthancException.h"
++
++
++static Orthanc::ReaderWriterLock maximumUncompressedFileSizeMutex_;
++static bool hasMaximumUncompressedFileSize_ = false;
++static size_t maximumUncompressedFileSize_ = 0;
++
+ 
+ namespace Orthanc
+ {
+@@ -177,100 +185,149 @@ namespace Orthanc
+   }
+ 
+ 
++  namespace
++  {
++    class GzipRaii : public boost::noncopyable
++    {
++    private:
++      z_stream&  stream_;
++
++    public:
++      GzipRaii(z_stream& stream) :
++        stream_(stream)
++      {
++        int error = inflateInit2(&stream_,
++                                 MAX_WBITS + 16);  // this is a gzip input
++
++        if (error != Z_OK)
++        {
++          throw OrthancException(ErrorCode_InternalError, "Cannot initialize zlib");
++        }
++      }
++
++      ~GzipRaii()
++      {
++        inflateEnd(&stream_);
++      }
++    };
++  }
++
++
+   void GzipCompressor::Uncompress(std::string& uncompressed,
+                                   const void* compressed,
+                                   size_t compressedSize)
+   {
+-    uint64_t uncompressedSize;
+     const uint8_t* source = reinterpret_cast<const uint8_t*>(compressed);
+ 
++    bool hasMaximumSize = false;
++    size_t maximumSize = 0;
++
+     if (HasPrefixWithUncompressedSize())
+     {
+-      uncompressedSize = ReadUncompressedSizePrefix(compressed, compressedSize);
++      hasMaximumSize = true;
++      maximumSize = ReadUncompressedSizePrefix(compressed, compressedSize);
+       source += sizeof(uint64_t);
+       compressedSize -= sizeof(uint64_t);
+     }
+     else
+     {
+-      uncompressedSize = GuessUncompressedSize(compressed, compressedSize);
+-    }
++      ReaderWriterLock::ReadLock lock(maximumUncompressedFileSizeMutex_);
+ 
+-    try
+-    {
+-      uncompressed.resize(static_cast<size_t>(uncompressedSize));
+-    }
+-    catch (...)
+-    {
+-      throw OrthancException(ErrorCode_NotEnoughMemory);
++      if (hasMaximumUncompressedFileSize_)
++      {
++        hasMaximumSize = true;
++        maximumSize = maximumUncompressedFileSize_;
++      }
+     }
+ 
+     z_stream stream;
+     memset(&stream, 0, sizeof(stream));
+ 
+-    char dummy = '\0';  // zlib does not like NULL output buffers (even if the uncompressed data is empty)
+     stream.next_in = const_cast<Bytef*>(source);
+-    stream.next_out = reinterpret_cast<Bytef*>(uncompressedSize == 0 ? &dummy : &uncompressed[0]);
+-
+     stream.avail_in = static_cast<uInt>(compressedSize);
+-    stream.avail_out = static_cast<uInt>(uncompressedSize);
+ 
+-    // Ensure no overflow (if the buffer is too large for the current archicture)
+-    if (static_cast<size_t>(stream.avail_in) != compressedSize ||
+-        static_cast<size_t>(stream.avail_out) != uncompressedSize)
++    // Ensure no overflow (if the buffer is too large for the current zlib archicture)
++    if (static_cast<size_t>(stream.avail_in) != compressedSize)
+     {
+       throw OrthancException(ErrorCode_NotEnoughMemory);
+     }
+ 
+-    // Initialize the compression engine
+-    int error = inflateInit2(&stream, 
+-                             MAX_WBITS + 16);  // this is a gzip input
++    ChunkedBuffer buffer;
+ 
+-    if (error != Z_OK)
+     {
+-      // Cannot initialize zlib
+-      uncompressed.clear();
+-      throw OrthancException(ErrorCode_InternalError);
+-    }
++      GzipRaii raii(stream);
+ 
+-    // Uncompress the input buffer
+-    error = inflate(&stream, Z_FINISH);
++      std::string chunk;
++      chunk.resize(10 * 1024 * 1024); // Read by chunks of 10MB
+ 
+-    if (error != Z_STREAM_END)
+-    {
+-      inflateEnd(&stream);
+-      uncompressed.clear();
++      int ret = Z_OK;
+ 
+-      switch (error)
++      while (ret != Z_STREAM_END)
+       {
++        stream.next_out  = const_cast<Bytef*>(reinterpret_cast<const Bytef*>(chunk.data()));
++        stream.avail_out = static_cast<uInt>(chunk.size());
++
++        ret = inflate(&stream, Z_NO_FLUSH);
++
++        switch (ret)
++        {
++        case Z_STREAM_END:
++        case Z_OK:
++          break; // Normal
++
++        case Z_NEED_DICT:
++        case Z_DATA_ERROR:
++        case Z_STREAM_ERROR:
++          throw OrthancException(ErrorCode_BadFileFormat);
++
+         case Z_MEM_ERROR:
+           throw OrthancException(ErrorCode_NotEnoughMemory);
+-          
++
+         case Z_BUF_ERROR:
+-        case Z_NEED_DICT:
+-          throw OrthancException(ErrorCode_BadFileFormat);
+-          
++          // Not fatal: means no progress was possible this round.
++          // If avail_in is also 0 here, the input is truncated.
++          if (stream.avail_in == 0)
++          {
++            throw OrthancException(ErrorCode_BadFileFormat, "Truncated gzip input");
++          }
++          break;
++
+         default:
+-          throw OrthancException(ErrorCode_InternalError);
++          throw OrthancException(ErrorCode_InternalError); // Unknown error
++        }
++
++        const size_t produced = chunk.size() - stream.avail_out;
++
++        if (hasMaximumSize &&
++            buffer.GetNumBytes() + produced > maximumSize)
++        {
++          char s[32];
++          sprintf(s, "%0.1f", static_cast<float>(maximumSize) / (1024.0f * 1024.0f));
++          throw OrthancException(ErrorCode_BadFileFormat, "Uncompressed size exceeds limit (" + std::string(s) + "MB)");
++        }
++        else
++        {
++          // OK, add the chunk
++          buffer.AddChunk(&chunk[0], produced);
++        }
+       }
+     }
+ 
+-    size_t size = stream.total_out;
++    buffer.Flatten(uncompressed);
++  }
+ 
+-    if (inflateEnd(&stream) != Z_OK)
++
++  void GzipCompressor::SetMaximumUncompressedFileSize(uint64_t size)
++  {
++    if (static_cast<uint64_t>(static_cast<size_t>(size)) != size)
+     {
+-      uncompressed.clear();
+-      throw OrthancException(ErrorCode_InternalError);
++      throw OrthancException(ErrorCode_NotEnoughMemory);
+     }
+-
+-    if (size != uncompressedSize)
++    else
+     {
+-      uncompressed.clear();
+-
+-      // The uncompressed size was not that properly guess, presumably
+-      // because of a file size over 4GB. Should fallback to
+-      // stream-based decompression.
+-      throw OrthancException(ErrorCode_NotImplemented,
+-                             "The uncompressed size of a gzip-encoded buffer was not properly guessed");
++      ReaderWriterLock::WriteLock lock(maximumUncompressedFileSizeMutex_);
++      hasMaximumUncompressedFileSize_ = true;
++      maximumUncompressedFileSize_ = size;
+     }
+   }
+ }
+Index: Orthanc-1.12.10/OrthancFramework/Sources/Compression/GzipCompressor.h
+===================================================================
+--- Orthanc-1.12.10.orig/OrthancFramework/Sources/Compression/GzipCompressor.h
++++ Orthanc-1.12.10/OrthancFramework/Sources/Compression/GzipCompressor.h
+@@ -45,5 +45,7 @@ namespace Orthanc
+     virtual void Uncompress(std::string& uncompressed,
+                             const void* compressed,
+                             size_t compressedSize) ORTHANC_OVERRIDE;
++
++    static void SetMaximumUncompressedFileSize(uint64_t size);
+   };
+ }
+Index: Orthanc-1.12.10/OrthancFramework/Sources/Compression/ZipReader.cpp
+===================================================================
+--- Orthanc-1.12.10.orig/OrthancFramework/Sources/Compression/ZipReader.cpp
++++ Orthanc-1.12.10/OrthancFramework/Sources/Compression/ZipReader.cpp
+@@ -30,8 +30,10 @@
+ 
+ #include "ZipReader.h"
+ 
++#include "../ChunkedBuffer.h"
+ #include "../OrthancException.h"
+-#include "../../Resources/ThirdParty/minizip/unzip.h"
++#include "../MultiThreading/ReaderWriterLock.h"
++#include <minizip/unzip.h>
+ 
+ #if ORTHANC_SANDBOXED != 1
+ #  include "../SystemToolbox.h"
+@@ -56,6 +58,11 @@ typedef ssize_t SSIZE_T;
+ #include <string.h>
+ 
+ 
++static Orthanc::ReaderWriterLock maximumUncompressedFileSizeMutex_;
++static bool hasMaximumUncompressedFileSize_ = false;
++static size_t maximumUncompressedFileSize_ = 0;
++
++
+ namespace Orthanc
+ {
+   // ZPOS64_T corresponds to "uint64_t"
+@@ -313,6 +320,62 @@ namespace Orthanc
+   }
+ 
+ 
++  static bool ReadInternal(std::string& content,
++                           unzFile& unzip,
++                           uint64_t size) ORTHANC_NOEXCEPT
++  {
++#if 0
++    /**
++     * This was the code using in Orthanc <= 1.12.10
++     **/
++    content.resize(static_cast<size_t>(size));
++    return (unzReadCurrentFile(unzip, &content[0], static_cast<uLong>(content.size())) != 0);
++
++#else
++    /**
++     * Read chunk by chunk to disarm ZIP bombs
++     **/
++    ChunkedBuffer buffer;
++
++    std::string chunk;
++    chunk.resize(10 * 1024 * 1024); // Read by chunks of 10MB
++
++    for (;;)
++    {
++      int r = unzReadCurrentFile(unzip, &chunk[0], chunk.size());
++
++      if (r == 0)
++      {
++        // We're done
++        if (buffer.GetNumBytes() == size)
++        {
++          buffer.Flatten(content);
++          return true;
++        }
++        else
++        {
++          return false;  // Presumably a malicious ZIP file
++        }
++      }
++      else if (r < 0)
++      {
++        // Error (might come from a malicious ZIP file)
++        return false;
++      }
++      else if (static_cast<uint64_t>(buffer.GetNumBytes()) + r > size)
++      {
++        // Presumably a ZIP bomb
++        return false;
++      }
++      else
++      {
++        buffer.AddChunk(&chunk[0], r);
++      }
++    }
++#endif
++  }
++
++
+   bool ZipReader::ReadNextFile(std::string& filename,
+                                std::string& content)
+   {
+@@ -330,6 +393,12 @@ namespace Orthanc
+         throw OrthancException(ErrorCode_BadFileFormat);
+       }
+ 
++      if (static_cast<uint64_t>(static_cast<size_t>(info.uncompressed_size)) != info.uncompressed_size)
++      {
++        // Too large file for a 32bit architecture
++        throw OrthancException(ErrorCode_NotEnoughMemory);
++      }
++
+       filename.resize(info.size_filename);
+       if (!filename.empty() &&
+           unzGetCurrentFileInfo64(pimpl_->unzip_, &info, &filename[0],
+@@ -338,14 +407,28 @@ namespace Orthanc
+         throw OrthancException(ErrorCode_BadFileFormat);
+       }
+ 
+-      content.resize(info.uncompressed_size);
++      {
++        // Prevent ZIP bombs
++        ReaderWriterLock::ReadLock lock(maximumUncompressedFileSizeMutex_);
++
++        if (hasMaximumUncompressedFileSize_ &&
++            info.uncompressed_size > maximumUncompressedFileSize_)
++        {
++          char s[32];
++          sprintf(s, "%0.1f", static_cast<float>(maximumUncompressedFileSize_) / (1024.0f * 1024.0f));
++          throw OrthancException(ErrorCode_BadFileFormat, "Uncompressed size exceeds limit (" + std::string(s) + "MB)");
++        }
++      }
+ 
+-      if (!content.empty())
++      if (info.uncompressed_size == 0)
++      {
++        content.clear();
++      }
++      else
+       {
+         if (unzOpenCurrentFile(pimpl_->unzip_) == 0)
+         {
+-          bool success = (unzReadCurrentFile(pimpl_->unzip_, &content[0],
+-                                             static_cast<uLong>(content.size())) != 0);
++          bool success = ReadInternal(content, pimpl_->unzip_, info.uncompressed_size);
+                           
+           if (unzCloseCurrentFile(pimpl_->unzip_) != 0 ||
+               !success)
+@@ -432,4 +515,19 @@ namespace Orthanc
+     }
+   }
+ #endif
++
++
++  void ZipReader::SetMaximumUncompressedFileSize(uint64_t size)
++  {
++    if (static_cast<uint64_t>(static_cast<size_t>(size)) != size)
++    {
++      throw OrthancException(ErrorCode_NotEnoughMemory);
++    }
++    else
++    {
++      ReaderWriterLock::WriteLock lock(maximumUncompressedFileSizeMutex_);
++      hasMaximumUncompressedFileSize_ = true;
++      maximumUncompressedFileSize_ = size;
++    }
++  }
+ }
+Index: Orthanc-1.12.10/OrthancFramework/Sources/Compression/ZipReader.h
+===================================================================
+--- Orthanc-1.12.10.orig/OrthancFramework/Sources/Compression/ZipReader.h
++++ Orthanc-1.12.10/OrthancFramework/Sources/Compression/ZipReader.h
+@@ -87,5 +87,7 @@ namespace Orthanc
+ #if ORTHANC_SANDBOXED != 1
+     static bool IsZipFile(const boost::filesystem::path& path);
+ #endif
++
++    static void SetMaximumUncompressedFileSize(uint64_t size);
+   };
+ }
+Index: Orthanc-1.12.10/OrthancFramework/Sources/DicomFormat/DicomImageInformation.cpp
+===================================================================
+--- Orthanc-1.12.10.orig/OrthancFramework/Sources/DicomFormat/DicomImageInformation.cpp
++++ Orthanc-1.12.10/OrthancFramework/Sources/DicomFormat/DicomImageInformation.cpp
+@@ -42,6 +42,8 @@
+ #include <stdio.h>
+ #include <memory>
+ 
++static const uint64_t MAX_FRAME_SIZE = 4ul * 1024ul * 1024ul * 1024ul;  // defensive approach: set a reasonable max size for a frame
++
+ namespace Orthanc
+ {
+   DicomImageInformation::DicomImageInformation(const DicomMap& values)
+@@ -130,6 +132,11 @@ namespace Orthanc
+       values.GetValue(DICOM_TAG_COLUMNS).ParseFirstUnsignedInteger(width_); // in some US images, we've seen tag values of "800\0"; that's why we parse the 'first' value
+       values.GetValue(DICOM_TAG_ROWS).ParseFirstUnsignedInteger(height_);
+ 
++      if (height_ > 65535 || width_ > 65535)
++      {
++        throw OrthancException(ErrorCode_BadFileFormat, "Image width or height exceed DICOM VR US range (65535)");
++      }
++
+       if (!values.ParseUnsignedInteger32(bitsAllocated_, DICOM_TAG_BITS_ALLOCATED))
+       {
+         throw OrthancException(ErrorCode_BadFileFormat);
+@@ -454,13 +461,15 @@ namespace Orthanc
+ 
+   size_t DicomImageInformation::GetFrameSize() const
+   {
++    uint64_t totalFrameSize;
++
+     if (bitsStored_ == 1)
+     {
+       assert(GetWidth() % 8 == 0);
+       
+       if (GetChannelCount() == 1)
+       {
+-        return GetHeight() * GetWidth() / 8;
++        totalFrameSize = static_cast<uint64_t>(GetHeight()) * static_cast<uint64_t>(GetWidth()) / 8;
+       }
+       else
+       {
+@@ -470,10 +479,20 @@ namespace Orthanc
+     }
+     else
+     {
+-      return (GetHeight() *
+-              GetWidth() *
+-              GetBytesPerValue() *
+-              GetChannelCount());
++      totalFrameSize = (static_cast<uint64_t>(GetHeight()) *
++                        static_cast<uint64_t>(GetWidth()) *
++                        static_cast<uint64_t>(GetBytesPerValue()) *
++                        static_cast<uint64_t>(GetChannelCount()));
++    }
++
++    if (totalFrameSize > MAX_FRAME_SIZE ||
++        static_cast<uint64_t>(static_cast<size_t>(totalFrameSize)) != totalFrameSize)
++    {
++      throw OrthancException(ErrorCode_BadFileFormat, "DICOM Frame size overflow");
++    }
++    else
++    {
++      return static_cast<size_t>(totalFrameSize);
+     }
+   }
+ 
+Index: Orthanc-1.12.10/OrthancFramework/Sources/DicomFormat/DicomIntegerPixelAccessor.cpp
+===================================================================
+--- Orthanc-1.12.10.orig/OrthancFramework/Sources/DicomFormat/DicomIntegerPixelAccessor.cpp
++++ Orthanc-1.12.10/OrthancFramework/Sources/DicomFormat/DicomIntegerPixelAccessor.cpp
+@@ -49,7 +49,7 @@ namespace Orthanc
+         information_.GetBitsStored() >= 32)
+     {
+       // Not available, as the accessor internally uses int32_t values
+-      throw OrthancException(ErrorCode_NotImplemented);
++      throw OrthancException(ErrorCode_NotImplemented, "No support for BitsAllocated > 32 or BitsStored >= 32");
+     }
+ 
+     frame_ = 0;
+@@ -57,7 +57,7 @@ namespace Orthanc
+ 
+     if (information_.GetNumberOfFrames() * frameOffset_ > size)
+     {
+-      throw OrthancException(ErrorCode_BadFileFormat);
++      throw OrthancException(ErrorCode_BadFileFormat, "Invalid size");
+     }
+ 
+     if (information_.IsSigned())
+Index: Orthanc-1.12.10/OrthancFramework/Sources/DicomFormat/DicomStreamReader.cpp
+===================================================================
+--- Orthanc-1.12.10.orig/OrthancFramework/Sources/DicomFormat/DicomStreamReader.cpp
++++ Orthanc-1.12.10/OrthancFramework/Sources/DicomFormat/DicomStreamReader.cpp
+@@ -204,6 +204,11 @@ namespace Orthanc
+       {
+         uint16_t length = ReadUnsignedInteger16(p + pos + 6, true);
+ 
++        if (pos + 8 + length > block.size())
++        {
++          throw OrthancException(ErrorCode_BadFileFormat, "DICOM meta-header tag length exceeds available data");
++        }
++
+         std::string value;
+         value.assign(p + pos + 8, length);
+         NormalizeValue(value, vr);
+@@ -237,6 +242,11 @@ namespace Orthanc
+           
+         uint32_t length = ReadUnsignedInteger32(p + pos + 8, true);
+ 
++        if (pos + 12 + static_cast<size_t>(length) > block.size())
++        {
++          throw OrthancException(ErrorCode_BadFileFormat, "DICOM meta-header tag length exceeds available data");
++        }
++
+         if (tag.GetGroup() == 0x0002)
+         {
+           std::string value;
+Index: Orthanc-1.12.10/OrthancFramework/Sources/DicomParsing/Internals/DicomImageDecoder.cpp
+===================================================================
+--- Orthanc-1.12.10.orig/OrthancFramework/Sources/DicomParsing/Internals/DicomImageDecoder.cpp
++++ Orthanc-1.12.10/OrthancFramework/Sources/DicomParsing/Internals/DicomImageDecoder.cpp
+@@ -192,6 +192,11 @@ namespace Orthanc
+     {
+       if (inbuffer[i] == 0xa5)
+       {
++        if (i + 2 >= length)
++        {
++          throw OrthancException(ErrorCode_BadFileFormat, "Truncated PMSCT_RLE1 escape sequence");
++        }
++
+         temp.push_back(inbuffer[i+2]);
+         for (uint8_t repeat = inbuffer[i + 1]; repeat != 0; repeat--)
+         {
+@@ -215,6 +220,11 @@ namespace Orthanc
+ 
+       if (temp[i] == 0x5a)
+       {
++        if (i + 2 >= temp.size())
++        {
++          throw OrthancException(ErrorCode_BadFileFormat, "Truncated PMSCT_RLE1 delta sequence");
++        }
++
+         uint16_t v1 = temp[i + 1];
+         uint16_t v2 = temp[i + 2];
+         value = (v2 << 8) + v1;
+@@ -460,9 +470,12 @@ namespace Orthanc
+           throw OrthancException(ErrorCode_NotImplemented, std::string("Palette Color Lookup Table Descriptor invalid palette size: '") + r.c_str() + "'");
+         }
+ 
+-        if (pixelLength != target->GetWidth() * target->GetHeight())
++        const uint64_t expectedSize = (static_cast<uint64_t>(target->GetWidth()) *
++                                       static_cast<uint64_t>(target->GetHeight()));
++
++        if (pixelLength != expectedSize)
+         {
+-          DcmElement *elem;
++          DcmElement *elem = NULL;
+           Uint16 bitsAllocated = 0;
+ 
+           if (!dataset.findAndGetUint16(DCM_BitsAllocated, bitsAllocated).good())
+@@ -470,7 +483,8 @@ namespace Orthanc
+             throw OrthancException(ErrorCode_NotImplemented);  
+           }
+ 
+-          if (!dataset.findAndGetElement(DCM_PixelData, elem).good())
++          if (!dataset.findAndGetElement(DCM_PixelData, elem).good() ||
++              elem == NULL)
+           {
+             throw OrthancException(ErrorCode_NotImplemented);  
+           }
+@@ -478,9 +492,11 @@ namespace Orthanc
+           // In implicit VR files, pixelLength is expressed in words (OW) although pixels can actually be 8 bits
+           // -> pixelLength is wrong by a factor of two and the image can still be decoded!
+           // seen in some Philips ClearVue 650 images (using 8 bits LUT)
+-          if (!(elem->getVR() == EVR_OW && bitsAllocated == 8 && (2*pixelLength == target->GetWidth() * target->GetHeight())))  
++          if (elem->getVR() != EVR_OW ||
++              bitsAllocated != 8 ||
++              2 * pixelLength != expectedSize)
+           {
+-            throw OrthancException(ErrorCode_NotImplemented);
++            throw OrthancException(ErrorCode_BadFileFormat, "Invalid size");
+           }
+         }
+ 
+@@ -494,6 +510,11 @@ namespace Orthanc
+ 
+           for (unsigned int x = 0; x < width; x++)
+           {
++            if (*source >= paletteSize)
++            {
++              throw OrthancException(ErrorCode_BadFileFormat, "Pixel value exceeds palette");
++            }
++
+             p[0] = lutRed[*source] >> offsetBits;
+             p[1] = lutGreen[*source] >> offsetBits;
+             p[2] = lutBlue[*source] >> offsetBits;
+Index: Orthanc-1.12.10/OrthancFramework/Sources/HttpServer/HttpOutput.cpp
+===================================================================
+--- Orthanc-1.12.10.orig/OrthancFramework/Sources/HttpServer/HttpOutput.cpp
++++ Orthanc-1.12.10/OrthancFramework/Sources/HttpServer/HttpOutput.cpp
+@@ -973,7 +973,7 @@ namespace Orthanc
+     std::string filename;
+     if (stream.HasContentFilename(filename))
+     {
+-      stateMachine_.AddHeader("Content-Disposition", "filename=\"" + std::string(filename) + "\"");
++      stateMachine_.SetContentFilename(filename.c_str());
+     }
+ 
+     stateMachine_.StartStream(contentType);
+Index: Orthanc-1.12.10/OrthancFramework/Sources/HttpServer/HttpServer.cpp
+===================================================================
+--- Orthanc-1.12.10.orig/OrthancFramework/Sources/HttpServer/HttpServer.cpp
++++ Orthanc-1.12.10/OrthancFramework/Sources/HttpServer/HttpServer.cpp
+@@ -185,7 +185,8 @@ namespace Orthanc
+       PostDataStatus_Success,
+       PostDataStatus_NoLength,
+       PostDataStatus_Pending,
+-      PostDataStatus_Failure
++      PostDataStatus_Failure,
++      PostDataStatus_RequestEntityTooLarge  // New in Orthanc 1.12.11
+     };
+   }
+ 
+@@ -491,12 +492,64 @@ namespace Orthanc
+     reader.AddChunk(body);        
+     reader.CloseStream();
+   }
+-  
++
++
++  static PostDataStatus ReadBodyUsingFile(std::string& body,
++                                          struct mg_connection *connection,
++                                          bool hasMaxBodySize,
++                                          size_t maxBodySize)
++  {
++    assert(!hasMaxBodySize || maxBodySize > 0);
++
++    // Store the individual chunks in a temporary file, then read it
++    // back into the memory buffer "body"
++    FileBuffer buffer;
++
++    uint64_t readSoFar = 0;
++    std::string tmp(1024 * 1024, 0);
++
++    for (;;)
++    {
++      int r = mg_read(connection, &tmp[0], tmp.size());
++      if (r < 0)
++      {
++        return PostDataStatus_Failure;
++      }
++      else if (r == 0)
++      {
++        break;
++      }
++      else
++      {
++        readSoFar += r;
++
++        if (readSoFar > std::numeric_limits<size_t>::max() ||
++            (hasMaxBodySize &&
++             readSoFar > maxBodySize))
++        {
++          return PostDataStatus_RequestEntityTooLarge;
++        }
++
++        buffer.Append(tmp.c_str(), r);
++      }
++    }
++
++    buffer.Read(body);
++
++    return PostDataStatus_Success;
++  }
++
+ 
+   static PostDataStatus ReadBodyWithContentLength(std::string& body,
+                                                   struct mg_connection *connection,
+-                                                  const std::string& contentLength)
++                                                  const std::string& contentLength,
++                                                  bool hasMaxBodySize,
++                                                  size_t maxBodySize)
+   {
++    static const size_t MAXIMUM_BODY_SIZE_IN_MEMORY = 100 * 1024 * 1024;  // 100MB
++
++    assert(!hasMaxBodySize || maxBodySize > 0);
++
+     size_t length;
+     try
+     {
+@@ -507,86 +560,107 @@ namespace Orthanc
+       }
+ 
+       length = static_cast<size_t>(tmp);
++      if (static_cast<int64_t>(length) != tmp)
++      {
++        return PostDataStatus_Failure;
++      }
+     }
+     catch (boost::bad_lexical_cast&)
+     {
+       return PostDataStatus_NoLength;
+     }
+ 
+-    body.resize(length);
++    if (hasMaxBodySize &&
++        length > maxBodySize)
++    {
++      return PostDataStatus_RequestEntityTooLarge;
++    }
+ 
+-    size_t pos = 0;
+-    while (length > 0)
++    if (length < MAXIMUM_BODY_SIZE_IN_MEMORY)
+     {
+-      int r = mg_read(connection, &body[pos], length);
+-      if (r <= 0)
++      /**
++       * Small POST bodies should land into RAM to avoid creating
++       * temporary files, which would result in bad performance.
++       **/
++      body.resize(length);
++
++      size_t pos = 0;
++      while (length > 0)
+       {
+-        return PostDataStatus_Failure;
++        int r = mg_read(connection, &body[pos], length);
++        if (r <= 0)
++        {
++          return PostDataStatus_Failure;
++        }
++
++        assert(static_cast<size_t>(r) <= length);
++        length -= r;
++        pos += r;
+       }
+ 
+-      assert(static_cast<size_t>(r) <= length);
+-      length -= r;
+-      pos += r;
++      return PostDataStatus_Success;
+     }
++    else
++    {
++      /**
++       * Deal with CWE-770 (Machine Spirits UG). If the client wants
++       * to send a large body, use a temporary file to prevent memory
++       * exhaustion by a malicious client that would set a large
++       * "Content-Length" without sending any actual data.
++       **/
+ 
+-    return PostDataStatus_Success;
+-  }
+-                                                  
+-
+-  static PostDataStatus ReadBodyWithoutContentLength(std::string& body,
+-                                                     struct mg_connection *connection)
+-  {
+-    // Store the individual chunks in a temporary file, then read it
+-    // back into the memory buffer "body"
+-    FileBuffer buffer;
++      PostDataStatus status = ReadBodyUsingFile(body, connection,
++                                                true /* do not read after "Content-Length" */, length);
+ 
+-    std::string tmp(1024 * 1024, 0);
+-      
+-    for (;;)
+-    {
+-      int r = mg_read(connection, &tmp[0], tmp.size());
+-      if (r < 0)
+-      {
+-        return PostDataStatus_Failure;
+-      }
+-      else if (r == 0)
++      if (status == PostDataStatus_Success)
+       {
+-        break;
++        return (body.size() == length ?
++                PostDataStatus_Success :
++                PostDataStatus_Failure);
+       }
+       else
+       {
+-        buffer.Append(tmp.c_str(), r);
++        return status;
+       }
+     }
++  }
+ 
+-    buffer.Read(body);
+ 
+-    return PostDataStatus_Success;
++  static PostDataStatus ReadBodyWithoutContentLength(std::string& body,
++                                                     struct mg_connection *connection,
++                                                     bool hasMaxBodySize,
++                                                     size_t maxBodySize)
++  {
++    return ReadBodyUsingFile(body, connection, hasMaxBodySize, maxBodySize);
+   }
+                                                   
+ 
+   static PostDataStatus ReadBodyToString(std::string& body,
+                                          struct mg_connection *connection,
+-                                         const HttpToolbox::Arguments& headers)
++                                         const HttpToolbox::Arguments& headers,
++                                         bool hasMaxBodySize,
++                                         size_t maxBodySize)
+   {
+     HttpToolbox::Arguments::const_iterator contentLength = headers.find("content-length");
+ 
+     if (contentLength != headers.end())
+     {
+       // "Content-Length" is available
+-      return ReadBodyWithContentLength(body, connection, contentLength->second);
++      return ReadBodyWithContentLength(body, connection, contentLength->second, hasMaxBodySize, maxBodySize);
+     }
+     else
+     {
+       // No Content-Length
+-      return ReadBodyWithoutContentLength(body, connection);
++      return ReadBodyWithoutContentLength(body, connection, hasMaxBodySize, maxBodySize);
+     }
+   }
+ 
+ 
+   static PostDataStatus ReadBodyToStream(IHttpHandler::IChunkedRequestReader& stream,
+                                          struct mg_connection *connection,
+-                                         const HttpToolbox::Arguments& headers)
++                                         const HttpToolbox::Arguments& headers,
++                                         bool hasMaxBodySize,
++                                         size_t maxBodySize)
+   {
+     HttpToolbox::Arguments::const_iterator contentLength = headers.find("content-length");
+ 
+@@ -594,7 +668,7 @@ namespace Orthanc
+     {
+       // "Content-Length" is available
+       std::string body;
+-      PostDataStatus status = ReadBodyWithContentLength(body, connection, contentLength->second);
++      PostDataStatus status = ReadBodyWithContentLength(body, connection, contentLength->second, hasMaxBodySize, maxBodySize);
+ 
+       if (status == PostDataStatus_Success &&
+           !body.empty())
+@@ -830,7 +904,9 @@ namespace Orthanc
+                            const std::string& method,
+                            const HttpToolbox::Arguments& headers,
+                            const std::string& uri,
+-                           struct mg_connection *connection /* to read the PUT body if need be */)
++                           struct mg_connection *connection /* to read the PUT body if need be */,
++                           bool hasMaxBodySize,
++                           size_t maxBodySize)
+   {
+     if (buckets.empty())
+     {
+@@ -1040,7 +1116,7 @@ namespace Orthanc
+           {
+ #if CIVETWEB_HAS_WEBDAV_WRITING == 1           
+             std::string body;
+-            if (ReadBodyToString(body, connection, headers) == PostDataStatus_Success)
++            if (ReadBodyToString(body, connection, headers, hasMaxBodySize, maxBodySize) == PostDataStatus_Success)
+             {
+               if (bucket->second->StoreFile(body, path))
+               {
+@@ -1407,7 +1483,7 @@ namespace Orthanc
+ 
+ #if ORTHANC_ENABLE_PUGIXML == 1
+     if (HandleWebDav(output, server.GetWebDavBuckets(), request->request_method,
+-                     headers, requestUri, connection))
++                     headers, requestUri, connection, server.HasMaxBodySize(), server.GetMaxBodySize()))
+     {
+       return;
+     }
+@@ -1447,7 +1523,7 @@ namespace Orthanc
+          **/
+         isMultipartForm = true;
+ 
+-        postStatus = ReadBodyToString(body, connection, headers);
++        postStatus = ReadBodyToString(body, connection, headers, server.HasMaxBodySize(), server.GetMaxBodySize());
+         if (postStatus == PostDataStatus_Success)
+         {
+           server.ProcessMultipartFormData(remoteIp, username, uri, headers, body, boundary, authenticationPayload);
+@@ -1473,7 +1549,7 @@ namespace Orthanc
+             throw OrthancException(ErrorCode_InternalError);
+           }
+ 
+-          postStatus = ReadBodyToStream(*stream, connection, headers);
++          postStatus = ReadBodyToStream(*stream, connection, headers, server.HasMaxBodySize(), server.GetMaxBodySize());
+ 
+           if (postStatus == PostDataStatus_Success)
+           {
+@@ -1482,7 +1558,7 @@ namespace Orthanc
+         }
+         else
+         {
+-          postStatus = ReadBodyToString(body, connection, headers);
++          postStatus = ReadBodyToString(body, connection, headers, server.HasMaxBodySize(), server.GetMaxBodySize());
+         }
+       }
+ 
+@@ -1492,6 +1568,10 @@ namespace Orthanc
+           output.SendStatus(HttpStatus_411_LengthRequired);
+           return;
+ 
++        case PostDataStatus_RequestEntityTooLarge:  // New in Orthanc 1.12.11
++          output.SendStatus(HttpStatus_413_RequestEntityTooLarge);
++          return;
++
+         case PostDataStatus_Failure:
+           output.SendStatus(HttpStatus_400_BadRequest);
+           return;
+@@ -1685,7 +1765,9 @@ namespace Orthanc
+     threadsCount_(50),  // Default value in mongoose/civetweb
+     tcpNoDelay_(true),
+     requestTimeout_(30),  // Default value in mongoose/civetweb (30 seconds)
+-    threadCounter_(0)
++    threadCounter_(0),
++    hasMaxBodySize_(false),
++    maxBodySize_(0)
+   {
+ #if ORTHANC_ENABLE_MONGOOSE == 1
+     CLOG(INFO, HTTP) << "This Orthanc server uses Mongoose as its embedded HTTP server";
+@@ -2389,4 +2471,21 @@ namespace Orthanc
+       Logging::SetCurrentThreadName(std::string("HTTP-") + boost::lexical_cast<std::string>(threadCounter_++));
+     }
+   }
++
++
++  void HttpServer::SetMaxBodySize(uint64_t size)
++  {
++    Stop();
++
++    if (size == 0 ||
++        static_cast<uint64_t>(static_cast<size_t>(size)) != size)
++    {
++      throw OrthancException(ErrorCode_ParameterOutOfRange);
++    }
++    else
++    {
++      hasMaxBodySize_ = true;
++      maxBodySize_ = static_cast<size_t>(size);
++    }
++  }
+ }
+Index: Orthanc-1.12.10/OrthancFramework/Sources/HttpServer/HttpServer.h
+===================================================================
+--- Orthanc-1.12.10.orig/OrthancFramework/Sources/HttpServer/HttpServer.h
++++ Orthanc-1.12.10/OrthancFramework/Sources/HttpServer/HttpServer.h
+@@ -123,6 +123,10 @@ namespace Orthanc
+     boost::mutex threadCounterMutex_;  // New in Orthanc 1.12.9
+     uint16_t threadCounter_;           // Introduced as a global, static variable in Orthanc 1.12.2
+ 
++    // New in Orthanc 1.12.11
++    bool    hasMaxBodySize_;
++    size_t  maxBodySize_;
++
+ #if ORTHANC_ENABLE_PUGIXML == 1
+     WebDavBuckets webDavBuckets_;
+ #endif
+@@ -246,5 +250,17 @@ namespace Orthanc
+     MetricsRegistry::AvailableResourcesDecounter* CreateAvailableHttpThreadsDecounter();
+ 
+     void UpdateCurrentThreadName();
++
++    void SetMaxBodySize(uint64_t size);
++
++    bool HasMaxBodySize() const
++    {
++      return hasMaxBodySize_;
++    }
++
++    size_t GetMaxBodySize() const
++    {
++      return maxBodySize_;
++    }
+   };
+ }
+Index: Orthanc-1.12.10/OrthancFramework/Sources/Images/ImageAccessor.cpp
+===================================================================
+--- Orthanc-1.12.10.orig/OrthancFramework/Sources/Images/ImageAccessor.cpp
++++ Orthanc-1.12.10/OrthancFramework/Sources/Images/ImageAccessor.cpp
+@@ -165,7 +165,14 @@ namespace Orthanc
+   {
+     if (buffer_ != NULL)
+     {
+-      return buffer_ + static_cast<size_t>(y) * static_cast<size_t>(pitch_);
++      if (y < height_)
++      {
++        return buffer_ + static_cast<size_t>(y) * static_cast<size_t>(pitch_);
++      }
++      else
++      {
++        throw OrthancException(ErrorCode_ParameterOutOfRange);
++      }
+     }
+     else
+     {
+@@ -184,7 +191,14 @@ namespace Orthanc
+ 
+     if (buffer_ != NULL)
+     {
+-      return buffer_ + static_cast<size_t>(y) * static_cast<size_t>(pitch_);
++      if (y < height_)
++      {
++        return buffer_ + static_cast<size_t>(y) * static_cast<size_t>(pitch_);
++      }
++      else
++      {
++        throw OrthancException(ErrorCode_ParameterOutOfRange);
++      }
+     }
+     else
+     {
+@@ -210,17 +224,20 @@ namespace Orthanc
+                                      unsigned int pitch,
+                                      const void *buffer)
+   {
++    const uint64_t size = static_cast<uint64_t>(height) * static_cast<uint64_t>(pitch);
++
++    if (static_cast<uint64_t>(GetBytesPerPixel() * width) > static_cast<uint64_t>(pitch) ||
++        static_cast<uint64_t>(static_cast<size_t>(size)) != size)
++    {
++      throw OrthancException(ErrorCode_ParameterOutOfRange);
++    }
++
+     readOnly_ = true;
+     format_ = format;
+     width_ = width;
+     height_ = height;
+     pitch_ = pitch;
+     buffer_ = reinterpret_cast<uint8_t*>(const_cast<void*>(buffer));
+-
+-    if (GetBytesPerPixel() * width_ > pitch_)
+-    {
+-      throw OrthancException(ErrorCode_ParameterOutOfRange);
+-    }
+   }
+ 
+   void ImageAccessor::GetReadOnlyAccessor(ImageAccessor &target) const
+@@ -235,17 +252,20 @@ namespace Orthanc
+                                      unsigned int pitch,
+                                      void *buffer)
+   {
++    const uint64_t size = static_cast<uint64_t>(height) * static_cast<uint64_t>(pitch);
++
++    if (static_cast<uint64_t>(GetBytesPerPixel() * width) > static_cast<uint64_t>(pitch) ||
++        static_cast<uint64_t>(static_cast<size_t>(size)) != size)
++    {
++      throw OrthancException(ErrorCode_ParameterOutOfRange);
++    }
++
+     readOnly_ = false;
+     format_ = format;
+     width_ = width;
+     height_ = height;
+     pitch_ = pitch;
+     buffer_ = reinterpret_cast<uint8_t*>(buffer);
+-
+-    if (GetBytesPerPixel() * width_ > pitch_)
+-    {
+-      throw OrthancException(ErrorCode_ParameterOutOfRange);
+-    }
+   }
+ 
+ 
+Index: Orthanc-1.12.10/OrthancFramework/Sources/Images/ImageBuffer.cpp
+===================================================================
+--- Orthanc-1.12.10.orig/OrthancFramework/Sources/Images/ImageBuffer.cpp
++++ Orthanc-1.12.10/OrthancFramework/Sources/Images/ImageBuffer.cpp
+@@ -46,8 +46,16 @@ namespace Orthanc
+         }
+       */
+ 
+-      pitch_ = GetBytesPerPixel() * width_;
+-      size_t size = static_cast<size_t>(pitch_) * static_cast<size_t>(height_);
++      const uint64_t tmpPitch = static_cast<uint64_t>(GetBytesPerPixel()) * static_cast<uint64_t>(width_);
++      const uint64_t size = tmpPitch * static_cast<uint64_t>(height_);
++
++      if (static_cast<uint64_t>(static_cast<unsigned int>(tmpPitch)) != tmpPitch ||
++          static_cast<uint64_t>(static_cast<size_t>(size)) != size)
++      {
++        throw OrthancException(ErrorCode_NotEnoughMemory);
++      }
++
++      pitch_ = static_cast<unsigned int>(tmpPitch);
+ 
+       if (size == 0)
+       {
+@@ -55,7 +63,7 @@ namespace Orthanc
+       }
+       else
+       {
+-        buffer_ = malloc(size);
++        buffer_ = malloc(static_cast<size_t>(size));
+         if (buffer_ == NULL)
+         {
+           throw OrthancException(ErrorCode_NotEnoughMemory,
+Index: Orthanc-1.12.10/OrthancFramework/Sources/Images/PamReader.cpp
+===================================================================
+--- Orthanc-1.12.10.orig/OrthancFramework/Sources/Images/PamReader.cpp
++++ Orthanc-1.12.10/OrthancFramework/Sources/Images/PamReader.cpp
+@@ -38,6 +38,7 @@
+ #include <boost/algorithm/string/find.hpp>
+ #include <boost/lexical_cast.hpp>
+ 
++static const uint64_t MAX_PAM_IMAGE_BUFFER_SIZE = 4ul * 1024ul * 1024ul * 1024ul;  // defensive approach: set a reasonable max size for a PAM image
+ 
+ namespace Orthanc
+ {
+@@ -181,11 +182,29 @@ namespace Orthanc
+     const unsigned int maxValue = LookupIntegerParameter(parameters, "MAXVAL");
+     const std::string tupleType = LookupStringParameter(parameters, "TUPLTYPE");
+ 
++    if (width > 65535 || height > 65535 || channelCount > 4 || maxValue > 65535)
++    {
++      throw OrthancException(ErrorCode_BadFileFormat, "PAM header values exceed reasonable limits");
++    }
++
+     unsigned int bytesPerChannel;
+     PixelFormat format;
+     GetPixelFormat(format, bytesPerChannel, maxValue, channelCount, tupleType);
+ 
+-    unsigned int pitch = width * channelCount * bytesPerChannel;
++    // unsigned int pitch = width * channelCount * bytesPerChannel;
++    uint64_t pitch = static_cast<uint64_t>(width) * channelCount * bytesPerChannel;
++
++    if (pitch > std::numeric_limits<unsigned int>::max())
++    {
++      throw OrthancException(ErrorCode_BadFileFormat, "PAM dimensions exceed limits");
++    }
++
++    uint64_t totalSize = pitch * height;
++    if (totalSize > MAX_PAM_IMAGE_BUFFER_SIZE ||
++        static_cast<uint64_t>(static_cast<size_t>(totalSize)) != totalSize)
++    {
++      throw OrthancException(ErrorCode_BadFileFormat, "PAM image too large");
++    }
+ 
+     if (content_.size() != header.size() + headerDelimiter.size() + pitch * height)
+     {
+@@ -196,7 +215,7 @@ namespace Orthanc
+ 
+     {
+       intptr_t bufferAddr = reinterpret_cast<intptr_t>(&content_[offset]);
+-      if((bufferAddr % 8) == 0)
++      if ((bufferAddr % 8) == 0)
+         LOG(TRACE) << "PamReader::ParseContent() image address = " << bufferAddr;
+       else
+         LOG(TRACE) << "PamReader::ParseContent() image address = " << bufferAddr << " (not a multiple of 8!)";
+Index: Orthanc-1.12.10/OrthancFramework/Sources/MultiThreading/ReaderWriterLock.h
+===================================================================
+--- /dev/null
++++ Orthanc-1.12.10/OrthancFramework/Sources/MultiThreading/ReaderWriterLock.h
+@@ -0,0 +1,76 @@
++/**
++ * Orthanc - A Lightweight, RESTful DICOM Store
++ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
++ * Department, University Hospital of Liege, Belgium
++ * Copyright (C) 2017-2023 Osimis S.A., Belgium
++ * Copyright (C) 2024-2025 Orthanc Team SRL, Belgium
++ * Copyright (C) 2021-2025 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
++ *
++ * This program is free software: you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public License
++ * as published by the Free Software Foundation, either version 3 of
++ * the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this program. If not, see
++ * <http://www.gnu.org/licenses/>.
++ **/
++
++
++#pragma once
++
++#include "../OrthancFramework.h"
++
++#if !defined(__EMSCRIPTEN__)
++// Multithreading is not supported in WebAssembly
++#  include <boost/thread/shared_mutex.hpp>
++#  include <boost/thread/lock_types.hpp>  // For boost::unique_lock<> and boost::shared_lock<>
++#endif
++
++
++namespace Orthanc
++{
++  class ORTHANC_PUBLIC ReaderWriterLock : public boost::noncopyable
++  {
++  private:
++    boost::shared_mutex mutex_;
++
++  public:
++    class ReadLock : public boost::noncopyable
++    {
++    private:
++#if !defined(__EMSCRIPTEN__)
++      boost::shared_lock<boost::shared_mutex> lock_;
++#endif
++
++    public:
++      explicit ReadLock(ReaderWriterLock& that)
++#if !defined(__EMSCRIPTEN__)
++        : lock_(that.mutex_)
++#endif
++      {
++      }
++    };
++
++    class WriteLock : public boost::noncopyable
++    {
++    private:
++#if !defined(__EMSCRIPTEN__)
++      boost::unique_lock<boost::shared_mutex> lock_;
++#endif
++
++    public:
++      explicit WriteLock(ReaderWriterLock& that)
++#if !defined(__EMSCRIPTEN__)
++        : lock_(that.mutex_)
++#endif
++      {
++      }
++    };
++  };
++}
+Index: Orthanc-1.12.10/OrthancServer/Sources/ServerContext.cpp
+===================================================================
+--- Orthanc-1.12.10.orig/OrthancServer/Sources/ServerContext.cpp
++++ Orthanc-1.12.10/OrthancServer/Sources/ServerContext.cpp
+@@ -1926,7 +1926,7 @@ namespace Orthanc
+       }
+       catch (OrthancException& e)
+       {
+-        LOG(INFO) << e.GetDetails();
++        LOG(INFO) << "Failed to decode a DICOM frame: " << e.GetDetails();
+       }
+     }
+ 
+Index: Orthanc-1.12.10/OrthancServer/Sources/main.cpp
+===================================================================
+--- Orthanc-1.12.10.orig/OrthancServer/Sources/main.cpp
++++ Orthanc-1.12.10/OrthancServer/Sources/main.cpp
+@@ -25,6 +25,8 @@
+ #include "OrthancRestApi/OrthancRestApi.h"
+ 
+ #include "../../OrthancFramework/Sources/Compatibility.h"
++#include "../../OrthancFramework/Sources/Compression/GzipCompressor.h"
++#include "../../OrthancFramework/Sources/Compression/ZipReader.h"
+ #include "../../OrthancFramework/Sources/DicomFormat/DicomArray.h"
+ #include "../../OrthancFramework/Sources/DicomNetworking/DicomAssociationParameters.h"
+ #include "../../OrthancFramework/Sources/DicomNetworking/DicomServer.h"
+@@ -46,8 +48,8 @@
+ #include "OrthancWebDav.h"
+ #include "ServerContext.h"
+ #include "ServerEnumerations.h"
+-#include "ServerJobs/StorageCommitmentScpJob.h"
+ #include "ServerJobs/DicomRetrieveScuBaseJob.h"
++#include "ServerJobs/StorageCommitmentScpJob.h"
+ #include "ServerToolbox.h"
+ #include "StorageCommitmentReports.h"
+ 
+@@ -1094,6 +1096,33 @@ static bool StartHttpServer(ServerContex
+       httpServer.SetTcpNoDelay(lock.GetConfiguration().GetBooleanParameter("TcpNoDelay", true));
+       httpServer.SetRequestTimeout(lock.GetConfiguration().GetUnsignedIntegerParameter("HttpRequestTimeout", 30));
+ 
++      // New in Orthanc 1.12.11
++      const unsigned int maxBodySize = lock.GetConfiguration().GetUnsignedIntegerParameter("MaximumRequestBodySizeMB", 8192);
++      if (maxBodySize != 0)
++      {
++        LOG(WARNING) << "Limiting the maximum body size in HTTP requests to " << maxBodySize << "MB";
++        httpServer.SetMaxBodySize(static_cast<uint64_t>(maxBodySize) *
++                                  static_cast<uint64_t>(1024 * 1024));
++      }
++      else
++      {
++        LOG(WARNING) << "No limit on the maximum body size in HTTP requests";
++      }
++
++      const unsigned int maxSizeInArchive = lock.GetConfiguration().GetUnsignedIntegerParameter("MaximumFileSizeInArchiveMB", 4096);
++      if (maxSizeInArchive != 0)
++      {
++        LOG(WARNING) << "Limiting on the maximum file size uncompressed from ZIP/gzip archives to " << maxSizeInArchive << "MB";
++        ZipReader::SetMaximumUncompressedFileSize(static_cast<uint64_t>(maxSizeInArchive) *
++                                                  static_cast<uint64_t>(1024 * 1024));
++        GzipCompressor::SetMaximumUncompressedFileSize(static_cast<uint64_t>(maxSizeInArchive) *
++                                                       static_cast<uint64_t>(1024 * 1024));
++      }
++      else
++      {
++        LOG(WARNING) << "No limit on the maximum file size uncompressed from ZIP/gzip archives";
++      }
++
+       // Let's assume that the HTTP server is secure
+       context.SetHttpServerSecure(true);
+ 


=====================================
debian/patches/series
=====================================
@@ -1 +1,2 @@
 libminizip
+security



View it on GitLab: https://salsa.debian.org/med-team/orthanc/-/compare/e17730de719656f33176d347e450929b6873e4ab...d1c1ce15c90c408c5c03d27bc0b79d9a995ad610

-- 
View it on GitLab: https://salsa.debian.org/med-team/orthanc/-/compare/e17730de719656f33176d347e450929b6873e4ab...d1c1ce15c90c408c5c03d27bc0b79d9a995ad610
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20260411/239e0a1f/attachment-0001.htm>


More information about the debian-med-commit mailing list