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

Merge tag 'rxrpc-fixes-20200203' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs

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
...@@ -194,6 +194,7 @@ static int rxrpc_bind(struct socket *sock, struct sockaddr *saddr, int len) ...@@ -194,6 +194,7 @@ static int rxrpc_bind(struct socket *sock, struct sockaddr *saddr, int len)
service_in_use: service_in_use:
write_unlock(&local->services_lock); write_unlock(&local->services_lock);
rxrpc_unuse_local(local); rxrpc_unuse_local(local);
rxrpc_put_local(local);
ret = -EADDRINUSE; ret = -EADDRINUSE;
error_unlock: error_unlock:
release_sock(&rx->sk); release_sock(&rx->sk);
...@@ -899,6 +900,7 @@ static int rxrpc_release_sock(struct sock *sk) ...@@ -899,6 +900,7 @@ static int rxrpc_release_sock(struct sock *sk)
rxrpc_purge_queue(&sk->sk_receive_queue); rxrpc_purge_queue(&sk->sk_receive_queue);
rxrpc_unuse_local(rx->local); rxrpc_unuse_local(rx->local);
rxrpc_put_local(rx->local);
rx->local = NULL; rx->local = NULL;
key_put(rx->key); key_put(rx->key);
rx->key = NULL; rx->key = NULL;
......
...@@ -490,6 +490,7 @@ enum rxrpc_call_flag { ...@@ -490,6 +490,7 @@ enum rxrpc_call_flag {
RXRPC_CALL_RX_HEARD, /* The peer responded at least once to this call */ RXRPC_CALL_RX_HEARD, /* The peer responded at least once to this call */
RXRPC_CALL_RX_UNDERRUN, /* Got data underrun */ RXRPC_CALL_RX_UNDERRUN, /* Got data underrun */
RXRPC_CALL_IS_INTR, /* The call is interruptible */ 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 *); ...@@ -1021,6 +1022,16 @@ void rxrpc_unuse_local(struct rxrpc_local *);
void rxrpc_queue_local(struct rxrpc_local *); void rxrpc_queue_local(struct rxrpc_local *);
void rxrpc_destroy_all_locals(struct rxrpc_net *); 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 * misc.c
*/ */
......
...@@ -493,7 +493,7 @@ void rxrpc_release_call(struct rxrpc_sock *rx, struct rxrpc_call *call) ...@@ -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); _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); rxrpc_disconnect_call(call);
if (call->security) if (call->security)
call->security->free_call_crypto(call); call->security->free_call_crypto(call);
...@@ -569,6 +569,7 @@ static void rxrpc_rcu_destroy_call(struct rcu_head *rcu) ...@@ -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_call *call = container_of(rcu, struct rxrpc_call, rcu);
struct rxrpc_net *rxnet = call->rxnet; struct rxrpc_net *rxnet = call->rxnet;
rxrpc_put_connection(call->conn);
rxrpc_put_peer(call->peer); rxrpc_put_peer(call->peer);
kfree(call->rxtx_buffer); kfree(call->rxtx_buffer);
kfree(call->rxtx_annotations); kfree(call->rxtx_annotations);
...@@ -590,7 +591,6 @@ void rxrpc_cleanup_call(struct rxrpc_call *call) ...@@ -590,7 +591,6 @@ void rxrpc_cleanup_call(struct rxrpc_call *call)
ASSERTCMP(call->state, ==, RXRPC_CALL_COMPLETE); ASSERTCMP(call->state, ==, RXRPC_CALL_COMPLETE);
ASSERT(test_bit(RXRPC_CALL_RELEASED, &call->flags)); ASSERT(test_bit(RXRPC_CALL_RELEASED, &call->flags));
ASSERTCMP(call->conn, ==, NULL);
rxrpc_cleanup_ring(call); rxrpc_cleanup_ring(call);
rxrpc_free_skb(call->tx_pending, rxrpc_skb_cleaned); rxrpc_free_skb(call->tx_pending, rxrpc_skb_cleaned);
......
...@@ -785,6 +785,7 @@ void rxrpc_disconnect_client_call(struct rxrpc_call *call) ...@@ -785,6 +785,7 @@ void rxrpc_disconnect_client_call(struct rxrpc_call *call)
u32 cid; u32 cid;
spin_lock(&conn->channel_lock); spin_lock(&conn->channel_lock);
set_bit(RXRPC_CALL_DISCONNECTED, &call->flags);
cid = call->cid; cid = call->cid;
if (cid) { if (cid) {
...@@ -792,7 +793,6 @@ void rxrpc_disconnect_client_call(struct rxrpc_call *call) ...@@ -792,7 +793,6 @@ void rxrpc_disconnect_client_call(struct rxrpc_call *call)
chan = &conn->channels[channel]; chan = &conn->channels[channel];
} }
trace_rxrpc_client(conn, channel, rxrpc_client_chan_disconnect); trace_rxrpc_client(conn, channel, rxrpc_client_chan_disconnect);
call->conn = NULL;
/* Calls that have never actually been assigned a channel can simply be /* Calls that have never actually been assigned a channel can simply be
* discarded. If the conn didn't get used either, it will follow * discarded. If the conn didn't get used either, it will follow
...@@ -908,7 +908,6 @@ void rxrpc_disconnect_client_call(struct rxrpc_call *call) ...@@ -908,7 +908,6 @@ void rxrpc_disconnect_client_call(struct rxrpc_call *call)
spin_unlock(&rxnet->client_conn_cache_lock); spin_unlock(&rxnet->client_conn_cache_lock);
out_2: out_2:
spin_unlock(&conn->channel_lock); spin_unlock(&conn->channel_lock);
rxrpc_put_connection(conn);
_leave(""); _leave("");
return; return;
......
...@@ -438,16 +438,12 @@ static void rxrpc_process_delayed_final_acks(struct rxrpc_connection *conn) ...@@ -438,16 +438,12 @@ static void rxrpc_process_delayed_final_acks(struct rxrpc_connection *conn)
/* /*
* connection-level event processor * 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; struct sk_buff *skb;
u32 abort_code = RX_PROTOCOL_ERROR; u32 abort_code = RX_PROTOCOL_ERROR;
int ret; int ret;
rxrpc_see_connection(conn);
if (test_and_clear_bit(RXRPC_CONN_EV_CHALLENGE, &conn->events)) if (test_and_clear_bit(RXRPC_CONN_EV_CHALLENGE, &conn->events))
rxrpc_secure_connection(conn); rxrpc_secure_connection(conn);
...@@ -475,18 +471,32 @@ void rxrpc_process_connection(struct work_struct *work) ...@@ -475,18 +471,32 @@ void rxrpc_process_connection(struct work_struct *work)
} }
} }
out:
rxrpc_put_connection(conn);
_leave("");
return; return;
requeue_and_leave: requeue_and_leave:
skb_queue_head(&conn->rx_queue, skb); skb_queue_head(&conn->rx_queue, skb);
goto out; return;
protocol_error: protocol_error:
if (rxrpc_abort_connection(conn, ret, abort_code) < 0) if (rxrpc_abort_connection(conn, ret, abort_code) < 0)
goto requeue_and_leave; goto requeue_and_leave;
rxrpc_free_skb(skb, rxrpc_skb_freed); 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;
} }
...@@ -171,6 +171,8 @@ void __rxrpc_disconnect_call(struct rxrpc_connection *conn, ...@@ -171,6 +171,8 @@ void __rxrpc_disconnect_call(struct rxrpc_connection *conn,
_enter("%d,%x", conn->debug_id, call->cid); _enter("%d,%x", conn->debug_id, call->cid);
set_bit(RXRPC_CALL_DISCONNECTED, &call->flags);
if (rcu_access_pointer(chan->call) == call) { if (rcu_access_pointer(chan->call) == call) {
/* Save the result of the call so that we can repeat it if necessary /* Save the result of the call so that we can repeat it if necessary
* through the channel, whilst disposing of the actual call record. * through the channel, whilst disposing of the actual call record.
...@@ -223,9 +225,7 @@ void rxrpc_disconnect_call(struct rxrpc_call *call) ...@@ -223,9 +225,7 @@ void rxrpc_disconnect_call(struct rxrpc_call *call)
__rxrpc_disconnect_call(conn, call); __rxrpc_disconnect_call(conn, call);
spin_unlock(&conn->channel_lock); spin_unlock(&conn->channel_lock);
call->conn = NULL;
conn->idle_timestamp = jiffies; conn->idle_timestamp = jiffies;
rxrpc_put_connection(conn);
} }
/* /*
......
...@@ -599,10 +599,8 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) ...@@ -599,10 +599,8 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb)
false, true, false, true,
rxrpc_propose_ack_input_data); rxrpc_propose_ack_input_data);
if (seq0 == READ_ONCE(call->rx_hard_ack) + 1) { trace_rxrpc_notify_socket(call->debug_id, serial);
trace_rxrpc_notify_socket(call->debug_id, serial); rxrpc_notify_socket(call);
rxrpc_notify_socket(call);
}
unlock: unlock:
spin_unlock(&call->input_lock); spin_unlock(&call->input_lock);
......
...@@ -364,11 +364,14 @@ void rxrpc_queue_local(struct rxrpc_local *local) ...@@ -364,11 +364,14 @@ void rxrpc_queue_local(struct rxrpc_local *local)
void rxrpc_put_local(struct rxrpc_local *local) void rxrpc_put_local(struct rxrpc_local *local)
{ {
const void *here = __builtin_return_address(0); const void *here = __builtin_return_address(0);
unsigned int debug_id;
int n; int n;
if (local) { if (local) {
debug_id = local->debug_id;
n = atomic_dec_return(&local->usage); n = atomic_dec_return(&local->usage);
trace_rxrpc_local(local->debug_id, rxrpc_local_put, n, here); trace_rxrpc_local(debug_id, rxrpc_local_put, n, here);
if (n == 0) if (n == 0)
call_rcu(&local->rcu, rxrpc_local_rcu); call_rcu(&local->rcu, rxrpc_local_rcu);
...@@ -380,14 +383,11 @@ void rxrpc_put_local(struct rxrpc_local *local) ...@@ -380,14 +383,11 @@ void rxrpc_put_local(struct rxrpc_local *local)
*/ */
struct rxrpc_local *rxrpc_use_local(struct rxrpc_local *local) struct rxrpc_local *rxrpc_use_local(struct rxrpc_local *local)
{ {
unsigned int au;
local = rxrpc_get_local_maybe(local); local = rxrpc_get_local_maybe(local);
if (!local) if (!local)
return NULL; return NULL;
au = atomic_fetch_add_unless(&local->active_users, 1, 0); if (!__rxrpc_use_local(local)) {
if (au == 0) {
rxrpc_put_local(local); rxrpc_put_local(local);
return NULL; return NULL;
} }
...@@ -401,14 +401,11 @@ struct rxrpc_local *rxrpc_use_local(struct rxrpc_local *local) ...@@ -401,14 +401,11 @@ struct rxrpc_local *rxrpc_use_local(struct rxrpc_local *local)
*/ */
void rxrpc_unuse_local(struct rxrpc_local *local) void rxrpc_unuse_local(struct rxrpc_local *local)
{ {
unsigned int au;
if (local) { if (local) {
au = atomic_dec_return(&local->active_users); if (__rxrpc_unuse_local(local)) {
if (au == 0) rxrpc_get_local(local);
rxrpc_queue_local(local); rxrpc_queue_local(local);
else }
rxrpc_put_local(local);
} }
} }
...@@ -465,7 +462,7 @@ static void rxrpc_local_processor(struct work_struct *work) ...@@ -465,7 +462,7 @@ static void rxrpc_local_processor(struct work_struct *work)
do { do {
again = false; again = false;
if (atomic_read(&local->active_users) == 0) { if (!__rxrpc_use_local(local)) {
rxrpc_local_destroyer(local); rxrpc_local_destroyer(local);
break; break;
} }
...@@ -479,6 +476,8 @@ static void rxrpc_local_processor(struct work_struct *work) ...@@ -479,6 +476,8 @@ static void rxrpc_local_processor(struct work_struct *work)
rxrpc_process_local_events(local); rxrpc_process_local_events(local);
again = true; again = true;
} }
__rxrpc_unuse_local(local);
} while (again); } while (again);
rxrpc_put_local(local); rxrpc_put_local(local);
......
...@@ -129,7 +129,7 @@ static size_t rxrpc_fill_out_ack(struct rxrpc_connection *conn, ...@@ -129,7 +129,7 @@ static size_t rxrpc_fill_out_ack(struct rxrpc_connection *conn,
int rxrpc_send_ack_packet(struct rxrpc_call *call, bool ping, int rxrpc_send_ack_packet(struct rxrpc_call *call, bool ping,
rxrpc_serial_t *_serial) rxrpc_serial_t *_serial)
{ {
struct rxrpc_connection *conn = NULL; struct rxrpc_connection *conn;
struct rxrpc_ack_buffer *pkt; struct rxrpc_ack_buffer *pkt;
struct msghdr msg; struct msghdr msg;
struct kvec iov[2]; struct kvec iov[2];
...@@ -139,18 +139,14 @@ int rxrpc_send_ack_packet(struct rxrpc_call *call, bool ping, ...@@ -139,18 +139,14 @@ int rxrpc_send_ack_packet(struct rxrpc_call *call, bool ping,
int ret; int ret;
u8 reason; u8 reason;
spin_lock_bh(&call->lock); if (test_bit(RXRPC_CALL_DISCONNECTED, &call->flags))
if (call->conn)
conn = rxrpc_get_connection_maybe(call->conn);
spin_unlock_bh(&call->lock);
if (!conn)
return -ECONNRESET; return -ECONNRESET;
pkt = kzalloc(sizeof(*pkt), GFP_KERNEL); pkt = kzalloc(sizeof(*pkt), GFP_KERNEL);
if (!pkt) { if (!pkt)
rxrpc_put_connection(conn);
return -ENOMEM; return -ENOMEM;
}
conn = call->conn;
msg.msg_name = &call->peer->srx.transport; msg.msg_name = &call->peer->srx.transport;
msg.msg_namelen = call->peer->srx.transport_len; msg.msg_namelen = call->peer->srx.transport_len;
...@@ -244,7 +240,6 @@ int rxrpc_send_ack_packet(struct rxrpc_call *call, bool ping, ...@@ -244,7 +240,6 @@ int rxrpc_send_ack_packet(struct rxrpc_call *call, bool ping,
} }
out: out:
rxrpc_put_connection(conn);
kfree(pkt); kfree(pkt);
return ret; return ret;
} }
...@@ -254,7 +249,7 @@ int rxrpc_send_ack_packet(struct rxrpc_call *call, bool ping, ...@@ -254,7 +249,7 @@ int rxrpc_send_ack_packet(struct rxrpc_call *call, bool ping,
*/ */
int rxrpc_send_abort_packet(struct rxrpc_call *call) int rxrpc_send_abort_packet(struct rxrpc_call *call)
{ {
struct rxrpc_connection *conn = NULL; struct rxrpc_connection *conn;
struct rxrpc_abort_buffer pkt; struct rxrpc_abort_buffer pkt;
struct msghdr msg; struct msghdr msg;
struct kvec iov[1]; struct kvec iov[1];
...@@ -271,13 +266,11 @@ int rxrpc_send_abort_packet(struct rxrpc_call *call) ...@@ -271,13 +266,11 @@ int rxrpc_send_abort_packet(struct rxrpc_call *call)
test_bit(RXRPC_CALL_TX_LAST, &call->flags)) test_bit(RXRPC_CALL_TX_LAST, &call->flags))
return 0; return 0;
spin_lock_bh(&call->lock); if (test_bit(RXRPC_CALL_DISCONNECTED, &call->flags))
if (call->conn)
conn = rxrpc_get_connection_maybe(call->conn);
spin_unlock_bh(&call->lock);
if (!conn)
return -ECONNRESET; return -ECONNRESET;
conn = call->conn;
msg.msg_name = &call->peer->srx.transport; msg.msg_name = &call->peer->srx.transport;
msg.msg_namelen = call->peer->srx.transport_len; msg.msg_namelen = call->peer->srx.transport_len;
msg.msg_control = NULL; msg.msg_control = NULL;
...@@ -312,8 +305,6 @@ int rxrpc_send_abort_packet(struct rxrpc_call *call) ...@@ -312,8 +305,6 @@ int rxrpc_send_abort_packet(struct rxrpc_call *call)
trace_rxrpc_tx_packet(call->debug_id, &pkt.whdr, trace_rxrpc_tx_packet(call->debug_id, &pkt.whdr,
rxrpc_tx_point_call_abort); rxrpc_tx_point_call_abort);
rxrpc_tx_backoff(call, ret); rxrpc_tx_backoff(call, ret);
rxrpc_put_connection(conn);
return ret; return ret;
} }
......
...@@ -364,27 +364,31 @@ static void rxrpc_peer_keepalive_dispatch(struct rxrpc_net *rxnet, ...@@ -364,27 +364,31 @@ static void rxrpc_peer_keepalive_dispatch(struct rxrpc_net *rxnet,
if (!rxrpc_get_peer_maybe(peer)) if (!rxrpc_get_peer_maybe(peer))
continue; continue;
spin_unlock_bh(&rxnet->peer_hash_lock); if (__rxrpc_use_local(peer->local)) {
spin_unlock_bh(&rxnet->peer_hash_lock);
keepalive_at = peer->last_tx_at + RXRPC_KEEPALIVE_TIME;
slot = keepalive_at - base; keepalive_at = peer->last_tx_at + RXRPC_KEEPALIVE_TIME;
_debug("%02x peer %u t=%d {%pISp}", slot = keepalive_at - base;
cursor, peer->debug_id, slot, &peer->srx.transport); _debug("%02x peer %u t=%d {%pISp}",
cursor, peer->debug_id, slot, &peer->srx.transport);
if (keepalive_at <= base ||
keepalive_at > base + RXRPC_KEEPALIVE_TIME) {
rxrpc_send_keepalive(peer);
slot = RXRPC_KEEPALIVE_TIME;
}
if (keepalive_at <= base || /* A transmission to this peer occurred since last we
keepalive_at > base + RXRPC_KEEPALIVE_TIME) { * examined it so put it into the appropriate future
rxrpc_send_keepalive(peer); * bucket.
slot = RXRPC_KEEPALIVE_TIME; */
slot += cursor;
slot &= mask;
spin_lock_bh(&rxnet->peer_hash_lock);
list_add_tail(&peer->keepalive_link,
&rxnet->peer_keepalive[slot & mask]);
rxrpc_unuse_local(peer->local);
} }
/* A transmission to this peer occurred since last we examined
* it so put it into the appropriate future bucket.
*/
slot += cursor;
slot &= mask;
spin_lock_bh(&rxnet->peer_hash_lock);
list_add_tail(&peer->keepalive_link,
&rxnet->peer_keepalive[slot & mask]);
rxrpc_put_peer_locked(peer); rxrpc_put_peer_locked(peer);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment