linux-nfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Joe Habermann <joe.habermann@gmail.com>
To: "J. Bruce Fields" <bfields@fieldses.org>
Cc: linux-nfs@vger.kernel.org
Subject: Re: incorrect EXDEV error occasionally returned for rename over NFS
Date: Tue, 16 Nov 2010 17:49:22 -0600	[thread overview]
Message-ID: <AANLkTimmyOEov1m=Pk=brX1-JGQo2MkirapZHyx66cvj@mail.gmail.com> (raw)
In-Reply-To: <20101116185551.GD3971@fieldses.org>

I did test out the fix and it did avoid the EXDEV problem.  But I see your
point about it opening up a small security hole.

The other thought I had was to use checking similar to what's in
svc_export_match() to see if the two exports were really for the same
namespace/client domain:

--- vfs.c.orig  2011-02-10 18:39:30.000000000 -0600
+++ vfs.c       2011-02-10 18:39:46.000000000 -0600
@@ -1744,8 +1744,12 @@ nfsd_rename(struct svc_rqst *rqstp, stru
        tdir = tdentry->d_inode;

        err = (rqstp->rq_vers == 2) ? nfserr_acces : nfserr_xdev;
-       if (ffhp->fh_export != tfhp->fh_export)
+       if (ffhp->fh_export != tfhp->fh_export) {
+           if (ffhp->fh_export->ex_client      != tfhp->fh_export->ex_client ||
+                ffhp->fh_export->ex_path.dentry !=
tfhp->fh_export->ex_path.dentry ||
+                ffhp->fh_export->ex_path.mnt    !=
tfhp->fh_export->ex_path.mnt)
                goto out;
+       }

        err = nfserr_perm;
        if (!flen || isdotent(fname, flen) || !tlen || isdotent(tname, tlen))

I don't think this is perfect because in the race window, the old export
could be removed and a new one could enter the cache with a different
ex_client pointer.

More noodling.

Thanks, -Joe

On Tue, Nov 16, 2010 at 12:55 PM, J. Bruce Fields <bfields@fieldses.org> wrote:
> On Mon, Nov 15, 2010 at 03:46:44PM -0600, Joe Habermann wrote:
>> When running SVN checkouts over NFSv3, they occasionally fail with an
>> EXDEV error:
>>
>> (etc.)
>> U    snfs/build/Makefile.hpux
>> svn: In directory 'snfs/build'
>> svn: Can't move 'snfs/build/tempfile.tmp' to
>> 'snfs/build/Makefile.sol': Invalid cross-device link
>>
>> I've noticed that other people have reported this problem but it remains
>> unresolved.  Through some debugging, I believe I have found root cause.
>> nfsd_rename() contains the following code:
>>
>> 1707 /*
>> 1708  * Rename a file
>> 1709  * N.B. After this call _both_ ffhp and tfhp need an fh_put 1710  */
>> 1711 __be32
>> 1712 nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char
>> *fname, int flen,
>> 1713                             struct svc_fh *tfhp, char *tname, int tlen)
>> 1714 {
>> 1715         struct dentry   *fdentry, *tdentry, *odentry, *ndentry, *trap;
>> 1716         struct inode    *fdir, *tdir;
>> 1717         __be32          err;
>> 1718         int             host_err;
>> 1719
>> 1720         err = fh_verify(rqstp, ffhp, S_IFDIR, NFSD_MAY_REMOVE);
>> 1721         if (err)
>> 1722                 goto out;
>> 1723         err = fh_verify(rqstp, tfhp, S_IFDIR, NFSD_MAY_CREATE);
>> 1724         if (err)
>> 1725                 goto out;
>> 1726
>> 1727         fdentry = ffhp->fh_dentry;
>> 1728         fdir = fdentry->d_inode;
>> 1729
>> 1730         tdentry = tfhp->fh_dentry;
>> 1731         tdir = tdentry->d_inode;
>> 1732
>> 1733         err = (rqstp->rq_vers == 2) ? nfserr_acces : nfserr_xdev;
>> 1734         if (ffhp->fh_export != tfhp->fh_export)
>> 1735                 goto out;
>>
>> Line 1734 compares the fh_export pointers in the file handles for
>> the "from" and "to" directories.  However, this is a bad check since
>> it's possible these pointers differ yet refer to the same underlying
>> file system.  Based on code inspection, encached export pointers are
>> only valid for 30 minutes before they expire and are reinserted through
>> calls made between the kernel and rpc.mountd.  So a race exists where the
>> ffhp->fh_export is populated on line 1720, the export cache is updated,
>> and tfhp->fh_export is set on line 1723 to a different value.
>>
>> This window can be cranked wide open by putting an "msleep(10);"
>> between lines 1722 and 1723 above, recompiling and reinstalling nfsd.ko
>> etc., and then running SVN checkouts in a loop from an NFS client.
>> Within 30 minutes, the problem is seen.
>
> Good detective work, thanks.  Have you confirmed that applying your
> change below eliminates the problem?
>
>> I'm still very new to the NFS code, but I'm wonder whether the check
>> needs to be something like this instead:
>>
>> --- vfs.c.orig  2010-11-15 14:35:18.000000000 -0600
>> +++ vfs.c       2010-11-15 14:43:52.000000000 -0600
>> @@ -1731,7 +1731,7 @@ nfsd_rename(struct svc_rqst *rqstp, stru
>>    tdir = tdentry->d_inode;
>>
>>    err = (rqstp->rq_vers == 2) ? nfserr_acces : nfserr_xdev;
>> -       if (ffhp->fh_export != tfhp->fh_export)
>> +       if (fdir->i_sb != tdir->i_sb)
>>        goto out;
>>
>>    err = nfserr_perm;
>>
>> Thoughts?  I think it's either that or some locking needs to be put in
>> to guarantee that the file handles for the "to" and "from" directory
>> always wind up with the same export pointer when they belong to the
>> same file system.  I guess the other question is why there is an
>> EXDEV check at the top of nfsd_rename() instead of putting it in
>> vfs_rename().
>
> The fh_verify call is also responsible for setting the identity of the
> user, and that may be affected by export options like root_squash.  In
> the above case if ffhp was on a root_squash export, and tfhp on a
> no_root_squash export, and we get a request from uid 0, we'd end up
> with an incoming uid-0 user running as root (since the fh_verify for
> tfhp was performed last), and might allow renaming from a directory that
> only root had write permission to.
>
> It's a minor hole (especially since it's already easy for a client to
> circumvent protections that vary from export to export in the default
> no_subtree_check case).  But we should be able to do better.
>
> I'm not sure what to suggest.
>
> --b.
>
>>
>> For full disclosure, I'm actually running the SLES11 SP1 kernel,
>> linux-2.6.32.12-0.7, but I've looked at the latest upstream kernels and
>> they appear to have the same code and I assume the same issue.
>

  reply	other threads:[~2010-11-16 23:49 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-11-15 21:46 incorrect EXDEV error occasionally returned for rename over NFS Joe Habermann
2010-11-16 18:55 ` J. Bruce Fields
2010-11-16 23:49   ` Joe Habermann [this message]
2010-11-17 23:39   ` Joe Habermann
2010-11-19 23:57     ` J. Bruce Fields
2010-11-23 20:55       ` Joe Habermann
2010-11-24 15:07         ` J. Bruce Fields

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='AANLkTimmyOEov1m=Pk=brX1-JGQo2MkirapZHyx66cvj@mail.gmail.com' \
    --to=joe.habermann@gmail.com \
    --cc=bfields@fieldses.org \
    --cc=linux-nfs@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).