CVE-2019-15692: Out-of-bounds Write (CWE-787)
value: TigerVNC version prior to 1.10.1 is vulnerable to heap buffer overflow. Vulnerability could be triggered from CopyRectDecoder due to incorrect value checks. Exploitation of this vulnerability could potentially result into remote code execution. This attack appear to be exploitable via network connectivity.
CWE description: The product writes data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 7.2
Published: 2019-12-26T15:15Z
Repository: https://github.com/CendioOssman/tigervnc
Language: Other
Files changed: common/rfb/PixelBuffer.cxx
Commit date: 2019-11-15 10:53:41 +0100
Commit message: Restrict PixelBuffer dimensions to safe values

We do a lot of calculations based on pixel coordinates and we need
to make sure they do not overflow. Restrict the maximum dimensions
we support rather than try to switch over all calculations to use
64 bit integers.

This prevents attackers from from injecting code by specifying a
huge framebuffer size and relying on the values overflowing to
access invalid areas of the heap.

This primarily affects the client which gets both the screen
dimensions and the pixel contents from the remote side. But the
server might also be affected as a client can adjust the screen
dimensions, as can applications inside the session.

Issue found by Pavel Cheremushkin from Kaspersky Lab.

Diff: diff --git a/common/rfb/PixelBuffer.cxx b/common/rfb/PixelBuffer.cxx
index 0aa67744..fe406b96 100644
--- a/common/rfb/PixelBuffer.cxx
+++ b/common/rfb/PixelBuffer.cxx
@@ -29,10 +29,18 @@
 using namespace rfb;
 using namespace rdr;
 
 static LogWriter vlog("PixelBuffer");
 
+// We do a lot of byte offset calculations that assume the result fits
+// inside a signed 32 bit integer. Limit the maximum size of pixel
+// buffers so that these calculations never overflow.
+
+const int maxPixelBufferWidth = 16384;
+const int maxPixelBufferHeight = 16384;
+const int maxPixelBufferStride = 16384;
+
 
 // -=- Generic pixel buffer class
 
 PixelBuffer::PixelBuffer(const PixelFormat& pf, int w, int h)
   : format(pf), width_(0), height_(0)
@@ -106,10 +114,15 @@ void PixelBuffer::getImage(const PixelFormat& pf, void* imageBuf,
                       stride, srcStride);
 }
 
 void PixelBuffer::setSize(int width, int height)
 {
+  if ((width < 0) || (width > maxPixelBufferWidth))
+    throw rfb::Exception("Invalid PixelBuffer width of %d pixels requested", width);
+  if ((height < 0) || (height > maxPixelBufferHeight))
+    throw rfb::Exception("Invalid PixelBuffer height of %d pixels requested", height);
+
   width_ = width;
   height_ = height;
 }
 
 // -=- Modifiable generic pixel buffer class
@@ -338,10 +351,19 @@ const rdr::U8* FullFramePixelBuffer::getBuffer(const Rect& r, int* stride_) cons
 }
 
 void FullFramePixelBuffer::setBuffer(int width, int height,
                                      rdr::U8* data_, int stride_)
 {
+  if ((width < 0) || (width > maxPixelBufferWidth))
+    throw rfb::Exception("Invalid PixelBuffer width of %d pixels requested", width);
+  if ((height < 0) || (height > maxPixelBufferHeight))
+    throw rfb::Exception("Invalid PixelBuffer height of %d pixels requested", height);
+  if ((stride_ < 0) || (stride_ > maxPixelBufferStride) || (stride_ < width))
+    throw rfb::Exception("Invalid PixelBuffer stride of %d pixels requested", stride_);
+  if ((width != 0) && (height != 0) && (data_ == NULL))
+    throw rfb::Exception("PixelBuffer requested without a valid memory area");
+
   ModifiablePixelBuffer::setSize(width, height);
   stride = stride_;
   data = data_;
 }
 

Fixed code: // We do a lot of byte offset calculations that assume the result fits
// inside a signed 32 bit integer. Limit the maximum size of pixel
// buffers so that these calculations never overflow.

const int maxPixelBufferWidth = 16384;
const int maxPixelBufferHeight = 16384;
const int maxPixelBufferStride = 16384;

  if ((width < 0) || (width > maxPixelBufferWidth))
    throw rfb::Exception("Invalid PixelBuffer width of %d pixels requested", width);
  if ((height < 0) || (height > maxPixelBufferHeight))
    throw rfb::Exception("Invalid PixelBuffer height of %d pixels requested", height);

  if ((width < 0) || (width > maxPixelBufferWidth))
    throw rfb::Exception("Invalid PixelBuffer width of %d pixels requested", width);
  if ((height < 0) || (height > maxPixelBufferHeight))
    throw rfb::Exception("Invalid PixelBuffer height of %d pixels requested", height);
  if ((stride_ < 0) || (stride_ > maxPixelBufferStride) || (stride_ < width))
    throw rfb::Exception("Invalid PixelBuffer stride of %d pixels requested", stride_);
  if ((width != 0) && (height != 0) && (data_ == NULL))
    throw rfb::Exception("PixelBuffer requested without a valid memory area");