Skip to content

Commit daef505

Browse files
ebblakerwmjones
authored andcommitted
server: Give client EOF when we are done writing
If we detect a fatal error but do not close the socket back to the client, then we can create deadlock where we are stuck waiting to read NBD_CMD_DISC from the client but where the client is stuck waiting to read our reply. Try harder to explicitly inform the client any time that we have detected when our connection has degraded enough to warrant that we won't do any more writing, even if we still hang on to the socket for a bit longer for further reads. State-wise, all clients will exit through one of two paths: normal -> conn->close(SHUT_WR) -> conn->close(SHUT_RDWR) normal -> conn->close(SHUT_RDWR) That is, SHUT_WR is an optional state which exists to help avoid deadlocks, but which does not cause an fd leak of sockin because we will always reach SHUT_RDWR. Message-Id: <[email protected]> (cherry picked from commit bf8653b)
1 parent 661b21e commit daef505

File tree

3 files changed

+34
-17
lines changed

3 files changed

+34
-17
lines changed

server/connections.c

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ static int raw_send_socket (const void *buf, size_t len, int flags);
6262
#ifndef WIN32
6363
static int raw_send_other (const void *buf, size_t len, int flags);
6464
#endif
65-
static void raw_close (void);
65+
static void raw_close (int how);
6666

6767
conn_status
6868
connection_get_status (void)
@@ -97,6 +97,8 @@ connection_set_status (conn_status value)
9797
if (write (conn->status_pipe[1], &c, 1) != 1 && errno != EAGAIN)
9898
debug ("failed to notify pipe-to-self: %m");
9999
}
100+
if (conn->status >= STATUS_CLIENT_DONE && value < STATUS_CLIENT_DONE)
101+
conn->close (SHUT_WR);
100102
conn->status = value;
101103
}
102104
if (conn->nworkers &&
@@ -348,7 +350,7 @@ free_connection (struct connection *conn)
348350
if (!conn)
349351
return;
350352

351-
conn->close ();
353+
conn->close (SHUT_RDWR);
352354

353355
/* Don't call the plugin again if quit has been set because the main
354356
* thread will be in the process of unloading it. The plugin.unload
@@ -397,6 +399,7 @@ raw_send_socket (const void *vbuf, size_t len, int flags)
397399
ssize_t r;
398400
int f = 0;
399401

402+
assert (sock >= 0);
400403
#ifdef MSG_MORE
401404
if (flags & SEND_MORE)
402405
f |= MSG_MORE;
@@ -427,6 +430,7 @@ raw_send_other (const void *vbuf, size_t len, int flags)
427430
const char *buf = vbuf;
428431
ssize_t r;
429432

433+
assert (sock >= 0);
430434
while (len > 0) {
431435
r = write (sock, buf, len);
432436
if (r == -1) {
@@ -489,12 +493,21 @@ raw_recv (void *vbuf, size_t len)
489493
* close, so this function ignores errors.
490494
*/
491495
static void
492-
raw_close (void)
496+
raw_close (int how)
493497
{
494498
GET_CONN;
495499

496-
if (conn->sockin >= 0)
497-
closesocket (conn->sockin);
498-
if (conn->sockout >= 0 && conn->sockin != conn->sockout)
499-
closesocket (conn->sockout);
500+
if (conn->sockout >= 0 && how == SHUT_WR) {
501+
if (conn->sockin == conn->sockout)
502+
shutdown (conn->sockout, how);
503+
else
504+
closesocket (conn->sockout);
505+
conn->sockout = -1;
506+
}
507+
else {
508+
if (conn->sockin >= 0)
509+
closesocket (conn->sockin);
510+
if (conn->sockout >= 0 && conn->sockin != conn->sockout)
511+
closesocket (conn->sockout);
512+
}
500513
}

server/crypto.c

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -412,25 +412,29 @@ crypto_send (const void *vbuf, size_t len, int flags)
412412
* close, so this function ignores errors.
413413
*/
414414
static void
415-
crypto_close (void)
415+
crypto_close (int how)
416416
{
417417
GET_CONN;
418418
gnutls_session_t session = conn->crypto_session;
419419
int sockin, sockout;
420420

421421
assert (session != NULL);
422422

423-
gnutls_transport_get_int2 (session, &sockin, &sockout);
423+
if (how == SHUT_WR)
424+
gnutls_bye (session, GNUTLS_SHUT_WR);
425+
else {
426+
gnutls_transport_get_int2 (session, &sockin, &sockout);
424427

425-
gnutls_bye (session, GNUTLS_SHUT_RDWR);
428+
gnutls_bye (session, GNUTLS_SHUT_RDWR);
426429

427-
if (sockin >= 0)
428-
closesocket (sockin);
429-
if (sockout >= 0 && sockin != sockout)
430-
closesocket (sockout);
430+
if (sockin >= 0)
431+
closesocket (sockin);
432+
if (sockout >= 0 && sockin != sockout)
433+
closesocket (sockout);
431434

432-
gnutls_deinit (session);
433-
conn->crypto_session = NULL;
435+
gnutls_deinit (session);
436+
conn->crypto_session = NULL;
437+
}
434438
}
435439

436440
#ifdef WIN32

server/internal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ typedef int (*connection_recv_function) (void *buf, size_t len)
194194
typedef int (*connection_send_function) (const void *buf, size_t len,
195195
int flags)
196196
__attribute__((__nonnull__ (1)));
197-
typedef void (*connection_close_function) (void);
197+
typedef void (*connection_close_function) (int how);
198198

199199
/* struct context stores data per connection and backend. Primarily
200200
* this is the filter or plugin handle, but other state is also stored

0 commit comments

Comments
 (0)