[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