Skip to content

Commit

Permalink
net/tls: fix race condition causing kernel panic
Browse files Browse the repository at this point in the history
tls_sw_recvmsg() and tls_decrypt_done() can be run concurrently.
// tls_sw_recvmsg()
	if (atomic_read(&ctx->decrypt_pending))
		crypto_wait_req(-EINPROGRESS, &ctx->async_wait);
	else
		reinit_completion(&ctx->async_wait.completion);

//tls_decrypt_done()
  	pending = atomic_dec_return(&ctx->decrypt_pending);

  	if (!pending && READ_ONCE(ctx->async_notify))
  		complete(&ctx->async_wait.completion);

Consider the scenario tls_decrypt_done() is about to run complete()

	if (!pending && READ_ONCE(ctx->async_notify))

and tls_sw_recvmsg() reads decrypt_pending == 0, does reinit_completion(),
then tls_decrypt_done() runs complete(). This sequence of execution
results in wrong completion. Consequently, for next decrypt request,
it will not wait for completion, eventually on connection close, crypto
resources freed, there is no way to handle pending decrypt response.

This race condition can be avoided by having atomic_read() mutually
exclusive with atomic_dec_return(),complete().Intoduced spin lock to
ensure the mutual exclution.

Addressed similar problem in tx direction.

v1->v2:
- More readable commit message.
- Corrected the lock to fix new race scenario.
- Removed barrier which is not needed now.

Fixes: a42055e ("net/tls: Add support for async encryption of records for performance")
Signed-off-by: Vinay Kumar Yadav <vinay.yadav@chelsio.com>
Reviewed-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
vinaychelsio authored and davem330 committed May 26, 2020
1 parent 98790bb commit 0cada33
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 6 deletions.
4 changes: 4 additions & 0 deletions include/net/tls.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ struct tls_sw_context_tx {
struct tls_rec *open_rec;
struct list_head tx_list;
atomic_t encrypt_pending;
/* protect crypto_wait with encrypt_pending */
spinlock_t encrypt_compl_lock;
int async_notify;
u8 async_capable:1;

Expand All @@ -155,6 +157,8 @@ struct tls_sw_context_rx {
u8 async_capable:1;
u8 decrypted:1;
atomic_t decrypt_pending;
/* protect crypto_wait with decrypt_pending*/
spinlock_t decrypt_compl_lock;
bool async_notify;
};

Expand Down
33 changes: 27 additions & 6 deletions net/tls/tls_sw.c
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,12 @@ static void tls_decrypt_done(struct crypto_async_request *req, int err)

kfree(aead_req);

spin_lock_bh(&ctx->decrypt_compl_lock);
pending = atomic_dec_return(&ctx->decrypt_pending);

if (!pending && READ_ONCE(ctx->async_notify))
if (!pending && ctx->async_notify)
complete(&ctx->async_wait.completion);
spin_unlock_bh(&ctx->decrypt_compl_lock);
}

static int tls_do_decryption(struct sock *sk,
Expand Down Expand Up @@ -467,10 +469,12 @@ static void tls_encrypt_done(struct crypto_async_request *req, int err)
ready = true;
}

spin_lock_bh(&ctx->encrypt_compl_lock);
pending = atomic_dec_return(&ctx->encrypt_pending);

if (!pending && READ_ONCE(ctx->async_notify))
if (!pending && ctx->async_notify)
complete(&ctx->async_wait.completion);
spin_unlock_bh(&ctx->encrypt_compl_lock);

if (!ready)
return;
Expand Down Expand Up @@ -929,6 +933,7 @@ int tls_sw_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
int num_zc = 0;
int orig_size;
int ret = 0;
int pending;

if (msg->msg_flags & ~(MSG_MORE | MSG_DONTWAIT | MSG_NOSIGNAL))
return -EOPNOTSUPP;
Expand Down Expand Up @@ -1095,13 +1100,19 @@ int tls_sw_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
goto send_end;
} else if (num_zc) {
/* Wait for pending encryptions to get completed */
smp_store_mb(ctx->async_notify, true);
spin_lock_bh(&ctx->encrypt_compl_lock);
ctx->async_notify = true;

if (atomic_read(&ctx->encrypt_pending))
pending = atomic_read(&ctx->encrypt_pending);
spin_unlock_bh(&ctx->encrypt_compl_lock);
if (pending)
crypto_wait_req(-EINPROGRESS, &ctx->async_wait);
else
reinit_completion(&ctx->async_wait.completion);

/* There can be no concurrent accesses, since we have no
* pending encrypt operations
*/
WRITE_ONCE(ctx->async_notify, false);

if (ctx->async_wait.err) {
Expand Down Expand Up @@ -1732,6 +1743,7 @@ int tls_sw_recvmsg(struct sock *sk,
bool is_kvec = iov_iter_is_kvec(&msg->msg_iter);
bool is_peek = flags & MSG_PEEK;
int num_async = 0;
int pending;

flags |= nonblock;

Expand Down Expand Up @@ -1894,8 +1906,11 @@ int tls_sw_recvmsg(struct sock *sk,
recv_end:
if (num_async) {
/* Wait for all previously submitted records to be decrypted */
smp_store_mb(ctx->async_notify, true);
if (atomic_read(&ctx->decrypt_pending)) {
spin_lock_bh(&ctx->decrypt_compl_lock);
ctx->async_notify = true;
pending = atomic_read(&ctx->decrypt_pending);
spin_unlock_bh(&ctx->decrypt_compl_lock);
if (pending) {
err = crypto_wait_req(-EINPROGRESS, &ctx->async_wait);
if (err) {
/* one of async decrypt failed */
Expand All @@ -1907,6 +1922,10 @@ int tls_sw_recvmsg(struct sock *sk,
} else {
reinit_completion(&ctx->async_wait.completion);
}

/* There can be no concurrent accesses, since we have no
* pending decrypt operations
*/
WRITE_ONCE(ctx->async_notify, false);

/* Drain records from the rx_list & copy if required */
Expand Down Expand Up @@ -2293,6 +2312,7 @@ int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx)

if (tx) {
crypto_init_wait(&sw_ctx_tx->async_wait);
spin_lock_init(&sw_ctx_tx->encrypt_compl_lock);
crypto_info = &ctx->crypto_send.info;
cctx = &ctx->tx;
aead = &sw_ctx_tx->aead_send;
Expand All @@ -2301,6 +2321,7 @@ int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx)
sw_ctx_tx->tx_work.sk = sk;
} else {
crypto_init_wait(&sw_ctx_rx->async_wait);
spin_lock_init(&sw_ctx_rx->decrypt_compl_lock);
crypto_info = &ctx->crypto_recv.info;
cctx = &ctx->rx;
skb_queue_head_init(&sw_ctx_rx->rx_list);
Expand Down

0 comments on commit 0cada33

Please sign in to comment.