Dear all,

I can reproduce this. Moreover, this has already been patched upstream.
I have backported the patch. Please apply by integrating
17-upstream-POLLRDHUP-handling-error-fix.patch into your debian package!

Regards
Joachim Falk
Description: Fix Backported premature data truncation problem due to over eager POLLRDHUP handling.
Origin: upstream; https://www.stunnel.org/pipermail/stunnel-users/2014-November/004860.html
Last-Update: 2015-03-04

Index: stunnel4-5.06/src/client.c
===================================================================
--- stunnel4-5.06.orig/src/client.c	2014-10-15 22:55:07.000000000 +0200
+++ stunnel4-5.06/src/client.c	2015-03-04 23:27:01.052295381 +0100
@@ -515,6 +515,11 @@
     int write_wants_read=0, write_wants_write=0;
     /* actual conditions on file descriptors */
     int sock_can_rd, sock_can_wr, ssl_can_rd, ssl_can_wr;
+#ifdef USE_WIN32
+    unsigned long bytes;
+#else
+    int bytes;
+#endif
 
     c->sock_ptr=c->ssl_ptr=0;
 
@@ -810,32 +815,44 @@
         }
 
         /****************************** check for hangup conditions */
-        if(s_poll_rdhup(c->fds, c->sock_rfd->fd)) {
-            s_log(LOG_INFO, "Read socket closed (hangup)");
+        /* http://marc.info/?l=linux-man&m=128002066306087 */
+        /* readsocket() must be the last sock_rfd operation before FIONREAD */
+        if(sock_open_rd && s_poll_rdhup(c->fds, c->sock_rfd->fd) &&
+                (ioctlsocket(c->sock_rfd->fd, FIONREAD, &bytes) || !bytes)) {
+            s_log(LOG_INFO, "Read socket closed (read hangup)");
             sock_open_rd=0;
         }
-        if(s_poll_hup(c->fds, c->sock_wfd->fd)) {
+        if(sock_open_wr && s_poll_hup(c->fds, c->sock_wfd->fd)) {
             if(c->ssl_ptr) {
                 s_log(LOG_ERR,
-                    "Write socket closed (hangup) with %d unsent byte(s)",
+                    "Write socket closed (write hangup) with %d unsent byte(s)",
                     c->ssl_ptr);
                 longjmp(c->err, 1); /* reset the socket */
             }
-            s_log(LOG_INFO, "Write socket closed (hangup)");
+            s_log(LOG_INFO, "Write socket closed (write hangup)");
             sock_open_wr=0;
         }
-        if(s_poll_hup(c->fds, c->ssl_rfd->fd) ||
-                s_poll_hup(c->fds, c->ssl_wfd->fd)) {
+        /* SSL_read() must be the last ssl_rfd operation before FIONREAD */
+        if(!(SSL_get_shutdown(c->ssl)&SSL_RECEIVED_SHUTDOWN) &&
+                s_poll_rdhup(c->fds, c->ssl_rfd->fd) &&
+                (ioctlsocket(c->ssl_rfd->fd, FIONREAD, &bytes) || !bytes)) {
             /* hangup -> buggy (e.g. Microsoft) peer:
              * SSL socket closed without close_notify alert */
+            s_log(LOG_INFO, "SSL socket closed (read hangup)");
+            SSL_set_shutdown(c->ssl,
+                SSL_get_shutdown(c->ssl)|SSL_RECEIVED_SHUTDOWN);
+        }
+        if(!(SSL_get_shutdown(c->ssl)&SSL_SENT_SHUTDOWN) &&
+                s_poll_hup(c->fds, c->ssl_wfd->fd)) {
             if(c->sock_ptr || write_wants_write) {
                 s_log(LOG_ERR,
-                    "SSL socket closed (hangup) with %d unsent byte(s)",
+                    "SSL socket closed (write hangup) with %d unsent byte(s)",
                     c->sock_ptr);
                 longjmp(c->err, 1); /* reset the socket */
             }
-            s_log(LOG_INFO, "SSL socket closed (hangup)");
-            SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
+            s_log(LOG_INFO, "SSL socket closed (write hangup)");
+            SSL_set_shutdown(c->ssl,
+                SSL_get_shutdown(c->ssl)|SSL_SENT_SHUTDOWN);
         }
 
         /****************************** check write shutdown conditions */

Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to