CVE-2020-14398 (published 2020-06-17T16:15Z)

An issue was discovered in LibVNCServer before 0.9.13. An improperly closed TCP connection causes an infinite loop in libvncclient/sockets.c.

Severity: MEDIUM
CVSS v2 base score: 5.0 (AV:N/AC:L/Au:N/C:N/I:N/A:P)
CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)

Weakness information:
cwe_id: CWE-835
cwe_name: Loop with Unreachable Exit Condition ('Infinite Loop')
description: The product contains an iteration or loop with an exit condition that cannot be reached, i.e., an infinite loop.
extended_description: If the loop can be influenced by an attacker, this weakness could allow attackers to consume excessive resources such as CPU or memory.
         

Fix information:
repo_url: https://github.com/LibVNC/libvncserver
commit_details: repo_url: https://github.com/LibVNC/libvncserver
author: Tobias Junghans
author_date: 2020-04-24 13:41:59+02:00
msg: libvncclient: handle half-open TCP connections

When a connection is not reset properly at the TCP level (e.g. sudden
power loss or process crash) the TCP connection becomes half-open and
read() always returns -1 with errno = EAGAIN while select() always
returns 0. This leads to an infinite loop and can be fixed by closing
the connection after a certain number of retries (based on a timeout)
has been exceeded.
repository: repo_url: https://github.com/LibVNC/libvncserver
repo_name: LibVNC/libvncserver
description: LibVNCServer/LibVNCClient are cross-platform C libraries that allow you to easily implement VNC server or client functionality in your program.
repo_language: C
owner: LibVNC
file_changes: filename: sockets.c
change_type: ModificationType.MODIFY
diff: @@ -62,6 +62,8 @@ rfbBool errorMessageOnReadFailure = TRUE;
 rfbBool
 ReadFromRFBServer(rfbClient* client, char *out, unsigned int n)
 {
+  const int USECS_WAIT_PER_RETRY = 100000;
+  int retries = 0;
 #undef DEBUG_READ_EXACT
 #ifdef DEBUG_READ_EXACT
 	char* oout=out;
@@ -151,10 +153,16 @@ ReadFromRFBServer(rfbClient* client, char *out, unsigned int n)
       if (i <= 0) {
 	if (i < 0) {
 	  if (errno == EWOULDBLOCK || errno == EAGAIN) {
+	    if (client->readTimeout > 0 &&
+		++retries > (client->readTimeout * 1000 * 1000 / USECS_WAIT_PER_RETRY))
+	    {
+	      rfbClientLog("Connection timed out\n");
+	      return FALSE;
+	    }
 	    /* TODO:
 	       ProcessXtEvents();
 	    */
-	    WaitForMessage(client, 100000);
+	    WaitForMessage(client, USECS_WAIT_PER_RETRY);
 	    i = 0;
 	  } else {
 	    rfbClientErr("read (%d: %s)\n",errno,strerror(errno));
@@ -194,10 +202,16 @@ ReadFromRFBServer(rfbClient* client, char *out, unsigned int n)
 	  errno=WSAGetLastError();
 #endif
 	  if (errno == EWOULDBLOCK || errno == EAGAIN) {
+	    if (client->readTimeout > 0 &&
+		++retries > (client->readTimeout * 1000 * 1000 / USECS_WAIT_PER_RETRY))
+	    {
+		rfbClientLog("Connection timed out\n");
+		return FALSE;
+	    }
 	    /* TODO:
 	       ProcessXtEvents();
 	    */
-	    WaitForMessage(client, 100000);
+	    WaitForMessage(client, USECS_WAIT_PER_RETRY);
 	    i = 0;
 	  } else {
 	    rfbClientErr("read (%s)\n",strerror(errno));

programming_language: C
filename: vncviewer.c
change_type: ModificationType.MODIFY
diff: @@ -272,6 +272,7 @@ rfbClient* rfbGetClient(int bitsPerSample,int samplesPerPixel,
   client->destPort = 5900;
   
   client->connectTimeout = DEFAULT_CONNECT_TIMEOUT;
+  client->readTimeout = DEFAULT_READ_TIMEOUT;
 
   client->CurrentKeyboardLedState = 0;
   client->HandleKeyboardLedState = (HandleKeyboardLedStateProc)DummyPoint;

programming_language: C
filename: rfbclient.h
change_type: ModificationType.MODIFY
diff: @@ -85,6 +85,7 @@
 #define SERVER_PORT_OFFSET 5900
 
 #define DEFAULT_CONNECT_TIMEOUT 60
+#define DEFAULT_READ_TIMEOUT 0
 
 #define DEFAULT_SSH_CMD "/usr/bin/ssh"
 #define DEFAULT_TUNNEL_CMD  \
@@ -454,6 +455,9 @@ typedef struct _rfbClient {
 #endif
 	/* timeout in seconds for select() after connect() */
 	unsigned int connectTimeout;
+	/* timeout in seconds when reading from half-open connections in
+	 * ReadFromRFBServer() - keep at 0 to disable timeout detection and handling */
+	unsigned int readTimeout;
 } rfbClient;
 
 /* cursor.c */

programming_language: C