[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