Message ID | 167632902904.1896.16364452992981515041@noble.neil.brown.name (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | NFS: avoid infinite NFS4ERR_OLD_STATEID loops | expand |
On Tue, 2023-02-14 at 09:57 +1100, NeilBrown wrote: > > Linux-NFS responds to NFS4ERR_OLD_STATEID by simply retrying the > request, hoping to make use of an updated stateid that might have > arrived from the server. This is usually successful. > > However if the client and server get out-of-sync for some reason and > if > there is no new stateid to try, this can result in an indefinite loop > which looks a bit like a DoS attack. > > This can particularly happen when a server replies with success to an > OPEN request, but fails a subsequent GETATTR. This has been observed > with Netapp and Hitachi servers when a concurrent unlink from a > different client removes the file between the OPEN and the GETATTR. > The > GETATTR returns NFS4ERR_STALE. Then they are both badly broken servers, and people should complain to NetApp and Hitachi. We're still not fixing their server bugs in the Linux client. NACK...
On Tue, 14 Feb 2023, Trond Myklebust wrote: > On Tue, 2023-02-14 at 09:57 +1100, NeilBrown wrote: > > > > Linux-NFS responds to NFS4ERR_OLD_STATEID by simply retrying the > > request, hoping to make use of an updated stateid that might have > > arrived from the server. This is usually successful. > > > > However if the client and server get out-of-sync for some reason and > > if > > there is no new stateid to try, this can result in an indefinite loop > > which looks a bit like a DoS attack. > > > > This can particularly happen when a server replies with success to an > > OPEN request, but fails a subsequent GETATTR. This has been observed > > with Netapp and Hitachi servers when a concurrent unlink from a > > different client removes the file between the OPEN and the GETATTR. > > The > > GETATTR returns NFS4ERR_STALE. > > Then they are both badly broken servers, and people should complain to > NetApp and Hitachi. We're still not fixing their server bugs in the > Linux client. It's not about fixing a server bug. It is defensive programming. We shouldn't let a buggy server cause a melt-down. > > NACK... Oh well, I tried. Thanks, NeilBrown > -- > Trond Myklebust > Linux NFS client maintainer, Hammerspace > trond.myklebust@hammerspace.com > > >
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 40d749f29ed3..ee77bf42ec38 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -408,7 +408,7 @@ static long nfs4_update_delay(long *timeout) long ret; if (!timeout) return NFS4_POLL_RETRY_MAX; - if (*timeout <= 0) + if (*timeout <= NFS4_POLL_RETRY_MIN) *timeout = NFS4_POLL_RETRY_MIN; if (*timeout > NFS4_POLL_RETRY_MAX) *timeout = NFS4_POLL_RETRY_MAX; @@ -562,9 +562,24 @@ static int nfs4_do_handle_exception(struct nfs_server *server, return 0; case -NFS4ERR_RETRY_UNCACHED_REP: - case -NFS4ERR_OLD_STATEID: exception->retry = 1; break; + case -NFS4ERR_OLD_STATEID: + /* Simple retry is usually safe, but buggy + * servers can cause DoS - so be careful. + */ + if (exception->timeout > HZ*2) { + ret = -ESTALE; + } else if (exception->timeout < 5) { + /* 5 tries with no delay */ + exception->retry = 1; + exception->timeout += 1; + ret = 0; + } else { + exception->delay = 1; + ret = 0; + } + break; case -NFS4ERR_BADOWNER: /* The following works around a Linux server bug! */ case -NFS4ERR_BADNAME: @@ -5506,10 +5521,12 @@ static int nfs4_write_done_cb(struct rpc_task *task, .inode = hdr->inode, .state = hdr->args.context->state, .stateid = &hdr->args.stateid, + .timeout = hdr->timeout, }; task->tk_status = nfs4_async_handle_exception(task, NFS_SERVER(inode), task->tk_status, &exception); + hdr->timeout = exception.timeout; if (exception.retry) { rpc_restart_call_prepare(task); return -EAGAIN; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index e86cf6642d21..0c791e5b213c 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1625,6 +1625,8 @@ struct nfs_pgio_header { unsigned int good_bytes; /* boundary of good data */ unsigned long flags; + long timeout; + /* * rpc data */