CVE-2023-50245 (published 2023-12-11T23:15Z)

OpenEXR-viewer is a viewer for OpenEXR files with detailed metadata probing. Versions prior to 0.6.1 have a memory overflow vulnerability. This issue is fixed in version 0.6.1.

CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-120
cwe_name: Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')
description: The product copies an input buffer to an output buffer without verifying that the size of the input buffer is less than the size of the output buffer, leading to a buffer overflow.
extended_description: A buffer overflow condition exists when a product attempts to put more data in a buffer than it can hold, or when it attempts to put data in a memory area outside of the boundaries of a buffer. The simplest type of error, and the most common cause of buffer overflows, is the "classic" case in which the product copies the buffer without restricting how much is copied. Other variants exist, but the existence of a classic overflow strongly suggests that the programmer is not considering even the most basic of security protections.
         

Fix information:
repo_url: https://github.com/afichet/openexr-viewer
commit_details: repo_url: https://github.com/afichet/openexr-viewer
author: Alban Fichet
author_date: 2023-12-09 14:13:36+01:00
msg: Early catch potential overflow issue #43

`m_width` and `m_height` are of `int` type in the OpenEXR library. We
currently keep the same types in our class but this may case issue
when mapping 1D memory. In the most favorable case, they are
multiplied together (Y framebuffer). For RGB(A) case, the required
memory can also be 4 time larger. We check if resp. `m_width *
m_height` and `4 * m_width * m_heigh` stay within the `int` higher
limit. Thanks to @GAP-dev for bringing this issue.

This commit also cleans a bit raw memory allocation in favor of
`std::vector` container.
repository: repo_url: https://github.com/afichet/openexr-viewer
repo_name: afichet/openexr-viewer
description: Simple viewer for OpenEXR files with detailed metadata display
repo_language: C++
owner: afichet
file_changes: filename: FramebufferModel.cpp
change_type: ModificationType.MODIFY
diff: @@ -39,7 +39,6 @@
 
 FramebufferModel::FramebufferModel(QObject* parent)
   : QObject(parent)
-  , m_pixelBuffer(nullptr)
   , m_width(0)
   , m_height(0)
   , m_isImageLoaded(false)
@@ -59,7 +58,4 @@ QRect FramebufferModel::getDataWindow() const
     return m_dataWindow;
 }
 
-FramebufferModel::~FramebufferModel()
-{
-    delete[] m_pixelBuffer;
-}
+FramebufferModel::~FramebufferModel() {}

programming_language: C++
filename: FramebufferModel.h
change_type: ModificationType.MODIFY
diff: @@ -37,7 +37,7 @@
 #include <QObject>
 #include <QRect>
 #include <QVector>
-#include <array>
+#include <vector>
 
 class FramebufferModel: public QObject
 {
@@ -67,8 +67,8 @@ class FramebufferModel: public QObject
     void loadFailed(QString message);
 
   protected:
-    float* m_pixelBuffer;
-    QImage m_image;
+    std::vector<float> m_pixelBuffer;
+    QImage             m_image;
 
     // Right now, the width and height are defined as Vec2i in OpenEXR
     // i.e. int type.

programming_language: C++
filename: RGBFramebufferModel.cpp
change_type: ModificationType.MODIFY
diff: @@ -81,6 +81,23 @@ void RGBFramebufferModel::load(
             m_displayWindow
               = QRect(dispW.min.x, dispW.min.y, dispW_width, dispW_height);
 
+            // Check to avoid type overflow, width and height are 32bits int
+            // representing a 2 dimentional image. Can overflow the type when
+            // multiplied together.
+            // 0x1FFFFFFF is a save limit for 4 * 0x7FFFFFFF the max
+            // representable int since we need 4 channels.
+            // TODO: Use larger type when manipulating framebuffer
+            const uint64_t partial_size
+              = (uint64_t)m_width * (uint64_t)m_height;
+
+            if (partial_size > 0x1FFFFFFF) {
+                throw std::runtime_error(
+                  "The total image size is too large. May be supported in a "
+                  "future revision.");
+            }
+
+            m_pixelBuffer.resize(4 * m_width * m_height);
+
             // Check if there is specific chromaticities tied to the color
             // representation in this part.
             const Imf::ChromaticitiesAttribute* c
@@ -93,8 +110,6 @@ void RGBFramebufferModel::load(
                 chromaticities = c->value();
             }
 
-            m_pixelBuffer = new float[4 * m_width * m_height];
-
             // Check if there is alpha channel
             if (hasAlpha) {
                 std::string      aLayer = m_parentLayer + "A";
@@ -190,12 +205,12 @@ void RGBFramebufferModel::load(
 
                     Imf::FrameBuffer framebuffer;
 
-                    Imf::Rgba* buff1 = new Imf::Rgba[m_width * m_height];
-                    Imf::Rgba* buff2 = new Imf::Rgba[m_width * m_height];
+                    std::vector<Imf::Rgba> buff1(m_width * m_height);
+                    std::vector<Imf::Rgba> buff2(m_width * m_height);
 
-                    float* yBuffer  = new float[m_width * m_height];
-                    float* ryBuffer = new float[m_width / 2 * m_height / 2];
-                    float* byBuffer = new float[m_width / 2 * m_height / 2];
+                    std::vector<float> yBuffer(m_width * m_height);
+                    std::vector<float> ryBuffer(m_width / 2 * m_height / 2);
+                    std::vector<float> byBuffer(m_width / 2 * m_height / 2);
 
                     Imf::Slice ySlice = Imf::Slice::Make(
                       Imf::PixelType::FLOAT,
@@ -335,12 +350,6 @@ void RGBFramebufferModel::load(
                             m_pixelBuffer[4 * (y * m_width + x) + 2] = rgb.z;
                         }
                     }
-
-                    delete[] yBuffer;
-                    delete[] ryBuffer;
-                    delete[] byBuffer;
-                    delete[] buff1;
-                    delete[] buff2;
                 }
 
                 break;

programming_language: C++
filename: YFramebufferModel.cpp
change_type: ModificationType.MODIFY
diff: @@ -90,12 +90,25 @@ void YFramebufferModel::load(Imf::MultiPartInputFile& file, int partId)
                   dispW_width / 2,
                   dispW_height / 2);
 
-                m_pixelBuffer = new float[m_width * m_height];
+                // Check to avoid type overflow, width and height are 32bits int
+                // representing a 2 dimentional image. Can overflow the type when
+                // multiplied together
+                // TODO: Use larger type when manipulating framebuffer
+                const uint64_t partial_size
+                  = (uint64_t)m_width * (uint64_t)m_height;
+
+                if (partial_size > 0x7FFFFFFF) {
+                    throw std::runtime_error(
+                      "The total image size is too large. May be supported in "
+                      "a future revision.");
+                }
+
+                m_pixelBuffer.resize(m_width * m_height);
 
                 // Luminance Chroma channels
                 graySlice = Imf::Slice::Make(
                   Imf::PixelType::FLOAT,
-                  m_pixelBuffer,
+                  m_pixelBuffer.data(),
                   datW,
                   sizeof(float),
                   m_width * sizeof(float),
@@ -112,11 +125,11 @@ void YFramebufferModel::load(Imf::MultiPartInputFile& file, int partId)
                 m_displayWindow
                   = QRect(dispW.min.x, dispW.min.y, dispW_width, dispW_height);
 
-                m_pixelBuffer = new float[m_width * m_height];
+                m_pixelBuffer.resize(m_width * m_height);
 
                 graySlice = Imf::Slice::Make(
                   Imf::PixelType::FLOAT,
-                  m_pixelBuffer,
+                  m_pixelBuffer.data(),
                   datW);
             }
 

programming_language: C++