Benno Evers created MESOS-9867:
----------------------------------

             Summary: Libevent fd cleanup failure may cause hangs in subsequent 
tests
                 Key: MESOS-9867
                 URL: https://issues.apache.org/jira/browse/MESOS-9867
             Project: Mesos
          Issue Type: Bug
            Reporter: Benno Evers


A listening LibeventSSLSocket will check cryptographic certificate validity 
during the OpenSSL handshake and afterwards call the `openssl::verify()` 
function to perform hostname validation and other checks on the client 
certificate. If these checks fail, the bufferevent is deleted and the 
connection closed:
{noformat}
// libevent_ssl_socket.cpp, accept_SSL_callback()
          if (verify.isError()) {
            VLOG(1) << "Failed accept, verification error: " << verify.error();
            request->promise.fail(verify.error());
            SSL_free(ssl);
            bufferevent_free(bev);
            // TODO(jmlvanre): Clean up for readability. Consider RAII
            // or constructing the impl earlier.
            CHECK(request->socket >= 0);
            Try<Nothing> close = os::close(request->socket);
            if (close.isError()) {
              LOG(FATAL)
                << "Failed to close socket " << stringify(request->socket)
                << ": " << close.error();
            }
            delete request;
            return;
          }
{noformat}

However, when we close the socket fd in the above code, libevent had already 
registered that file descriptor with epoll() to watch for read and write events 
on that socket. Since the socket is closed, attempts to remove the 
corresponding fd from the epoll() structs will fail: (See also: 
https://idea.popcount.org/2017-03-20-epoll-is-fundamentally-broken-22/)
{noformat}
[warn] Epoll MOD(4) on fd 9 failed.  Old events were 6; read change was 2 
(del); write change was 0 (none): Bad file descriptor
[warn] Epoll MOD(1) on fd 9 failed.  Old events were 6; read change was 0 
(none); write change was 2 (del): Bad file descriptor
{noformat}

However, that in itself is harmless since the kernel will remove the kernel 
object that was associated with fd 9 from the data structure associated with 
that epoll instance in the kernel. So while we get an error attempting to 
remove fd 9, there is actually nothing left to remove. However, in a case of 
epoll failure, libprocess does not adjust the number of readers and writers on 
that file descriptor:
{noformat}
        // evmap.c, evmap_io_del()
        [...]
        if (evsel->del(base, ev->ev_fd, old, res, extra) == -1)
               return (-1);

        [...]
        ctx->nread = nread;
        ctx->nwrite = nwrite;
{noformat}

In the above, ctx is part of an array collecting information for each file 
descriptor. That still wouldn't be so bad, however libevent also only adds file 
descriptors to `epoll()` struct the *first* time we attempt to create a read or 
write event on that file descriptor:

{noformat}
        // evmap.c, evmap_io_del()
        if (ev->ev_events & EV_READ) {
                if (++nread == 1)
                        res |= EV_READ;
        }
        if (ev->ev_events & EV_WRITE) {
                if (++nwrite == 1)
                        res |= EV_WRITE;
        }

        [...]

        if (res) {
                [...]
                if (evsel->add(base, ev->ev_fd,
                        old, (ev->ev_events & EV_ET) | res, extra) == -1)
                        return (-1);
                [...]
        }
{noformat}

So when the same file descriptor is attempted to be used again by libevent for 
epoll() polling, the process will hang because reads or writes to that file 
descriptor are never noticed.

This can be reproduced for example by running a test where the 
`verify()`-callback fails on the server side twice in a row: (note, the 
LIBPROCESS_IP below is set in order to induce a test failure, result may vary 
on your local network and ssl configuration)
{noformat}
LIBPROCESS_IP=127.0.1.1 ./libprocess-tests --gtest_filter="*VerifyCertificate*" 
--gtest_repeat=2
{noformat}

There is a chance that the issue described here is the same as the ominous 
"issues" described in MESOS-3008, 



--
This message was sent by Atlassian JIRA
(v7.6.3#76005)

Reply via email to