Commit 3d80c653 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files


David Howells says:

====================
RxRPC fixes

Here are a number of fixes for AF_RXRPC:

 (1) Fix a potential use after free in rxrpc_put_local() where it was
     accessing the object just put to get tracing information.

 (2) Fix insufficient notifications being generated by the function that
     queues data packets on a call.  This occasionally causes recvmsg() to
     stall indefinitely.

 (3) Fix a number of packet-transmitting work functions to hold an active
     count on the local endpoint so that the UDP socket doesn't get
     destroyed whilst they're calling kernel_sendmsg() on it.

 (4) Fix a NULL pointer deref that stemmed from a call's connection pointer
     being cleared when the call was disconnected.

Changes:

 v2: Removed a couple of BUG() statements that got added.
====================

Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 83d0585f 5273a191
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -194,6 +194,7 @@ static int rxrpc_bind(struct socket *sock, struct sockaddr *saddr, int len)
service_in_use:
	write_unlock(&local->services_lock);
	rxrpc_unuse_local(local);
	rxrpc_put_local(local);
	ret = -EADDRINUSE;
error_unlock:
	release_sock(&rx->sk);
@@ -899,6 +900,7 @@ static int rxrpc_release_sock(struct sock *sk)
	rxrpc_purge_queue(&sk->sk_receive_queue);

	rxrpc_unuse_local(rx->local);
	rxrpc_put_local(rx->local);
	rx->local = NULL;
	key_put(rx->key);
	rx->key = NULL;
+11 −0
Original line number Diff line number Diff line
@@ -490,6 +490,7 @@ enum rxrpc_call_flag {
	RXRPC_CALL_RX_HEARD,		/* The peer responded at least once to this call */
	RXRPC_CALL_RX_UNDERRUN,		/* Got data underrun */
	RXRPC_CALL_IS_INTR,		/* The call is interruptible */
	RXRPC_CALL_DISCONNECTED,	/* The call has been disconnected */
};

/*
@@ -1021,6 +1022,16 @@ void rxrpc_unuse_local(struct rxrpc_local *);
void rxrpc_queue_local(struct rxrpc_local *);
void rxrpc_destroy_all_locals(struct rxrpc_net *);

static inline bool __rxrpc_unuse_local(struct rxrpc_local *local)
{
	return atomic_dec_return(&local->active_users) == 0;
}

static inline bool __rxrpc_use_local(struct rxrpc_local *local)
{
	return atomic_fetch_add_unless(&local->active_users, 1, 0) != 0;
}

/*
 * misc.c
 */
+2 −2
Original line number Diff line number Diff line
@@ -493,7 +493,7 @@ void rxrpc_release_call(struct rxrpc_sock *rx, struct rxrpc_call *call)

	_debug("RELEASE CALL %p (%d CONN %p)", call, call->debug_id, conn);

	if (conn)
	if (conn && !test_bit(RXRPC_CALL_DISCONNECTED, &call->flags))
		rxrpc_disconnect_call(call);
	if (call->security)
		call->security->free_call_crypto(call);
@@ -569,6 +569,7 @@ static void rxrpc_rcu_destroy_call(struct rcu_head *rcu)
	struct rxrpc_call *call = container_of(rcu, struct rxrpc_call, rcu);
	struct rxrpc_net *rxnet = call->rxnet;

	rxrpc_put_connection(call->conn);
	rxrpc_put_peer(call->peer);
	kfree(call->rxtx_buffer);
	kfree(call->rxtx_annotations);
@@ -590,7 +591,6 @@ void rxrpc_cleanup_call(struct rxrpc_call *call)

	ASSERTCMP(call->state, ==, RXRPC_CALL_COMPLETE);
	ASSERT(test_bit(RXRPC_CALL_RELEASED, &call->flags));
	ASSERTCMP(call->conn, ==, NULL);

	rxrpc_cleanup_ring(call);
	rxrpc_free_skb(call->tx_pending, rxrpc_skb_cleaned);
+1 −2
Original line number Diff line number Diff line
@@ -785,6 +785,7 @@ void rxrpc_disconnect_client_call(struct rxrpc_call *call)
	u32 cid;

	spin_lock(&conn->channel_lock);
	set_bit(RXRPC_CALL_DISCONNECTED, &call->flags);

	cid = call->cid;
	if (cid) {
@@ -792,7 +793,6 @@ void rxrpc_disconnect_client_call(struct rxrpc_call *call)
		chan = &conn->channels[channel];
	}
	trace_rxrpc_client(conn, channel, rxrpc_client_chan_disconnect);
	call->conn = NULL;

	/* Calls that have never actually been assigned a channel can simply be
	 * discarded.  If the conn didn't get used either, it will follow
@@ -908,7 +908,6 @@ out:
	spin_unlock(&rxnet->client_conn_cache_lock);
out_2:
	spin_unlock(&conn->channel_lock);
	rxrpc_put_connection(conn);
	_leave("");
	return;

+20 −10
Original line number Diff line number Diff line
@@ -438,16 +438,12 @@ again:
/*
 * connection-level event processor
 */
void rxrpc_process_connection(struct work_struct *work)
static void rxrpc_do_process_connection(struct rxrpc_connection *conn)
{
	struct rxrpc_connection *conn =
		container_of(work, struct rxrpc_connection, processor);
	struct sk_buff *skb;
	u32 abort_code = RX_PROTOCOL_ERROR;
	int ret;

	rxrpc_see_connection(conn);

	if (test_and_clear_bit(RXRPC_CONN_EV_CHALLENGE, &conn->events))
		rxrpc_secure_connection(conn);

@@ -475,18 +471,32 @@ void rxrpc_process_connection(struct work_struct *work)
		}
	}

out:
	rxrpc_put_connection(conn);
	_leave("");
	return;

requeue_and_leave:
	skb_queue_head(&conn->rx_queue, skb);
	goto out;
	return;

protocol_error:
	if (rxrpc_abort_connection(conn, ret, abort_code) < 0)
		goto requeue_and_leave;
	rxrpc_free_skb(skb, rxrpc_skb_freed);
	goto out;
	return;
}

void rxrpc_process_connection(struct work_struct *work)
{
	struct rxrpc_connection *conn =
		container_of(work, struct rxrpc_connection, processor);

	rxrpc_see_connection(conn);

	if (__rxrpc_use_local(conn->params.local)) {
		rxrpc_do_process_connection(conn);
		rxrpc_unuse_local(conn->params.local);
	}

	rxrpc_put_connection(conn);
	_leave("");
	return;
}
Loading