rxrpc: Call state should be read with READ_ONCE() under some circumstances
The call state may be changed at any time by the data-ready routine in response to received packets, so if the call state is to be read and acted upon several times in a function, READ_ONCE() must be used unless the call state lock is held. Signed-off-by: David Howells <dhowells@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:

committed by
David S. Miller

parent
02b2faaf0a
commit
146d8fef9d
@@ -488,6 +488,7 @@ rxrpc_new_client_call_for_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg,
|
||||
int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len)
|
||||
__releases(&rx->sk.sk_lock.slock)
|
||||
{
|
||||
enum rxrpc_call_state state;
|
||||
enum rxrpc_command cmd;
|
||||
struct rxrpc_call *call;
|
||||
unsigned long user_call_ID = 0;
|
||||
@@ -526,13 +527,17 @@ int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len)
|
||||
return PTR_ERR(call);
|
||||
/* ... and we have the call lock. */
|
||||
} else {
|
||||
ret = -EBUSY;
|
||||
if (call->state == RXRPC_CALL_UNINITIALISED ||
|
||||
call->state == RXRPC_CALL_CLIENT_AWAIT_CONN ||
|
||||
call->state == RXRPC_CALL_SERVER_PREALLOC ||
|
||||
call->state == RXRPC_CALL_SERVER_SECURING ||
|
||||
call->state == RXRPC_CALL_SERVER_ACCEPTING)
|
||||
switch (READ_ONCE(call->state)) {
|
||||
case RXRPC_CALL_UNINITIALISED:
|
||||
case RXRPC_CALL_CLIENT_AWAIT_CONN:
|
||||
case RXRPC_CALL_SERVER_PREALLOC:
|
||||
case RXRPC_CALL_SERVER_SECURING:
|
||||
case RXRPC_CALL_SERVER_ACCEPTING:
|
||||
ret = -EBUSY;
|
||||
goto error_release_sock;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ret = mutex_lock_interruptible(&call->user_mutex);
|
||||
release_sock(&rx->sk);
|
||||
@@ -542,10 +547,11 @@ int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len)
|
||||
}
|
||||
}
|
||||
|
||||
state = READ_ONCE(call->state);
|
||||
_debug("CALL %d USR %lx ST %d on CONN %p",
|
||||
call->debug_id, call->user_call_ID, call->state, call->conn);
|
||||
call->debug_id, call->user_call_ID, state, call->conn);
|
||||
|
||||
if (call->state >= RXRPC_CALL_COMPLETE) {
|
||||
if (state >= RXRPC_CALL_COMPLETE) {
|
||||
/* it's too late for this call */
|
||||
ret = -ESHUTDOWN;
|
||||
} else if (cmd == RXRPC_CMD_SEND_ABORT) {
|
||||
@@ -555,12 +561,12 @@ int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len)
|
||||
} else if (cmd != RXRPC_CMD_SEND_DATA) {
|
||||
ret = -EINVAL;
|
||||
} else if (rxrpc_is_client_call(call) &&
|
||||
call->state != RXRPC_CALL_CLIENT_SEND_REQUEST) {
|
||||
state != RXRPC_CALL_CLIENT_SEND_REQUEST) {
|
||||
/* request phase complete for this client call */
|
||||
ret = -EPROTO;
|
||||
} else if (rxrpc_is_service_call(call) &&
|
||||
call->state != RXRPC_CALL_SERVER_ACK_REQUEST &&
|
||||
call->state != RXRPC_CALL_SERVER_SEND_REPLY) {
|
||||
state != RXRPC_CALL_SERVER_ACK_REQUEST &&
|
||||
state != RXRPC_CALL_SERVER_SEND_REPLY) {
|
||||
/* Reply phase not begun or not complete for service call. */
|
||||
ret = -EPROTO;
|
||||
} else {
|
||||
@@ -605,14 +611,20 @@ int rxrpc_kernel_send_data(struct socket *sock, struct rxrpc_call *call,
|
||||
_debug("CALL %d USR %lx ST %d on CONN %p",
|
||||
call->debug_id, call->user_call_ID, call->state, call->conn);
|
||||
|
||||
if (call->state >= RXRPC_CALL_COMPLETE) {
|
||||
ret = -ESHUTDOWN; /* it's too late for this call */
|
||||
} else if (call->state != RXRPC_CALL_CLIENT_SEND_REQUEST &&
|
||||
call->state != RXRPC_CALL_SERVER_ACK_REQUEST &&
|
||||
call->state != RXRPC_CALL_SERVER_SEND_REPLY) {
|
||||
ret = -EPROTO; /* request phase complete for this client call */
|
||||
} else {
|
||||
switch (READ_ONCE(call->state)) {
|
||||
case RXRPC_CALL_CLIENT_SEND_REQUEST:
|
||||
case RXRPC_CALL_SERVER_ACK_REQUEST:
|
||||
case RXRPC_CALL_SERVER_SEND_REPLY:
|
||||
ret = rxrpc_send_data(rxrpc_sk(sock->sk), call, msg, len);
|
||||
break;
|
||||
case RXRPC_CALL_COMPLETE:
|
||||
/* It's too late for this call */
|
||||
ret = -ESHUTDOWN;
|
||||
break;
|
||||
default:
|
||||
/* Request phase complete for this client call */
|
||||
ret = -EPROTO;
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&call->user_mutex);
|
||||
|
Reference in New Issue
Block a user