[JDEV] Bug in the SSL I/O layer + fix

Daniel Veillard veillard at redhat.com
Thu Jan 17 08:00:24 CST 2002


Symptoms:
   a large write from an SSL client gets blocked/delayed for
   a long time in jabberd. 

Bug:
   the select() mechanism is not able to detect that there is some
   data left in the SSL buffers

Explanation:
   The client sends a really large chunk of data (anything > 8KB will do)
   The SSL decryption layer ends up generating an input buffer which
   is larger than the read done by the mail loop following the select
   (the read is limited to 8K and usually less due to the karma computing.
   The main loop does the read which fills up the buffer, is then 
   passed to the parser, and return in select(). The fact that the
   read() may not consume all data is usually not a problem because
   the leftovers will retrigger the exit from select. But for SSL
   (and any layer with an intermediate buffer) there may be data left,
   and select won't detect it. The data simply stalls in the buffer
   until something else triggers the read on that selector again.

Fix:
   The enclosed patch provides an approximation of the correct solution.
   The SSL read simply checks that SSL_read() filled the input buffer
   and if yes request the main loop to iterate over the read on that 
   selector. It might not be a complete solution since the read may 
   be exactly the size of the buffer. The best is to ask the SSL layer
   if there is some data left at the end of SSL_read() but I didn't
   found the right API for this. This should be fixed at the end of 
   _mio_ssl_read() by replacing "if (ret == count) {" with code asking
   the SSL layer.

Extra question:
   Is there an easy way to simply disable all Karma checks ? Or change
   them all to something more in line for distributed computing needs.

Daniel

-- 
Daniel Veillard      | Red Hat Network https://rhn.redhat.com/
veillard at redhat.com  | libxml Gnome XML XSLT toolkit  http://xmlsoft.org/
http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/
-------------- next part --------------
Index: jabberd/mio.c
===================================================================
RCS file: /home/cvs/jabberd/mio.c,v
retrieving revision 1.60
diff -c -r1.60 mio.c
*** jabberd/mio.c	2002/01/10 22:06:29	1.60
--- jabberd/mio.c	2002/01/17 13:40:00
***************
*** 74,79 ****
--- 74,80 ----
  
  /* global object */
  int mio__errno = 0;
+ int mio__ssl_reread = 0;
  ios mio__data = NULL;
  extern xmlnode greymatter__;
  
***************
*** 706,751 ****
                      continue;
                  }
  
!                 maxlen = KARMA_READ_MAX(cur->k.val);
  
!                 if(maxlen > 8191) maxlen = 8191;
  
!                 len = (*(cur->mh->read))(cur, buf, maxlen);
  
!                 /* if we had a bad read */
!                 if(len == 0 && maxlen > 0)
!                 { 
!                     mio_close(cur);
!                     continue; /* loop on the same socket to kill it for real */
!                 }
!                 else if(len < 0)
!                 {
!                     if(errno != EWOULDBLOCK && errno != EINTR && 
!                        errno != EAGAIN && mio__errno != EAGAIN) 
!                     {
!                         /* kill this socket and move on */
!                         mio_close(cur);
!                         continue;  /* loop on the same socket to kill it for real */
!                     }
!                 }
!                 else 
!                 {
!                     if(cur->k.dec != 0)
!                     { /* karma is enabled */
!                         karma_decrement(&cur->k, len);
!                         /* Check if that socket ran out of karma */
!                         if(cur->k.val <= 0)
!                         { /* ran out of karma */
!                             log_notice("MIO_XML_READ", "socket from %s is out of karma", cur->ip);
!                             FD_CLR(cur->fd, &all_rfds); /* this fd is being punished */
!                         }
!                     }
! 
!                     buf[len] = '\0';
! 
!                     log_debug(ZONE, "MIO read from socket %d: %s", cur->fd, buf);
!                     (*cur->mh->parser)(cur, buf, len);
!                 }
              } 
  
              /* we could have gotten a bad parse, and want to close */
--- 707,755 ----
                      continue;
                  }
  
! 		do {
! 		    maxlen = KARMA_READ_MAX(cur->k.val);
  
! 		    if(maxlen > 8191) maxlen = 8191;
  
! 		    mio__ssl_reread = 0;
! 		    len = (*(cur->mh->read))(cur, buf, maxlen);
  
! 		    /* if we had a bad read */
! 		    if(len == 0 && maxlen > 0)
! 		    { 
! 			mio_close(cur);
! 			continue; /* loop on the same socket to kill it for real */
! 		    }
! 		    else if(len < 0)
! 		    {
! 			if(errno != EWOULDBLOCK && errno != EINTR && 
! 			   errno != EAGAIN && mio__errno != EAGAIN) 
! 			{
! 			    /* kill this socket and move on */
! 			    mio_close(cur);
! 			    continue;  /* loop on the same socket to kill it for real */
! 			}
! 		    }
! 		    else 
! 		    {
! 			if(cur->k.dec != 0)
! 			{ /* karma is enabled */
! 			    karma_decrement(&cur->k, len);
! 			    /* Check if that socket ran out of karma */
! 			    if(cur->k.val <= 0)
! 			    { /* ran out of karma */
! 				log_notice("MIO_XML_READ", "socket from %s is out of karma", cur->ip);
! 				FD_CLR(cur->fd, &all_rfds); /* this fd is being punished */
! 			    }
! 			}
! 
! 			buf[len] = '\0';
! 
! 			log_debug(ZONE, "MIO read from socket %d: %s", cur->fd, buf);
! 			(*cur->mh->parser)(cur, buf, len);
! 		    }
! 		} while (mio__ssl_reread == 1);
              } 
  
              /* we could have gotten a bad parse, and want to close */
Index: jabberd/mio_ssl.c
===================================================================
RCS file: /home/cvs/jabberd/mio_ssl.c,v
retrieving revision 1.10
diff -c -r1.10 mio_ssl.c
*** jabberd/mio_ssl.c	2001/10/02 20:43:06	1.10
--- jabberd/mio_ssl.c	2002/01/17 13:40:00
***************
*** 4,9 ****
--- 4,10 ----
  
  HASHTABLE ssl__ctxs;
  extern int mio__errno;
+ extern int mio__ssl_reread;
  
  
  #ifndef NO_RSA
***************
*** 136,141 ****
--- 137,144 ----
  ssize_t _mio_ssl_read(mio m, void *buf, size_t count)
  {
      SSL *ssl;
+     ssize_t ret;
+     int sret; 
  
      ssl = m->ssl;
      
***************
*** 143,152 ****
          return 0;
  
      log_debug(ZONE, "Asked to read %d bytes from %d", count, m->fd);
      if(SSL_get_state(ssl) != SSL_ST_OK)
      {
-         int sret; 
- 
          sret = SSL_accept(ssl);
          if(sret <= 0)
          {
--- 146,154 ----
          return 0;
  
      log_debug(ZONE, "Asked to read %d bytes from %d", count, m->fd);
+     mio__ssl_reread = 0;
      if(SSL_get_state(ssl) != SSL_ST_OK)
      {
          sret = SSL_accept(ssl);
          if(sret <= 0)
          {
***************
*** 168,175 ****
              close(m->fd);
              return -1;
          }       
      }
!     return SSL_read(ssl, (char *)buf, count);
  }
  
  ssize_t _mio_ssl_write(mio m, const void *buf, size_t count)
--- 170,182 ----
              close(m->fd);
              return -1;
          }       
+     }
+     ret = SSL_read(ssl, (char *)buf, count);
+     if (ret == count) {
+ 	mio__ssl_reread = 1;
+ 	log_debug(ZONE, "SSL Asked to reread from %d", m->fd);
      }
!     return ret;
  }
  
  ssize_t _mio_ssl_write(mio m, const void *buf, size_t count)


More information about the JDev mailing list