-fixing minor issues (leaks, use after free) in recently added upgrade logic

This commit is contained in:
Christian Grothoff
2016-09-04 12:35:46 +00:00
parent b25731eb25
commit b32f9a0e3d
5 changed files with 142 additions and 49 deletions
+1 -1
View File
@@ -161,7 +161,7 @@ Examples based on reports we've received from developers include:
@cindex select
MHD supports four basic thread modes and up to three event loop
styes.
styles.
The four basic thread modes are external (MHD creates no threads,
event loop is fully managed by the application), internal (MHD creates
+111 -34
View File
@@ -653,25 +653,29 @@ urh_to_fdset (struct MHD_UpgradeResponseHandle *urh,
MHD_socket *max_fd,
unsigned int fd_setsize)
{
if ( (0 == (MHD_EPOLL_STATE_READ_READY & urh->mhd.celi)) &&
if ( (urh->out_buffer_off < urh->out_buffer_size) &&
(MHD_INVALID_SOCKET != urh->mhd.socket) &&
(! MHD_add_to_fd_set_ (urh->mhd.socket,
rs,
max_fd,
fd_setsize)) )
return MHD_NO;
if ( (0 == (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi)) &&
(MHD_INVALID_SOCKET != urh->mhd.socket) &&
(! MHD_add_to_fd_set_ (urh->mhd.socket,
ws,
max_fd,
fd_setsize)) )
return MHD_NO;
if ( (0 == (MHD_EPOLL_STATE_READ_READY & urh->app.celi)) &&
if ( (urh->in_buffer_off < urh->in_buffer_size) &&
(MHD_INVALID_SOCKET != urh->connection->socket_fd) &&
(! MHD_add_to_fd_set_ (urh->connection->socket_fd,
rs,
max_fd,
fd_setsize)) )
return MHD_NO;
if ( (0 == (MHD_EPOLL_STATE_WRITE_READY & urh->app.celi)) &&
(MHD_INVALID_SOCKET != urh->connection->socket_fd) &&
(! MHD_add_to_fd_set_ (urh->connection->socket_fd,
ws,
max_fd,
@@ -966,6 +970,12 @@ process_urh (struct MHD_UpgradeResponseHandle *urh)
{
urh->in_buffer_off += res;
}
if (0 == res)
{
/* connection was shut down, signal by shrinking buffer,
which will eventually ensure this FD is no longer listed. */
urh->in_buffer_size = urh->in_buffer_off;
}
}
if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi)) &&
(urh->in_buffer_off > 0) )
@@ -977,8 +987,18 @@ process_urh (struct MHD_UpgradeResponseHandle *urh)
urh->in_buffer_off);
if (-1 == res)
{
/* FIXME: differenciate by errno? */
urh->mhd.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
int err = MHD_socket_get_error_ ();
if ( (MHD_SCKT_ERR_IS_EINTR_ (err)) ||
(MHD_SCKT_ERR_IS_EAGAIN_ (err)) )
urh->mhd.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
else
{
/* persistent / unrecoverable error, treat as
if connection was shut down */
urh->in_buffer_size = 0;
urh->in_buffer_off = 0;
}
}
else
{
@@ -1014,6 +1034,12 @@ process_urh (struct MHD_UpgradeResponseHandle *urh)
{
urh->out_buffer_off += res;
}
if (0 == res)
{
/* connection was shut down, signal by shrinking buffer,
which will eventually ensure this FD is no longer listed. */
urh->out_buffer_size = urh->out_buffer_off;
}
fin_read = (0 == res);
}
else
@@ -1045,6 +1071,13 @@ process_urh (struct MHD_UpgradeResponseHandle *urh)
urh->out_buffer_off = 0;
}
}
else
{
/* persistent / unrecoverable error, treat as
if connection was shut down */
urh->out_buffer_size = 0;
urh->out_buffer_off = 0;
}
}
if ( (fin_read) &&
(0 == urh->out_buffer_off) &&
@@ -1064,20 +1097,10 @@ static void
thread_main_connection_upgrade (struct MHD_Connection *con)
{
struct MHD_Daemon *daemon = con->daemon;
struct MHD_UpgradeResponseHandle *urh = con->urh;
if (0 == (daemon->options & MHD_USE_SSL))
{
/* Here, we need to block until the application
signals us that it is done with the socket */
MHD_semaphore_down (con->upgrade_sem);
MHD_semaphore_destroy (con->upgrade_sem);
con->upgrade_sem = NULL;
return;
}
#if HTTPS_SUPPORT
{
struct MHD_UpgradeResponseHandle *urh = con->urh;
/* Here, we need to bi-directionally forward
until the application tells us that it is done
with the socket; */
@@ -1108,11 +1131,14 @@ thread_main_connection_upgrade (struct MHD_Connection *con)
#endif
break;
}
num_ready = MHD_SYS_select_ (max_fd + 1,
&rs,
&ws,
NULL,
NULL);
if (MHD_INVALID_SOCKET != max_fd)
num_ready = MHD_SYS_select_ (max_fd + 1,
&rs,
&ws,
NULL,
NULL);
else
num_ready = 0;
if (num_ready < 0)
{
const int err = MHD_socket_get_error_();
@@ -1131,31 +1157,37 @@ thread_main_connection_upgrade (struct MHD_Connection *con)
&rs,
&ws);
process_urh (urh);
if ( (0 == urh->out_buffer_size) &&
(0 == urh->in_buffer_size) )
break; /* connections died, we have no more purpose here */
}
}
#ifdef HAVE_POLL
else
{
/* use poll() */
struct pollfd p[2];
const unsigned int timeout = UINT_MAX;
p[0].fd = urh->connection->socket_fd;
p[1].fd = urh->mhd.socket;
while (MHD_CONNECTION_UPGRADE == con->state)
{
if (0 == (MHD_EPOLL_STATE_READ_READY & urh->app.celi))
struct pollfd p[2];
memset (p, 0, sizeof (struct pollfd) * 2);
p[0].fd = urh->connection->socket_fd;
p[1].fd = urh->mhd.socket;
if (urh->in_buffer_off < urh->in_buffer_size)
p[0].events |= POLLIN;
if (0 == (MHD_EPOLL_STATE_WRITE_READY & urh->app.celi))
p[0].events |= POLLOUT;
if (0 == (MHD_EPOLL_STATE_READ_READY & urh->mhd.celi))
if (urh->out_buffer_off < urh->out_buffer_size)
p[1].events |= POLLIN;
if (0 == (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi))
p[1].events |= POLLOUT;
if (MHD_sys_poll_ (p,
2,
timeout) < 0)
if ( (0 != (p[0].events | p[1].events)) &&
(MHD_sys_poll_ (p,
2,
timeout) < 0) )
{
const int err = MHD_socket_get_error_ ();
@@ -1177,6 +1209,9 @@ thread_main_connection_upgrade (struct MHD_Connection *con)
if (0 != (p[1].revents & POLLOUT))
urh->mhd.celi |= MHD_EPOLL_STATE_WRITE_READY;
process_urh (urh);
if ( (0 == urh->out_buffer_size) &&
(0 == urh->in_buffer_size) )
break; /* connections died, we have no more purpose here */
}
}
/* end POLL */
@@ -1187,6 +1222,13 @@ thread_main_connection_upgrade (struct MHD_Connection *con)
MHD_PANIC ("This should not be possible\n");
#endif
}
/* Here, we need to block until the application
signals us that it is done with the socket */
MHD_semaphore_down (con->upgrade_sem);
MHD_semaphore_destroy (con->upgrade_sem);
con->upgrade_sem = NULL;
free (urh);
}
@@ -2598,6 +2640,7 @@ MHD_run_from_select (struct MHD_Daemon *daemon,
struct MHD_Connection *next;
#if HTTPS_SUPPORT
struct MHD_UpgradeResponseHandle *urh;
struct MHD_UpgradeResponseHandle *urhn;
#endif
unsigned int mask = MHD_USE_SUSPEND_RESUME | MHD_USE_EPOLL_INTERNALLY |
MHD_USE_SELECT_INTERNALLY | MHD_USE_POLL_INTERNALLY | MHD_USE_THREAD_PER_CONNECTION;
@@ -2650,8 +2693,9 @@ MHD_run_from_select (struct MHD_Daemon *daemon,
/* handle upgraded HTTPS connections */
#if HTTPS_SUPPORT
for (urh = daemon->urh_head; NULL != urh; urh = urh->next)
for (urh = daemon->urh_head; NULL != urh; urh = urhn)
{
urhn = urh->next;
/* update urh state based on select() output */
urh_from_fdset (urh,
read_fd_set,
@@ -2934,13 +2978,13 @@ MHD_poll_all (struct MHD_Daemon *daemon,
for (urh = daemon->urh_head; NULL != urh; urh = urh->next)
{
p[poll_server+i].fd = urh->connection->socket_fd;
if (0 == (MHD_EPOLL_STATE_READ_READY & urh->app.celi))
if (urh->in_buffer_off < urh->in_buffer_size)
p[poll_server+i].events |= POLLIN;
if (0 == (MHD_EPOLL_STATE_WRITE_READY & urh->app.celi))
p[poll_server+i].events |= POLLOUT;
i++;
p[poll_server+i].fd = urh->mhd.socket;
if (0 == (MHD_EPOLL_STATE_READ_READY & urh->mhd.celi))
if (urh->out_buffer_off < urh->out_buffer_size)
p[poll_server+i].events |= POLLIN;
if (0 == (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi))
p[poll_server+i].events |= POLLOUT;
@@ -2952,7 +2996,9 @@ MHD_poll_all (struct MHD_Daemon *daemon,
free(p);
return MHD_YES;
}
if (MHD_sys_poll_(p, poll_server + num_connections, timeout) < 0)
if (MHD_sys_poll_(p,
poll_server + num_connections,
timeout) < 0)
{
const int err = MHD_socket_get_error_ ();
if (MHD_SCKT_ERR_IS_EINTR_ (err))
@@ -2988,17 +3034,20 @@ MHD_poll_all (struct MHD_Daemon *daemon,
next = pos->next;
/* first, sanity checks */
if (i >= num_connections)
continue; /* connection list changed somehow, retry later ... */
break; /* connection list changed somehow, retry later ... */
if (p[poll_server+i].fd != pos->socket_fd)
continue; /* fd mismatch, something else happened, retry later ... */
call_handlers (pos,
0 != (p[poll_server+i].revents & POLLIN),
0 != (p[poll_server+i].revents & POLLOUT),
MHD_NO);
i++;
}
#if HTTPS_SUPPORT
for (urh = daemon->urh_head; NULL != urh; urh = urhn)
{
if (i >= num_connections)
break; /* connection list changed somehow, retry later ... */
urhn = urh->next;
if (p[poll_server+i].fd != urh->connection->socket_fd)
continue; /* fd mismatch, something else happened, retry later ... */
@@ -3148,6 +3197,7 @@ run_epoll_for_upgrade (struct MHD_Daemon *daemon)
struct epoll_event events[MAX_EVENTS];
int num_events;
unsigned int i;
unsigned int j;
num_events = MAX_EVENTS;
while (MAX_EVENTS == num_events)
@@ -3172,7 +3222,34 @@ run_epoll_for_upgrade (struct MHD_Daemon *daemon)
for (i=0;i<(unsigned int) num_events;i++)
{
struct UpgradeEpollHandle *ueh = events[i].data.ptr;
struct MHD_UpgradeResponseHandle *urh = ueh->urh;
struct MHD_UpgradeResponseHandle *urh;
if (NULL == ueh)
continue; /* was killed, see below */
urh = ueh->urh;
/* In case we get two events for the same upgrade handle,
squash them together (otherwise the first one may
cause us to free the 'urh', and the second one then
causes a use-after-free). */
for (j=i+1;j< (unsigned int) num_events;j++)
{
struct UpgradeEpollHandle *uehj = events[j].data.ptr;
struct MHD_UpgradeResponseHandle *urhj;
if (NULL == uehj)
continue; /* was killed, see below */
urhj = uehj->urh;
if (urh == urhj) /* yep, indeed the same! */
{
if (0 != (events[j].events & EPOLLIN))
uehj->celi |= MHD_EPOLL_STATE_READ_READY;
if (0 != (events[j].events & EPOLLOUT))
uehj->celi |= MHD_EPOLL_STATE_WRITE_READY;
}
events[j].data.ptr = NULL; /* kill this one */
}
/* Update our state based on what is ready according to epoll() */
if (0 != (events[i].events & EPOLLIN))
+1 -1
View File
@@ -859,7 +859,6 @@ struct MHD_Connection
*/
TransmitCallback send_cls;
#if HTTPS_SUPPORT
/**
* If this connection was upgraded and if we are using
* #MHD_USE_THREAD_PER_CONNECTION, this points to the
@@ -869,6 +868,7 @@ struct MHD_Connection
*/
struct MHD_UpgradeResponseHandle *urh;
#if HTTPS_SUPPORT
/**
* If this connection was upgraded and if we are using
* #MHD_USE_THREAD_PER_CONNECTION without encryption,
+12 -8
View File
@@ -627,19 +627,16 @@ MHD_upgrade_action (struct MHD_UpgradeResponseHandle *urh,
/* Application is done with this connection, tear it down! */
if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION) )
{
if (0 == (daemon->options & MHD_USE_SSL) )
{
/* just need to signal the thread that we are done */
MHD_semaphore_up (connection->upgrade_sem);
}
#if HTTPS_SUPPORT
else
if (0 != (daemon->options & MHD_USE_SSL) )
{
/* signal thread by shutdown() of 'app' socket */
/* signal thread that app is done by shutdown() of 'app' socket */
shutdown (urh->app.socket,
SHUT_RDWR);
}
#endif
/* need to signal the thread that we are done */
MHD_semaphore_up (connection->upgrade_sem);
return MHD_YES;
}
#if HTTPS_SUPPORT
@@ -755,6 +752,12 @@ MHD_response_execute_upgrade_ (struct MHD_Response *response,
urh->mhd.celi = MHD_EPOLL_STATE_UNREADY;
pool = connection->pool;
avail = MHD_pool_get_free (pool);
if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION) )
{
/* Need to give the thread something to block on... */
connection->upgrade_sem = MHD_semaphore_create (0);
}
if (avail < 8)
{
/* connection's pool is totally at the limit,
@@ -871,7 +874,8 @@ MHD_response_execute_upgrade_ (struct MHD_Response *response,
{
/* Need to give the thread something to block on... */
connection->upgrade_sem = MHD_semaphore_create (0);
if (NULL == connection->upgrade_sem)
connection->urh = urh;
if (NULL == connection->upgrade_sem)
{
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
+17 -5
View File
@@ -400,7 +400,9 @@ test_upgrade_internal (int flags,
MHD_socket sock;
pid_t pid;
d = MHD_start_daemon (flags | MHD_USE_DEBUG | MHD_USE_SUSPEND_RESUME | MHD_USE_TLS,
if (0 == (flags & MHD_USE_THREAD_PER_CONNECTION))
flags |= MHD_USE_SUSPEND_RESUME;
d = MHD_start_daemon (flags | MHD_USE_DEBUG | MHD_USE_TLS,
1080,
NULL, NULL,
&ahc_upgrade, NULL,
@@ -442,25 +444,35 @@ main (int argc,
if (0 != system ("openssl version 1> /dev/null"))
return 77; /* openssl not available, can't run the test */
/* Test thread-per-connection */
error_count += test_upgrade_internal (MHD_USE_THREAD_PER_CONNECTION,
0);
error_count += test_upgrade_internal (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL,
0);
/* Test different event loops, with and without thread pool */
error_count += test_upgrade_internal (MHD_USE_SELECT_INTERNALLY,
1);
0);
error_count += test_upgrade_internal (MHD_USE_SELECT_INTERNALLY,
2);
#ifdef HAVE_POLL
error_count += test_upgrade_internal (MHD_USE_POLL_INTERNALLY,
1);
0);
error_count += test_upgrade_internal (MHD_USE_POLL_INTERNALLY,
2);
#endif
#ifdef EPOLL_SUPPORT
error_count += test_upgrade_internal (MHD_USE_EPOLL_INTERNALLY |
MHD_USE_TLS_EPOLL_UPGRADE,
1);
0);
error_count += test_upgrade_internal (MHD_USE_EPOLL_INTERNALLY |
MHD_USE_TLS_EPOLL_UPGRADE,
2);
#endif
if (error_count != 0)
/* report result */
if (0 != error_count)
fprintf (stderr,
"Error (code: %u)\n",
error_count);