Skip to content

Commit 6d4221b

Browse files
Jim SchuttSage Weil
authored andcommitted
libceph: avoid truncation due to racing banners
Because the Ceph client messenger uses a non-blocking connect, it is possible for the sending of the client banner to race with the arrival of the banner sent by the peer. When ceph_sock_state_change() notices the connect has completed, it schedules work to process the socket via con_work(). During this time the peer is writing its banner, and arrival of the peer banner races with con_work(). If con_work() calls try_read() before the peer banner arrives, there is nothing for it to do, after which con_work() calls try_write() to send the client's banner. In this case Ceph's protocol negotiation can complete succesfully. The server-side messenger immediately sends its banner and addresses after accepting a connect request, *before* actually attempting to read or verify the banner from the client. As a result, it is possible for the banner from the server to arrive before con_work() calls try_read(). If that happens, try_read() will read the banner and prepare protocol negotiation info via prepare_write_connect(). prepare_write_connect() calls con_out_kvec_reset(), which discards the as-yet-unsent client banner. Next, con_work() calls try_write(), which sends the protocol negotiation info rather than the banner that the peer is expecting. The result is that the peer sees an invalid banner, and the client reports "negotiation failed". Fix this by moving con_out_kvec_reset() out of prepare_write_connect() to its callers at all locations except the one where the banner might still need to be sent. [[email protected]: added note about server-side behavior] Signed-off-by: Jim Schutt <[email protected]> Reviewed-by: Alex Elder <[email protected]>
1 parent 6c5e50f commit 6d4221b

File tree

1 file changed

+9
-2
lines changed

1 file changed

+9
-2
lines changed

net/ceph/messenger.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -915,7 +915,6 @@ static int prepare_write_connect(struct ceph_connection *con)
915915
con->out_connect.authorizer_len = auth ?
916916
cpu_to_le32(auth->authorizer_buf_len) : 0;
917917

918-
con_out_kvec_reset(con);
919918
con_out_kvec_add(con, sizeof (con->out_connect),
920919
&con->out_connect);
921920
if (auth && auth->authorizer_buf_len)
@@ -1557,6 +1556,7 @@ static int process_connect(struct ceph_connection *con)
15571556
return -1;
15581557
}
15591558
con->auth_retry = 1;
1559+
con_out_kvec_reset(con);
15601560
ret = prepare_write_connect(con);
15611561
if (ret < 0)
15621562
return ret;
@@ -1577,6 +1577,7 @@ static int process_connect(struct ceph_connection *con)
15771577
ENTITY_NAME(con->peer_name),
15781578
ceph_pr_addr(&con->peer_addr.in_addr));
15791579
reset_connection(con);
1580+
con_out_kvec_reset(con);
15801581
ret = prepare_write_connect(con);
15811582
if (ret < 0)
15821583
return ret;
@@ -1601,6 +1602,7 @@ static int process_connect(struct ceph_connection *con)
16011602
le32_to_cpu(con->out_connect.connect_seq),
16021603
le32_to_cpu(con->in_reply.connect_seq));
16031604
con->connect_seq = le32_to_cpu(con->in_reply.connect_seq);
1605+
con_out_kvec_reset(con);
16041606
ret = prepare_write_connect(con);
16051607
if (ret < 0)
16061608
return ret;
@@ -1617,6 +1619,7 @@ static int process_connect(struct ceph_connection *con)
16171619
le32_to_cpu(con->in_reply.global_seq));
16181620
get_global_seq(con->msgr,
16191621
le32_to_cpu(con->in_reply.global_seq));
1622+
con_out_kvec_reset(con);
16201623
ret = prepare_write_connect(con);
16211624
if (ret < 0)
16221625
return ret;
@@ -2135,7 +2138,11 @@ static int try_read(struct ceph_connection *con)
21352138
BUG_ON(con->state != CON_STATE_CONNECTING);
21362139
con->state = CON_STATE_NEGOTIATING;
21372140

2138-
/* Banner is good, exchange connection info */
2141+
/*
2142+
* Received banner is good, exchange connection info.
2143+
* Do not reset out_kvec, as sending our banner raced
2144+
* with receiving peer banner after connect completed.
2145+
*/
21392146
ret = prepare_write_connect(con);
21402147
if (ret < 0)
21412148
goto out;

0 commit comments

Comments
 (0)