Blame SOURCES/tigervnc-be-defensive-about-overflows-in-stream-objects.patch

aeab07
From 75e6e0653a48baf474fd45d78b1da53e2f324642 Mon Sep 17 00:00:00 2001
aeab07
From: Pierre Ossman <ossman@cendio.se>
aeab07
Date: Tue, 24 Sep 2019 09:41:07 +0200
aeab07
Subject: [PATCH] Be defensive about overflows in stream objects
aeab07
aeab07
We use a lot of lengths given to us over the network, so be more
aeab07
paranoid about them causing an overflow as otherwise an attacker
aeab07
might trick us in to overwriting other memory.
aeab07
aeab07
This primarily affects the client which often gets lengths from the
aeab07
server, but there are also some scenarios where the server might
aeab07
theoretically be vulnerable.
aeab07
aeab07
Issue found by Pavel Cheremushkin from Kaspersky Lab.
aeab07
---
aeab07
 common/rdr/FdInStream.cxx    |  8 +++++---
aeab07
 common/rdr/FdOutStream.cxx   |  7 ++++---
aeab07
 common/rdr/FileInStream.cxx  |  8 +++++---
aeab07
 common/rdr/HexInStream.cxx   |  8 +++++---
aeab07
 common/rdr/HexOutStream.cxx  |  6 ++++--
aeab07
 common/rdr/InStream.h        | 24 +++++++++++++-----------
aeab07
 common/rdr/MemOutStream.h    |  4 ++++
aeab07
 common/rdr/OutStream.h       | 24 +++++++++++++-----------
aeab07
 common/rdr/RandomStream.cxx  |  6 ++++--
aeab07
 common/rdr/TLSInStream.cxx   | 10 ++++++----
aeab07
 common/rdr/TLSOutStream.cxx  |  6 ++++--
aeab07
 common/rdr/ZlibInStream.cxx  |  6 ++++--
aeab07
 common/rdr/ZlibOutStream.cxx |  6 ++++--
aeab07
 13 files changed, 75 insertions(+), 48 deletions(-)
aeab07
aeab07
diff --git a/common/rdr/FdInStream.cxx b/common/rdr/FdInStream.cxx
aeab07
index 789cbec..406ece5 100644
aeab07
--- a/common/rdr/FdInStream.cxx
aeab07
+++ b/common/rdr/FdInStream.cxx
aeab07
@@ -136,7 +136,7 @@ size_t FdInStream::overrun(size_t itemSize, size_t nItems, bool wait)
aeab07
   ptr = start;
aeab07
 
aeab07
   size_t bytes_to_read;
aeab07
-  while (end < start + itemSize) {
aeab07
+  while ((size_t)(end - start) < itemSize) {
aeab07
     bytes_to_read = start + bufSize - end;
aeab07
     if (!timing) {
aeab07
       // When not timing, we must be careful not to read too much
aeab07
@@ -152,8 +152,10 @@ size_t FdInStream::overrun(size_t itemSize, size_t nItems, bool wait)
aeab07
     end += n;
aeab07
   }
aeab07
 
aeab07
-  if (itemSize * nItems > (size_t)(end - ptr))
aeab07
-    nItems = (end - ptr) / itemSize;
aeab07
+  size_t nAvail;
aeab07
+  nAvail = (end - ptr) / itemSize;
aeab07
+  if (nAvail < nItems)
aeab07
+    return nAvail;
aeab07
 
aeab07
   return nItems;
aeab07
 }
aeab07
diff --git a/common/rdr/FdOutStream.cxx b/common/rdr/FdOutStream.cxx
aeab07
index 1757dc3..f5d07e4 100644
aeab07
--- a/common/rdr/FdOutStream.cxx
aeab07
+++ b/common/rdr/FdOutStream.cxx
aeab07
@@ -149,9 +149,10 @@ size_t FdOutStream::overrun(size_t itemSize, size_t nItems)
aeab07
     }
aeab07
   }
aeab07
 
aeab07
-  // Can we fit all the items asked for?
aeab07
-  if (itemSize * nItems > (size_t)(end - ptr))
aeab07
-    nItems = (end - ptr) / itemSize;
aeab07
+  size_t nAvail;
aeab07
+  nAvail = (end - ptr) / itemSize;
aeab07
+  if (nAvail < nItems)
aeab07
+    return nAvail;
aeab07
 
aeab07
   return nItems;
aeab07
 }
aeab07
diff --git a/common/rdr/FileInStream.cxx b/common/rdr/FileInStream.cxx
aeab07
index 94f5db8..bdb05a3 100644
aeab07
--- a/common/rdr/FileInStream.cxx
aeab07
+++ b/common/rdr/FileInStream.cxx
aeab07
@@ -68,7 +68,7 @@ size_t FileInStream::overrun(size_t itemSize, size_t nItems, bool wait)
aeab07
   ptr = b;
aeab07
 
aeab07
 
aeab07
-  while (end < b + itemSize) {
aeab07
+  while ((size_t)(end - b) < itemSize) {
aeab07
     size_t n = fread((U8 *)end, b + sizeof(b) - end, 1, file);
aeab07
     if (n == 0) {
aeab07
       if (ferror(file))
aeab07
@@ -80,8 +80,10 @@ size_t FileInStream::overrun(size_t itemSize, size_t nItems, bool wait)
aeab07
     end += b + sizeof(b) - end;
aeab07
   }
aeab07
 
aeab07
-  if (itemSize * nItems > (size_t)(end - ptr))
aeab07
-    nItems = (end - ptr) / itemSize;
aeab07
+  size_t nAvail;
aeab07
+  nAvail = (end - ptr) / itemSize;
aeab07
+  if (nAvail < nItems)
aeab07
+    return nAvail;
aeab07
 
aeab07
   return nItems;
aeab07
 }
aeab07
diff --git a/common/rdr/HexInStream.cxx b/common/rdr/HexInStream.cxx
aeab07
index 8f93988..a6bc92c 100644
aeab07
--- a/common/rdr/HexInStream.cxx
aeab07
+++ b/common/rdr/HexInStream.cxx
aeab07
@@ -91,7 +91,7 @@ size_t HexInStream::overrun(size_t itemSize, size_t nItems, bool wait) {
aeab07
   offset += ptr - start;
aeab07
   ptr = start;
aeab07
 
aeab07
-  while (end < ptr + itemSize) {
aeab07
+  while ((size_t)(end - ptr) < itemSize) {
aeab07
     size_t n = in_stream.check(2, 1, wait);
aeab07
     if (n == 0) return 0;
aeab07
     const U8* iptr = in_stream.getptr();
aeab07
@@ -110,8 +110,10 @@ size_t HexInStream::overrun(size_t itemSize, size_t nItems, bool wait) {
aeab07
     end += length;
aeab07
   }
aeab07
 
aeab07
-  if (itemSize * nItems > (size_t)(end - ptr))
aeab07
-    nItems = (end - ptr) / itemSize;
aeab07
+  size_t nAvail;
aeab07
+  nAvail = (end - ptr) / itemSize;
aeab07
+  if (nAvail < nItems)
aeab07
+    return nAvail;
aeab07
 
aeab07
   return nItems;
aeab07
 }
aeab07
diff --git a/common/rdr/HexOutStream.cxx b/common/rdr/HexOutStream.cxx
aeab07
index 7232514..eac2eff 100644
aeab07
--- a/common/rdr/HexOutStream.cxx
aeab07
+++ b/common/rdr/HexOutStream.cxx
aeab07
@@ -102,8 +102,10 @@ HexOutStream::overrun(size_t itemSize, size_t nItems) {
aeab07
 
aeab07
   writeBuffer();
aeab07
 
aeab07
-  if (itemSize * nItems > (size_t)(end - ptr))
aeab07
-    nItems = (end - ptr) / itemSize;
aeab07
+  size_t nAvail;
aeab07
+  nAvail = (end - ptr) / itemSize;
aeab07
+  if (nAvail < nItems)
aeab07
+    return nAvail;
aeab07
 
aeab07
   return nItems;
aeab07
 }
aeab07
diff --git a/common/rdr/InStream.h b/common/rdr/InStream.h
aeab07
index 14ecf09..f71a4d9 100644
aeab07
--- a/common/rdr/InStream.h
aeab07
+++ b/common/rdr/InStream.h
aeab07
@@ -43,12 +43,15 @@ namespace rdr {
aeab07
 
aeab07
     inline size_t check(size_t itemSize, size_t nItems=1, bool wait=true)
aeab07
     {
aeab07
-      if (ptr + itemSize * nItems > end) {
aeab07
-        if (ptr + itemSize > end)
aeab07
-          return overrun(itemSize, nItems, wait);
aeab07
+      size_t nAvail;
aeab07
+
aeab07
+      if (itemSize > (size_t)(end - ptr))
aeab07
+        return overrun(itemSize, nItems, wait);
aeab07
+
aeab07
+      nAvail = (end - ptr) / itemSize;
aeab07
+      if (nAvail < nItems)
aeab07
+        return nAvail;
aeab07
 
aeab07
-        nItems = (end - ptr) / itemSize;
aeab07
-      }
aeab07
       return nItems;
aeab07
     }
aeab07
 
aeab07
@@ -93,13 +96,12 @@ namespace rdr {
aeab07
     // readBytes() reads an exact number of bytes.
aeab07
 
aeab07
     void readBytes(void* data, size_t length) {
aeab07
-      U8* dataPtr = (U8*)data;
aeab07
-      U8* dataEnd = dataPtr + length;
aeab07
-      while (dataPtr < dataEnd) {
aeab07
-        size_t n = check(1, dataEnd - dataPtr);
aeab07
-        memcpy(dataPtr, ptr, n);
aeab07
+      while (length > 0) {
aeab07
+        size_t n = check(1, length);
aeab07
+        memcpy(data, ptr, n);
aeab07
         ptr += n;
aeab07
-        dataPtr += n;
aeab07
+        data = (U8*)data + n;
aeab07
+        length -= n;
aeab07
       }
aeab07
     }
aeab07
 
aeab07
diff --git a/common/rdr/MemOutStream.h b/common/rdr/MemOutStream.h
aeab07
index 4a815b3..b56bac3 100644
aeab07
--- a/common/rdr/MemOutStream.h
aeab07
+++ b/common/rdr/MemOutStream.h
aeab07
@@ -23,6 +23,7 @@
aeab07
 #ifndef __RDR_MEMOUTSTREAM_H__
aeab07
 #define __RDR_MEMOUTSTREAM_H__
aeab07
 
aeab07
+#include <rdr/Exception.h>
aeab07
 #include <rdr/OutStream.h>
aeab07
 
aeab07
 namespace rdr {
aeab07
@@ -65,6 +66,9 @@ namespace rdr {
aeab07
       if (len < (size_t)(end - start) * 2)
aeab07
         len = (end - start) * 2;
aeab07
 
aeab07
+      if (len < (size_t)(end - start))
aeab07
+        throw Exception("Overflow in MemOutStream::overrun()");
aeab07
+
aeab07
       U8* newStart = new U8[len];
aeab07
       memcpy(newStart, start, ptr - start);
aeab07
       ptr = newStart + (ptr - start);
aeab07
diff --git a/common/rdr/OutStream.h b/common/rdr/OutStream.h
aeab07
index 11aafd2..0f60ccc 100644
aeab07
--- a/common/rdr/OutStream.h
aeab07
+++ b/common/rdr/OutStream.h
aeab07
@@ -46,12 +46,15 @@ namespace rdr {
aeab07
 
aeab07
     inline size_t check(size_t itemSize, size_t nItems=1)
aeab07
     {
aeab07
-      if (ptr + itemSize * nItems > end) {
aeab07
-        if (ptr + itemSize > end)
aeab07
-          return overrun(itemSize, nItems);
aeab07
+      size_t nAvail;
aeab07
+
aeab07
+      if (itemSize > (size_t)(end - ptr))
aeab07
+        return overrun(itemSize, nItems);
aeab07
+
aeab07
+      nAvail = (end - ptr) / itemSize;
aeab07
+      if (nAvail < nItems)
aeab07
+        return nAvail;
aeab07
 
aeab07
-        nItems = (end - ptr) / itemSize;
aeab07
-      }
aeab07
       return nItems;
aeab07
     }
aeab07
 
aeab07
@@ -91,13 +94,12 @@ namespace rdr {
aeab07
     // writeBytes() writes an exact number of bytes.
aeab07
 
aeab07
     void writeBytes(const void* data, size_t length) {
aeab07
-      const U8* dataPtr = (const U8*)data;
aeab07
-      const U8* dataEnd = dataPtr + length;
aeab07
-      while (dataPtr < dataEnd) {
aeab07
-        size_t n = check(1, dataEnd - dataPtr);
aeab07
-        memcpy(ptr, dataPtr, n);
aeab07
+      while (length > 0) {
aeab07
+        size_t n = check(1, length);
aeab07
+        memcpy(ptr, data, n);
aeab07
         ptr += n;
aeab07
-        dataPtr += n;
aeab07
+        data = (U8*)data + n;
aeab07
+        length -= n;
aeab07
       }
aeab07
     }
aeab07
 
aeab07
diff --git a/common/rdr/RandomStream.cxx b/common/rdr/RandomStream.cxx
aeab07
index 7681095..6c64ac5 100644
aeab07
--- a/common/rdr/RandomStream.cxx
aeab07
+++ b/common/rdr/RandomStream.cxx
aeab07
@@ -123,8 +123,10 @@ size_t RandomStream::overrun(size_t itemSize, size_t nItems, bool wait) {
aeab07
       *(U8*)end++ = (int) (256.0*rand()/(RAND_MAX+1.0));
aeab07
   }
aeab07
 
aeab07
-  if (itemSize * nItems > (size_t)(end - ptr))
aeab07
-    nItems = (end - ptr) / itemSize;
aeab07
+  size_t nAvail;
aeab07
+  nAvail = (end - ptr) / itemSize;
aeab07
+  if (nAvail < nItems)
aeab07
+    return nAvail;
aeab07
 
aeab07
   return nItems;
aeab07
 }
aeab07
diff --git a/common/rdr/TLSInStream.cxx b/common/rdr/TLSInStream.cxx
aeab07
index d0f9426..3e1172f 100644
aeab07
--- a/common/rdr/TLSInStream.cxx
aeab07
+++ b/common/rdr/TLSInStream.cxx
aeab07
@@ -43,7 +43,7 @@ ssize_t TLSInStream::pull(gnutls_transport_ptr_t str, void* data, size_t size)
aeab07
       return -1;
aeab07
     }
aeab07
 
aeab07
-    if (in->getend() - in->getptr() < (ptrdiff_t)size)
aeab07
+    if ((size_t)(in->getend() - in->getptr()) < size)
aeab07
       size = in->getend() - in->getptr();
aeab07
   
aeab07
     in->readBytes(data, size);
aeab07
@@ -92,15 +92,17 @@ size_t TLSInStream::overrun(size_t itemSize, size_t nItems, bool wait)
aeab07
   end -= ptr - start;
aeab07
   ptr = start;
aeab07
 
aeab07
-  while (end < start + itemSize) {
aeab07
+  while ((size_t)(end - start) < itemSize) {
aeab07
     size_t n = readTLS((U8*) end, start + bufSize - end, wait);
aeab07
     if (!wait && n == 0)
aeab07
       return 0;
aeab07
     end += n;
aeab07
   }
aeab07
 
aeab07
-  if (itemSize * nItems > (size_t)(end - ptr))
aeab07
-    nItems = (end - ptr) / itemSize;
aeab07
+  size_t nAvail;
aeab07
+  nAvail = (end - ptr) / itemSize;
aeab07
+  if (nAvail < nItems)
aeab07
+    return nAvail;
aeab07
 
aeab07
   return nItems;
aeab07
 }
aeab07
diff --git a/common/rdr/TLSOutStream.cxx b/common/rdr/TLSOutStream.cxx
aeab07
index 30c456f..7d7c3b5 100644
aeab07
--- a/common/rdr/TLSOutStream.cxx
aeab07
+++ b/common/rdr/TLSOutStream.cxx
aeab07
@@ -100,8 +100,10 @@ size_t TLSOutStream::overrun(size_t itemSize, size_t nItems)
aeab07
 
aeab07
   flush();
aeab07
 
aeab07
-  if (itemSize * nItems > (size_t)(end - ptr))
aeab07
-    nItems = (end - ptr) / itemSize;
aeab07
+  size_t nAvail;
aeab07
+  nAvail = (end - ptr) / itemSize;
aeab07
+  if (nAvail < nItems)
aeab07
+    return nAvail;
aeab07
 
aeab07
   return nItems;
aeab07
 }
aeab07
diff --git a/common/rdr/ZlibInStream.cxx b/common/rdr/ZlibInStream.cxx
aeab07
index e2f971c..9fcfaf6 100644
aeab07
--- a/common/rdr/ZlibInStream.cxx
aeab07
+++ b/common/rdr/ZlibInStream.cxx
aeab07
@@ -113,8 +113,10 @@ size_t ZlibInStream::overrun(size_t itemSize, size_t nItems, bool wait)
aeab07
       return 0;
aeab07
   }
aeab07
 
aeab07
-  if (itemSize * nItems > (size_t)(end - ptr))
aeab07
-    nItems = (end - ptr) / itemSize;
aeab07
+  size_t nAvail;
aeab07
+  nAvail = (end - ptr) / itemSize;
aeab07
+  if (nAvail < nItems)
aeab07
+    return nAvail;
aeab07
 
aeab07
   return nItems;
aeab07
 }
aeab07
diff --git a/common/rdr/ZlibOutStream.cxx b/common/rdr/ZlibOutStream.cxx
aeab07
index 4e7ffd6..5e158bf 100644
aeab07
--- a/common/rdr/ZlibOutStream.cxx
aeab07
+++ b/common/rdr/ZlibOutStream.cxx
aeab07
@@ -127,8 +127,10 @@ size_t ZlibOutStream::overrun(size_t itemSize, size_t nItems)
aeab07
     }
aeab07
   }
aeab07
 
aeab07
-  if (itemSize * nItems > (size_t)(end - ptr))
aeab07
-    nItems = (end - ptr) / itemSize;
aeab07
+  size_t nAvail;
aeab07
+  nAvail = (end - ptr) / itemSize;
aeab07
+  if (nAvail < nItems)
aeab07
+    return nAvail;
aeab07
 
aeab07
   return nItems;
aeab07
 }