CVE-2019-15694 (C, published 2019-12-26T15:15Z)
Description: value: TigerVNC version prior to 1.10.1 is vulnerable to heap buffer overflow, which could be triggered from DecodeManager::decodeRect. Vulnerability occurs due to the signdness error in processing MemOutStream. Exploitation of this vulnerability could potentially result into remote code execution. This attack appear to be exploitable via network connectivity.
Severity: MEDIUM (CVSS v3 base score: 7.2)
Weakness: CWE-787 \u2014 Out-of-bounds Write
The product writes data past the end, or before the beginning, of the intended buffer.
Repository: https://github.com/CendioOssman/tigervnc
Files changed:
common/rdr/FdInStream.cxx
common/rdr/FdInStream.h
common/rdr/FdOutStream.cxx
common/rdr/FdOutStream.h
common/rdr/FileInStream.cxx
common/rdr/FileInStream.h
common/rdr/HexInStream.cxx
common/rdr/HexInStream.h
common/rdr/HexOutStream.cxx
common/rdr/HexOutStream.h
common/rdr/InStream.h
common/rdr/MemInStream.h
common/rdr/MemOutStream.h
common/rdr/OutStream.h
common/rdr/RandomStream.cxx
common/rdr/RandomStream.h
common/rdr/TLSInStream.cxx
common/rdr/TLSInStream.h
common/rdr/TLSOutStream.cxx
common/rdr/TLSOutStream.h
common/rdr/ZlibInStream.cxx
common/rdr/ZlibInStream.h
common/rdr/ZlibOutStream.cxx
common/rdr/ZlibOutStream.h
common/rfb/Configuration.cxx
common/rfb/Configuration.h
common/rfb/Password.cxx
common/rfb/Password.h
common/rfb/util.h
tests/perf/encperf.cxx
win/rfb_win32/Registry.cxx
win/rfb_win32/Registry.h
Commit message: Use size_t for lengths in stream objects

Provides safety against them accidentally becoming negative because
of bugs in the calculations.

Also does the same to CharArray and friends as they were strongly
connection to the stream objects.


Vulnerable code:
FdInStream::FdInStream(int fd_, int timeoutms_, int bufSize_,
                       int bufSize_)
int FdInStream::pos()
void FdInStream::readBytes(void* data, int length)
  int n = end - ptr;
int FdInStream::overrun(int itemSize, int nItems, bool wait)
  int bytes_to_read;
    int n = readWithTimeoutOrCallback((U8*)end, bytes_to_read, wait);
  if (itemSize * nItems > end - ptr)
int FdInStream::readWithTimeoutOrCallback(void* buf, int len, bool wait)
    FdInStream(int fd, int timeoutms=-1, int bufSize=0,
    FdInStream(int fd, FdInStreamBlockCallback* blockCallback, int bufSize=0);
    int pos();
    void readBytes(void* data, int length);
    int overrun(int itemSize, int nItems, bool wait);
    int readWithTimeoutOrCallback(void* buf, int len, bool wait=true);
    int bufSize;
    int offset;
FdOutStream::FdOutStream(int fd_, bool blocking_, int timeoutms_, int bufSize_)
int FdOutStream::length()
    int n = writeWithTimeout((const void*) sentUpTo,
                             ptr - sentUpTo,
                             blocking? timeoutms : 0);
int FdOutStream::overrun(int itemSize, int nItems)
  if (itemSize > end - ptr) {
    if ((sentUpTo - start > bufSize / 4) &&
  if (itemSize * nItems > end - ptr)
int FdOutStream::writeWithTimeout(const void* data, int length, int timeoutms)
    FdOutStream(int fd, bool blocking=true, int timeoutms=-1, int bufSize=0);
    int length();
    int overrun(int itemSize, int nItems);
    int writeWithTimeout(const void* data, int length, int timeoutms);
    int bufSize;
    int offset;
int FileInStream::pos()
int FileInStream::overrun(int itemSize, int nItems, bool wait)
  if (itemSize > (int)sizeof(b))
  if (itemSize * nItems > end - ptr)
    int pos();
    int overrun(int itemSize, int nItems, bool wait = true);
HexInStream::HexInStream(InStream& is, int bufSize_)
bool HexInStream::hexStrToBin(const char* s, char** data, int* length) {
  int l=strlen(s);
    for(int i=0;i<l;i+=2) {
int HexInStream::pos() {
int HexInStream::overrun(int itemSize, int nItems, bool wait) {
    int n = in_stream.check(2, 1, wait);
    int length = min((eptr - iptr)/2, start + bufSize - end);
    for (int i=0; i<length; i++) {
  if (itemSize * nItems > end - ptr)
    HexInStream(InStream& is, int bufSize=0);
    int pos();
    static bool hexStrToBin(const char* s, char** data, int* length);
    int overrun(int itemSize, int nItems, bool wait);
    int bufSize;
    int offset;
static inline int min(int a, int b) {return a<b ? a : b;}
HexOutStream::HexOutStream(OutStream& os, int buflen)
char* HexOutStream::binToHexStr(const char* data, int length) {
  for (int i=0; i<length; i++) {
    int length = min(ptr-pos, (oend-optr)/2);
    for (int i=0; i<length; i++) {
int HexOutStream::length()
int
HexOutStream::overrun(int itemSize, int nItems) {
  if (itemSize * nItems > end - ptr)
    HexOutStream(OutStream& os, int buflen=0);
    int length();
    static char* binToHexStr(const char* data, int length);
    int overrun(int itemSize, int nItems);
    int offset;
    int bufSize;
    inline int check(int itemSize, int nItems=1, bool wait=true)
    inline bool checkNoWait(int length) { return check(length, 1, false)!=0; }
    inline void skip(int bytes) {
        int n = check(1, bytes);
    void readBytes(void* data, int length) {
        int n = check(1, dataEnd - dataPtr);
    virtual int pos() = 0;
    virtual int overrun(int itemSize, int nItems, bool wait=true) = 0;
    MemInStream(const void* data, int len, bool deleteWhenDone_=false)
    int pos() { return ptr - start; }
    void reposition(int pos) { ptr = start + pos; }
    int overrun(int itemSize, int nItems, bool wait) { throw EndOfStream(); }
    void writeBytes(const void* data, int length) {
    int length() { return ptr - start; }
    void reposition(int pos) { ptr = start + pos; }
    int overrun(int itemSize, int nItems) {
      int len = ptr - start + itemSize * nItems;
      if (len < (end - start) * 2)
    inline int check(int itemSize, int nItems=1)
    inline void pad(int bytes) {
    inline void skip(int bytes) {
        int n = check(1, bytes);
    void writeBytes(const void* data, int length) {
        int n = check(1, dataEnd - dataPtr);
    void copyBytes(InStream* is, int length) {
        int n = check(1, length);
    virtual int length() = 0;
    virtual int overrun(int itemSize, int nItems) = 0;
const int DEFAULT_BUF_LEN = 256;
int RandomStream::pos() {
int RandomStream::overrun(int itemSize, int nItems, bool wait) {
  int length = start + DEFAULT_BUF_LEN - end;
    int n = fread((U8*)end, length, 1, fp);
    for (int i=0; i<length; i++)
  if (itemSize * nItems > end - ptr)
    int pos();
    int overrun(int itemSize, int nItems, bool wait);
    int offset;
int TLSInStream::pos()
int TLSInStream::overrun(int itemSize, int nItems, bool wait)
    int n = readTLS((U8*) end, start + bufSize - end, wait);
  if (itemSize * nItems > end - ptr)
int TLSInStream::readTLS(U8* buf, int len, bool wait)
    int pos();
    int overrun(int itemSize, int nItems, bool wait);
    int readTLS(U8* buf, int len, bool wait);
    int bufSize;
    int offset;
int TLSOutStream::length()
    int n = writeTLS(sentUpTo, ptr - sentUpTo);
int TLSOutStream::overrun(int itemSize, int nItems)
  if (itemSize * nItems > end - ptr)
int TLSOutStream::writeTLS(const U8* data, int length)
    int length();
    int overrun(int itemSize, int nItems);
    int writeTLS(const U8* data, int length);
    int bufSize;
    int offset;
ZlibInStream::ZlibInStream(int bufSize_)
void ZlibInStream::setUnderlying(InStream* is, int bytesIn_)
int ZlibInStream::pos()
int ZlibInStream::overrun(int itemSize, int nItems, bool wait)
  while (end - ptr < itemSize) {
  if (itemSize * nItems > end - ptr)
  int n = underlying->check(1, 1, wait);
  if ((int)zs->avail_in > bytesIn)
    ZlibInStream(int bufSize=0);
    void setUnderlying(InStream* is, int bytesIn);
    int pos();
    int overrun(int itemSize, int nItems, bool wait);
    int bufSize;
    int offset;
    int bytesIn;
ZlibOutStream::ZlibOutStream(OutStream* os, int bufSize_, int compressLevel)
int ZlibOutStream::length()
int ZlibOutStream::overrun(int itemSize, int nItems)
  while (end - ptr < itemSize) {
  if (itemSize * nItems > end - ptr)
    ZlibOutStream(OutStream* os=0, int bufSize=0, int compressionLevel=-1);
    int length();
    int overrun(int itemSize, int nItems);
    int bufSize;
    int offset;
				 const void* v, int l, ConfigurationObject co)
void BinaryParameter::setParam(const void* v, int len) {
void BinaryParameter::getData(void** data_, int* length_) const {
    BinaryParameter(const char* name_, const char* desc_, const void* v, int l,
		    ConfigurationObject co=ConfGlobal);
    virtual void setParam(const void* v, int l);
    void getData(void** data, int* length) const;
    int length;
    int def_length;
PlainPasswd::PlainPasswd(int len) : CharArray(len) {
ObfuscatedPasswd::ObfuscatedPasswd(int len) : CharArray(len), length(len) {
  int l = strlen(plainPwd.buf), i;
    PlainPasswd(int len);
    ObfuscatedPasswd(int l);
    int length;
    CharArray(int len) {
  virtual int length();
  virtual int overrun(int itemSize, int nItems);
int DummyOutStream::length()
int DummyOutStream::overrun(int itemSize, int nItems)
  if (itemSize * nItems > end - ptr)
void RegKey::setBinary(const TCHAR* valname, const void* value, int length) const {
void RegKey::getBinary(const TCHAR* valname, void** data, int* length) const {
void RegKey::getBinary(const TCHAR* valname, void** data, int* length, void* def, int deflen) const {
      void setBinary(const TCHAR* valname, const void* data, int length) const;
      void getBinary(const TCHAR* valname, void** data, int* length) const;
      void getBinary(const TCHAR* valname, void** data, int* length, void* def, int deflength) const;

Fixed code:
FdInStream::FdInStream(int fd_, int timeoutms_, size_t bufSize_,
                       size_t bufSize_)
size_t FdInStream::pos()
void FdInStream::readBytes(void* data, size_t length)
  size_t n = end - ptr;
size_t FdInStream::overrun(size_t itemSize, size_t nItems, bool wait)
  size_t bytes_to_read;
    size_t n = readWithTimeoutOrCallback((U8*)end, bytes_to_read, wait);
  if (itemSize * nItems > (size_t)(end - ptr))
size_t FdInStream::readWithTimeoutOrCallback(void* buf, size_t len, bool wait)
    FdInStream(int fd, int timeoutms=-1, size_t bufSize=0,
    FdInStream(int fd, FdInStreamBlockCallback* blockCallback,
               size_t bufSize=0);
    size_t pos();
    void readBytes(void* data, size_t length);
    size_t overrun(size_t itemSize, size_t nItems, bool wait);
    size_t readWithTimeoutOrCallback(void* buf, size_t len, bool wait=true);
    size_t bufSize;
    size_t offset;
FdOutStream::FdOutStream(int fd_, bool blocking_, int timeoutms_, size_t bufSize_)
size_t FdOutStream::length()
    size_t n = writeWithTimeout((const void*) sentUpTo,
                                ptr - sentUpTo,
                                blocking? timeoutms : 0);
size_t FdOutStream::overrun(size_t itemSize, size_t nItems)
  if (itemSize > (size_t)(end - ptr)) {
    if (((size_t)(sentUpTo - start) > bufSize / 4) &&
  if (itemSize * nItems > (size_t)(end - ptr))
size_t FdOutStream::writeWithTimeout(const void* data, size_t length, int timeoutms)
    FdOutStream(int fd, bool blocking=true, int timeoutms=-1, size_t bufSize=0);
    size_t length();
    size_t overrun(size_t itemSize, size_t nItems);
    size_t writeWithTimeout(const void* data, size_t length, int timeoutms);
    size_t bufSize;
    size_t offset;
size_t FileInStream::pos()
size_t FileInStream::overrun(size_t itemSize, size_t nItems, bool wait)
  if (itemSize > sizeof(b))
  if (itemSize * nItems > (size_t)(end - ptr))
    size_t pos();
    size_t overrun(size_t itemSize, size_t nItems, bool wait = true);
HexInStream::HexInStream(InStream& is, size_t bufSize_)
bool HexInStream::hexStrToBin(const char* s, char** data, size_t* length) {
  size_t l=strlen(s);
    for(size_t i=0;i<l;i+=2) {
size_t HexInStream::pos() {
size_t HexInStream::overrun(size_t itemSize, size_t nItems, bool wait) {
    size_t n = in_stream.check(2, 1, wait);
    size_t length = min((eptr - iptr)/2, start + bufSize - end);
    for (size_t i=0; i<length; i++) {
  if (itemSize * nItems > (size_t)(end - ptr))
    HexInStream(InStream& is, size_t bufSize=0);
    size_t pos();
    static bool hexStrToBin(const char* s, char** data, size_t* length);
    size_t overrun(size_t itemSize, size_t nItems, bool wait);
    size_t bufSize;
    size_t offset;
static inline size_t min(size_t a, size_t b) {return a<b ? a : b;}
HexOutStream::HexOutStream(OutStream& os, size_t buflen)
char* HexOutStream::binToHexStr(const char* data, size_t length) {
  for (size_t i=0; i<length; i++) {
    size_t length = min(ptr-pos, (oend-optr)/2);
    for (size_t i=0; i<length; i++) {
size_t HexOutStream::length()
size_t
HexOutStream::overrun(size_t itemSize, size_t nItems) {
  if (itemSize * nItems > (size_t)(end - ptr))
    HexOutStream(OutStream& os, size_t buflen=0);
    size_t length();
    static char* binToHexStr(const char* data, size_t length);
    size_t overrun(size_t itemSize, size_t nItems);
    size_t offset;
    size_t bufSize;
    inline size_t check(size_t itemSize, size_t nItems=1, bool wait=true)
    inline bool checkNoWait(size_t length) { return check(length, 1, false)!=0; }
    inline void skip(size_t bytes) {
        size_t n = check(1, bytes);
    void readBytes(void* data, size_t length) {
        size_t n = check(1, dataEnd - dataPtr);
    virtual size_t pos() = 0;
    virtual size_t overrun(size_t itemSize, size_t nItems, bool wait=true) = 0;
    MemInStream(const void* data, size_t len, bool deleteWhenDone_=false)
    size_t pos() { return ptr - start; }
    void reposition(size_t pos) { ptr = start + pos; }
    size_t overrun(size_t itemSize, size_t nItems, bool wait) { throw EndOfStream(); }
    void writeBytes(const void* data, size_t length) {
    size_t length() { return ptr - start; }
    void reposition(size_t pos) { ptr = start + pos; }
    size_t overrun(size_t itemSize, size_t nItems) {
      size_t len = ptr - start + itemSize * nItems;
      if (len < (size_t)(end - start) * 2)
    inline size_t check(size_t itemSize, size_t nItems=1)
    inline void pad(size_t bytes) {
    inline void skip(size_t bytes) {
        size_t n = check(1, bytes);
    void writeBytes(const void* data, size_t length) {
        size_t n = check(1, dataEnd - dataPtr);
    void copyBytes(InStream* is, size_t length) {
        size_t n = check(1, length);
    virtual size_t length() = 0;
    virtual size_t overrun(size_t itemSize, size_t nItems) = 0;
const size_t DEFAULT_BUF_LEN = 256;
size_t RandomStream::pos() {
size_t RandomStream::overrun(size_t itemSize, size_t nItems, bool wait) {
  size_t length = start + DEFAULT_BUF_LEN - end;
    size_t n = fread((U8*)end, length, 1, fp);
    for (size_t i=0; i<length; i++)
  if (itemSize * nItems > (size_t)(end - ptr))
    size_t pos();
    size_t overrun(size_t itemSize, size_t nItems, bool wait);
    size_t offset;
size_t TLSInStream::pos()
size_t TLSInStream::overrun(size_t itemSize, size_t nItems, bool wait)
    size_t n = readTLS((U8*) end, start + bufSize - end, wait);
  if (itemSize * nItems > (size_t)(end - ptr))
size_t TLSInStream::readTLS(U8* buf, size_t len, bool wait)
    size_t pos();
    size_t overrun(size_t itemSize, size_t nItems, bool wait);
    size_t readTLS(U8* buf, size_t len, bool wait);
    size_t bufSize;
    size_t offset;
size_t TLSOutStream::length()
    size_t n = writeTLS(sentUpTo, ptr - sentUpTo);
size_t TLSOutStream::overrun(size_t itemSize, size_t nItems)
  if (itemSize * nItems > (size_t)(end - ptr))
size_t TLSOutStream::writeTLS(const U8* data, size_t length)
    size_t length();
    size_t overrun(size_t itemSize, size_t nItems);
    size_t writeTLS(const U8* data, size_t length);
    size_t bufSize;
    size_t offset;
ZlibInStream::ZlibInStream(size_t bufSize_)
void ZlibInStream::setUnderlying(InStream* is, size_t bytesIn_)
size_t ZlibInStream::pos()
size_t ZlibInStream::overrun(size_t itemSize, size_t nItems, bool wait)
  while ((size_t)(end - ptr) < itemSize) {
  if (itemSize * nItems > (size_t)(end - ptr))
  size_t n = underlying->check(1, 1, wait);
  if (zs->avail_in > bytesIn)
    ZlibInStream(size_t bufSize=0);
    void setUnderlying(InStream* is, size_t bytesIn);
    size_t pos();
    size_t overrun(size_t itemSize, size_t nItems, bool wait);
    size_t bufSize;
    size_t offset;
    size_t bytesIn;
ZlibOutStream::ZlibOutStream(OutStream* os, size_t bufSize_, int compressLevel)
size_t ZlibOutStream::length()
size_t ZlibOutStream::overrun(size_t itemSize, size_t nItems)
  while ((size_t)(end - ptr) < itemSize) {
  if (itemSize * nItems > (size_t)(end - ptr))
    ZlibOutStream(OutStream* os=0, size_t bufSize=0, int compressionLevel=-1);
    size_t length();
    size_t overrun(size_t itemSize, size_t nItems);
    size_t bufSize;
    size_t offset;
				 const void* v, size_t l, ConfigurationObject co)
void BinaryParameter::setParam(const void* v, size_t len) {
void BinaryParameter::getData(void** data_, size_t* length_) const {
    BinaryParameter(const char* name_, const char* desc_,
                    const void* v, size_t l,
                    ConfigurationObject co=ConfGlobal);
    virtual void setParam(const void* v, size_t l);
    void getData(void** data, size_t* length) const;
    size_t length;
    size_t def_length;
PlainPasswd::PlainPasswd(size_t len) : CharArray(len) {
ObfuscatedPasswd::ObfuscatedPasswd(size_t len) : CharArray(len), length(len) {
  size_t l = strlen(plainPwd.buf), i;
    PlainPasswd(size_t len);
    ObfuscatedPasswd(size_t l);
    size_t length;
    CharArray(size_t len) {
  virtual size_t length();
  virtual size_t overrun(size_t itemSize, size_t nItems);
size_t DummyOutStream::length()
size_t DummyOutStream::overrun(size_t itemSize, size_t nItems)
  if (itemSize * nItems > (size_t)(end - ptr))
void RegKey::setBinary(const TCHAR* valname, const void* value, size_t length) const {
void RegKey::getBinary(const TCHAR* valname, void** data, size_t* length) const {
void RegKey::getBinary(const TCHAR* valname, void** data, size_t* length, void* def, size_t deflen) const {
      void setBinary(const TCHAR* valname, const void* data, size_t length) const;
      void getBinary(const TCHAR* valname, void** data, size_t* length) const;
      void getBinary(const TCHAR* valname, void** data, size_t* length, void* def, size_t deflength) const;