More NFS Client Bugfixes for Linux 6.6-rc

Stable Fix:
   * Revert "SUNRPC dont update timeout value on connection reset"
   * NFSv4: Fix a state manager thread deadlock regression
 
 Bugfixes:
   * Fix a potential NULL pointer dereference in nfs_inode_remove_request()
   * Fix a rare NULL pointer dereference in xs_tcp_tls_setup_socket()
   * Fix long delay before failing a TLS mount when server does not support TLS
   * Fix various NFS state manager issues
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEnZ5MQTpR7cLU7KEp18tUv7ClQOsFAmUcZYIACgkQ18tUv7Cl
 QOtNyBAA0RaAI4D5Em3/JxmdaqoK8LgSlw3gSup0VoWG1/gyKlt0wW6gDPzffIVK
 pb5wTMdDVzZmbamll15gvOJYYY4tEVpMGg5unR5LIIhS/5Z5TbGEU3ioO7xsKnTI
 0FYtQ0fEXJaPqVLUh3xY/W7Bn2wdehD2bqSfYakddL1C9+cc1XRnxA5BLEL67ZQ4
 zlBhI2acv/9eEGZjVYdI8cv+27WvQY+ud21VZrEGPuztZklfwipNBLr3qT2WpwTU
 vLqp5Py1PydEV7CHIrPtAKhQIuxlYr4m//YXMD4jU4bQuHql0TmUGzJEvfsOzIZn
 LPEJy2kIA37/Rm92lPeZ0m6E/bwXz8iv7HTFpvgIf6ZIY2PSsVNFZYovhSv/yV8N
 qoRcpBswi5nYXZ2KoVfjp+J8NQXWjVS/ODoycdCCWVKEjrVh5oFPfYH67jph4o7l
 E0QaSNKPhy2OHWVgf8hQNJ+Fi34qvxYG1F2RmaTcJybyco1o4jVQ4WJhNGNJbLWH
 dorEko6n82XPBB9+1Qo2nUqLwNgNdyHmmqBO0OG5tJH3mrIrRPJ8DBjAIWggDGSH
 pfB2GftuTuXIUvY97PxZlW0Iz1ZKX6NPFS/gtHJizbVGl+E5wttxbPPJNsApA/v1
 ZoE2K2cgSsyqTXR6arf55EJZyO4oAnmYEPQLu5jlyV83vQQz/xY=
 =qUGw
 -----END PGP SIGNATURE-----

Merge tag 'nfs-for-6.6-3' of git://git.linux-nfs.org/projects/anna/linux-nfs

Pull NFS client fixes from Anna Schumaker:
 "Stable fixes:
   - Revert "SUNRPC dont update timeout value on connection reset"
   - NFSv4: Fix a state manager thread deadlock regression

  Fixes:
   - Fix potential NULL pointer dereference in nfs_inode_remove_request()
   - Fix rare NULL pointer dereference in xs_tcp_tls_setup_socket()
   - Fix long delay before failing a TLS mount when server does not
     support TLS
   - Fix various NFS state manager issues"

* tag 'nfs-for-6.6-3' of git://git.linux-nfs.org/projects/anna/linux-nfs:
  nfs: decrement nrequests counter before releasing the req
  SUNRPC/TLS: Lock the lower_xprt during the tls handshake
  Revert "SUNRPC dont update timeout value on connection reset"
  NFSv4: Fix a state manager thread deadlock regression
  NFSv4: Fix a nfs4_state_manager() race
  SUNRPC: Fail quickly when server does not recognize TLS
This commit is contained in:
Linus Torvalds 2023-10-03 12:13:10 -07:00
commit cbf3a2cb15
7 changed files with 63 additions and 22 deletions

View file

@ -10622,7 +10622,9 @@ static void nfs4_disable_swap(struct inode *inode)
*/
struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
nfs4_schedule_state_manager(clp);
set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state);
wake_up_var(&clp->cl_state);
}
static const struct inode_operations nfs4_dir_inode_operations = {

View file

@ -1209,16 +1209,26 @@ void nfs4_schedule_state_manager(struct nfs_client *clp)
{
struct task_struct *task;
char buf[INET6_ADDRSTRLEN + sizeof("-manager") + 1];
struct rpc_clnt *clnt = clp->cl_rpcclient;
bool swapon = false;
if (clp->cl_rpcclient->cl_shutdown)
if (clnt->cl_shutdown)
return;
set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
if (test_and_set_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state) != 0) {
wake_up_var(&clp->cl_state);
return;
if (atomic_read(&clnt->cl_swapper)) {
swapon = !test_and_set_bit(NFS4CLNT_MANAGER_AVAILABLE,
&clp->cl_state);
if (!swapon) {
wake_up_var(&clp->cl_state);
return;
}
}
set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state);
if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
return;
__module_get(THIS_MODULE);
refcount_inc(&clp->cl_count);
@ -1235,8 +1245,9 @@ void nfs4_schedule_state_manager(struct nfs_client *clp)
__func__, PTR_ERR(task));
if (!nfs_client_init_is_complete(clp))
nfs_mark_client_ready(clp, PTR_ERR(task));
if (swapon)
clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state);
nfs4_clear_state_manager_bit(clp);
clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state);
nfs_put_client(clp);
module_put(THIS_MODULE);
}
@ -2703,6 +2714,13 @@ static void nfs4_state_manager(struct nfs_client *clp)
nfs4_end_drain_session(clp);
nfs4_clear_state_manager_bit(clp);
if (test_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state) &&
!test_and_set_bit(NFS4CLNT_MANAGER_RUNNING,
&clp->cl_state)) {
memflags = memalloc_nofs_save();
continue;
}
if (!test_and_set_bit(NFS4CLNT_RECALL_RUNNING, &clp->cl_state)) {
if (test_and_clear_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) {
nfs_client_return_marked_delegations(clp);
@ -2741,22 +2759,25 @@ static int nfs4_run_state_manager(void *ptr)
allow_signal(SIGKILL);
again:
set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state);
nfs4_state_manager(clp);
if (atomic_read(&cl->cl_swapper)) {
if (test_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state) &&
!test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state)) {
wait_var_event_interruptible(&clp->cl_state,
test_bit(NFS4CLNT_RUN_MANAGER,
&clp->cl_state));
if (atomic_read(&cl->cl_swapper) &&
test_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state))
if (!atomic_read(&cl->cl_swapper))
clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state);
if (refcount_read(&clp->cl_count) > 1 && !signalled() &&
!test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state))
goto again;
/* Either no longer a swapper, or were signalled */
clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state);
}
clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state);
if (refcount_read(&clp->cl_count) > 1 && !signalled() &&
test_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state) &&
!test_and_set_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state))
!test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state))
goto again;
nfs_put_client(clp);

View file

@ -802,8 +802,8 @@ static void nfs_inode_remove_request(struct nfs_page *req)
}
if (test_and_clear_bit(PG_INODE_REF, &req->wb_flags)) {
nfs_release_request(req);
atomic_long_dec(&NFS_I(nfs_page_to_inode(req))->nrequests);
nfs_release_request(req);
}
}

View file

@ -769,9 +769,14 @@ int rpcauth_wrap_req(struct rpc_task *task, struct xdr_stream *xdr)
* @task: controlling RPC task
* @xdr: xdr_stream containing RPC Reply header
*
* On success, @xdr is updated to point past the verifier and
* zero is returned. Otherwise, @xdr is in an undefined state
* and a negative errno is returned.
* Return values:
* %0: Verifier is valid. @xdr now points past the verifier.
* %-EIO: Verifier is corrupted or message ended early.
* %-EACCES: Verifier is intact but not valid.
* %-EPROTONOSUPPORT: Server does not support the requested auth type.
*
* When a negative errno is returned, @xdr is left in an undefined
* state.
*/
int
rpcauth_checkverf(struct rpc_task *task, struct xdr_stream *xdr)

View file

@ -129,9 +129,9 @@ static int tls_validate(struct rpc_task *task, struct xdr_stream *xdr)
if (*p != rpc_auth_null)
return -EIO;
if (xdr_stream_decode_opaque_inline(xdr, &str, starttls_len) != starttls_len)
return -EIO;
return -EPROTONOSUPPORT;
if (memcmp(str, starttls_token, starttls_len))
return -EIO;
return -EPROTONOSUPPORT;
return 0;
}

View file

@ -2476,8 +2476,7 @@ call_status(struct rpc_task *task)
goto out_exit;
}
task->tk_action = call_encode;
if (status != -ECONNRESET && status != -ECONNABORTED)
rpc_check_timeout(task);
rpc_check_timeout(task);
return;
out_exit:
rpc_call_rpcerror(task, status);
@ -2725,7 +2724,15 @@ rpc_decode_header(struct rpc_task *task, struct xdr_stream *xdr)
out_verifier:
trace_rpc_bad_verifier(task);
goto out_garbage;
switch (error) {
case -EPROTONOSUPPORT:
goto out_err;
case -EACCES:
/* Re-encode with a fresh cred */
fallthrough;
default:
goto out_garbage;
}
out_msg_denied:
error = -EACCES;

View file

@ -2672,6 +2672,10 @@ static void xs_tcp_tls_setup_socket(struct work_struct *work)
rcu_read_lock();
lower_xprt = rcu_dereference(lower_clnt->cl_xprt);
rcu_read_unlock();
if (wait_on_bit_lock(&lower_xprt->state, XPRT_LOCKED, TASK_KILLABLE))
goto out_unlock;
status = xs_tls_handshake_sync(lower_xprt, &upper_xprt->xprtsec);
if (status) {
trace_rpc_tls_not_started(upper_clnt, upper_xprt);
@ -2681,6 +2685,7 @@ static void xs_tcp_tls_setup_socket(struct work_struct *work)
status = xs_tcp_tls_finish_connecting(lower_xprt, upper_transport);
if (status)
goto out_close;
xprt_release_write(lower_xprt, NULL);
trace_rpc_socket_connect(upper_xprt, upper_transport->sock, 0);
if (!xprt_test_and_set_connected(upper_xprt)) {
@ -2702,6 +2707,7 @@ static void xs_tcp_tls_setup_socket(struct work_struct *work)
return;
out_close:
xprt_release_write(lower_xprt, NULL);
rpc_shutdown_client(lower_clnt);
/* xprt_force_disconnect() wakes tasks with a fixed tk_status code.