Skip to content

Commit 28c0194

Browse files
committed
io_tls: Fix file descriptor leak on cleanup.
Commit 0ea87e3 introduced a regression which leaked file descriptors for TLS sessions by adding a call to the MARK_DEAD macro. This macro modifies the sfd variable in place, which was not a problem in the TLS I/O thread, but would result in sfd being NULL post-traversal in ssl_unregister_fd, causing the TLS session to not be cleaned up. So: * Use a local variable for the traversal in MARK_DEAD to avoid mutating a variable outside the macro. * Log a warning if unregistering fails and we return -1. We were returning -1 previously, but there was no warning or error logged when this happened. * Simplify some cleanup code by using PIPE_CLOSE where possible.
1 parent d3cbfd4 commit 28c0194

File tree

1 file changed

+14
-14
lines changed

1 file changed

+14
-14
lines changed

io/io_tls.c

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,7 @@ static int ssl_register_fd(SSL *ssl, int fd, int *rfd, int *wfd, int client)
214214
return -1;
215215
} else if (pipe(sfd->writepipe)) {
216216
bbs_error("pipe failed: %s\n", strerror(errno));
217-
close(sfd->readpipe[0]);
218-
close(sfd->readpipe[1]);
217+
PIPE_CLOSE(sfd->readpipe);
219218
free(sfd);
220219
RWLIST_UNLOCK(&sslfds);
221220
return -1;
@@ -235,10 +234,8 @@ static int ssl_register_fd(SSL *ssl, int fd, int *rfd, int *wfd, int client)
235234

236235
static void ssl_fd_free(struct ssl_fd *sfd)
237236
{
238-
close(sfd->readpipe[1]);
239-
close(sfd->readpipe[0]);
240-
close(sfd->writepipe[1]);
241-
close(sfd->writepipe[0]);
237+
PIPE_CLOSE(sfd->readpipe);
238+
PIPE_CLOSE(sfd->writepipe);
242239
free(sfd);
243240
}
244241

@@ -247,17 +244,19 @@ static int defer_rebuild = 0; /* Whether to defer the next rebuild of the sessio
247244

248245
/*! \note It's possible it might not be in the list, because the owner thread has already called ssl_unregister_fd,
249246
* in which case there's no need to do anything. */
250-
#define MARK_DEAD(s) \
251-
RWLIST_TRAVERSE(&sslfds, sfd, entry) { \
252-
if (sfd->ssl == s) { \
253-
if (sfd->dead) { /* Shouldn't happen since we rebuild traversal list after MARK_DEAD */ \
254-
bbs_warning("SSL connection %p already marked as dead?\n", sfd->ssl); \
247+
#define MARK_DEAD(s) { \
248+
struct ssl_fd *sfd_traverse = sfd; /* Don't modify sfd in place or it would be NULL after traversal */ \
249+
RWLIST_TRAVERSE(&sslfds, sfd_traverse, entry) { \
250+
if (sfd_traverse->ssl == s) { \
251+
if (sfd_traverse->dead) { /* Shouldn't happen since we rebuild traversal list after MARK_DEAD */ \
252+
bbs_warning("SSL connection %p already marked as dead?\n", sfd_traverse->ssl); \
255253
} \
256-
sfd->dead = 1; \
257-
bbs_debug(5, "SSL connection %p now marked as dead\n", sfd->ssl); \
254+
sfd_traverse->dead = 1; \
255+
bbs_debug(5, "SSL connection %p now marked as dead\n", sfd_traverse->ssl); \
258256
break; \
259257
} \
260258
} \
259+
}
261260

262261
static int ssl_unregister_fd(SSL *ssl)
263262
{
@@ -278,6 +277,7 @@ static int ssl_unregister_fd(SSL *ssl)
278277
bbs_alertpipe_write(ssl_alert_pipe); /* Notify I/O thread that we removed a fd, although it'll probably detect this anyways. */
279278
return 0;
280279
}
280+
bbs_warning("Couldn't unregister SSL session for %p?\n", ssl);
281281
return -1;
282282
}
283283

@@ -1244,7 +1244,7 @@ static int tlsreload(int fd)
12441244
}
12451245

12461246
ssl_is_available = 0; /* Ensure any new connections are rejected until we're done reloading. */
1247-
RWLIST_UNLOCK(&sslfds); /* tls_cleanup will lock the list again, so unlock it for now. */
1247+
RWLIST_UNLOCK(&sslfds); /* tls_cleanup will lock the list again, so unlock it for now. XXX It's a different lock though, so could unlock after? */
12481248

12491249
tls_cleanup();
12501250

0 commit comments

Comments
 (0)