From: Trond Myklebust <Trond.Myklebust@netapp.com>
To: NeilBrown <neilb@suse.de>
Cc: linux-nfs@vger.kernel.org
Subject: Re: Bug in xdr_copy_to_scratch???
Date: Wed, 16 Mar 2011 18:57:02 -0400 [thread overview]
Message-ID: <1300316222.8295.5.camel@lade.trondhjem.org> (raw)
In-Reply-To: <20110317093838.0f15eb98@notabene.brown>
On Thu, 2011-03-17 at 09:38 +1100, NeilBrown wrote:
> On Wed, 16 Mar 2011 09:42:54 -0400 Trond Myklebust
> <Trond.Myklebust@netapp.com> wrote:
>
> > On Wed, 2011-03-16 at 21:36 +1100, NeilBrown wrote:
> > > Hi Trond,
> > >
> > > I'm currently trying to track down the cause of some very
> > > odd behaviour in readdir in openSUSE 11.4 (2.6.37.3 based).
> > >
> > > I think it might be caused by xdr_copy_to_scratch not quite
> > > behaving correctly.
> > >
> > > In particular, when it has to copy into the scratch buffer
> > > it only copies 'nbytes' bytes - which sounds reasonable but
> > > isn't. It should copy XDR_QUADLEN(nbytes) words.
> > >
> > > In particular, nfs3_decode_dirent contains:
> > >
> > > p = xdr_inline_decode(xdr, entry->len + 8);
> > > if (unlikely(!p))
> > > goto out_overflow;
> > > entry->name = (const char *) p;
> > > p += XDR_QUADLEN(entry->len);
> > > entry->prev_cookie = entry->cookie;
> > > p = xdr_decode_hyper(p, &entry->cookie);
> > >
> > >
> > > where the cookie needs all of those last few bytes which
> > > we would only get by rounding nbytes up to a multiple of 4.
> > >
> > >
> > > I haven't developed or tested a fix yet, but as it is clearly a bug,
> > > I thought I would let you know before I call it a night.
> >
> > Hi Neil,
> >
> > I'm not sure I 100% agree that is a bug in xdr_copy_to_scratch(). I
> > think it is rather an artifact of the fact that xdr_inline_decode takes
> > a byte argument instead of a word argument, which means that combining
> > buffer lengths needs to done with care: my fault :-(.
> >
> > To illustrate what I mean, consider the following snippet of xdr:
> >
> > p = xdr_decode_inline(xdr, len1 + len2);
> > name1 = p;
> > p += XDR_QUADLEN(len1);
> > name2 = p;
> >
> > No matter what we do in xdr_decode_inline(), there is no way we can
> > determine the true value of XDR_QUADLEN(len1)<<2 + XDR_QUADLEN(len2)<<2
> > if len1 and len2 are arbitrary buffer lengths.
> >
> > So I'd argue that while we could fix this issue in xdr_copy_to_scratch
> > for this particular case, in reality nfs3_decode_dirent should not be
> > asking for a buffer of (entry->len + 8) bytes when it knows there is an
> > alignment issue due to the fact that the 8 cookie bytes follows the
> > string.
>
> Your argument certainly has some merit.
> Probably the question to ask is "what sort of fix is most likely to
> avoid such problems recurring in the future", and I cannot convince myself
> that either is clearly better than the other on that basis.
>
> I note that in 2.6.38 the readdir problem has been 'fixed' by the
> introduction of decode_inline_filename3 and similar functions.
> However there is still one place where xdr_decode_inline is used in
> an unsafe way - see patch below.
>
> We should probably submit a fix to 2.6.37-stable though. For that it
> is possibly simplest to tell xdr_decode_inline to round nbytes up to
> a multiple of 4 - would you agree?
The only case that seems affected is nfs3_decode_dirent(). The other
decode_dirent cases are all OK AFAICS.
> Also: looking at xdr_copy_to_scratch again, is there any chance that
> 'cplen' could not be a multiple of 4? I think not but I'm not
> 100% sure. If it were not a multiple of 4, then the
> __xdr_inline_decode call would do the wrong thing.
If it is not a multiple of 4, then that would mean we must have decoded
the preceding xdr-encoded buffer incorrectly.
> And finally - it bothers me a little bit that a call to xdr_copy_to_scratch
> will corrupt the value returned by the previous call to
> xdr_copy_to_scratch. This is unlikely to be a problem as you
> get at most one of those calls every 4096 bytes, and the previous
> value will almost certainly have been used before the next value
> is copied over it. But it isn't clear what guarantees that it will
> have been used, so a future change or extension to the protocol could
> conceivably result in corruption happening in the scratch buffer.
> Maybe it isn't worth worrying about.... not sure.
The long term solution is to switch to using words instead of bytes as
the argument to xdr_inline_decode(). If we force people to use
XDR_QUADLEN() on non-word sized arguments, then that will avoid any
confusion.
>
> Fix theoretical decoding problem in nfsv4 compound header decoding.
>
> As all XDR encoded fields are rounded up to a multiple of 4
> bytes in size, a string field followed by a number field could
> have padding between the string and number.
>
> If xdr_inline_decode is asked to decode the combined field
> (by summing the lengths) it will normally work. However if it
> needs to copy the field into that 'scratch' space to avoid a
> break between pages, it will only copy the requested number of bytes,
> as it assumes any padding is at the end - so the number field will not be
> fully copied.
>
> So in decode_compound_hdr, call xdr_inline_decode twice instead of
> risking an incorrect decode.
>
> As this field is always near the start of a packet, the chance that it
> will ever cross a page boundary and so cause a problem is vanishingly small,
> but having 'wrong' code could set an example that might get copied.
> So fix it anyway.
>
>
> Signed-off-by: NeilBrown <neilb@suse.de>
>
> diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
> index 4e2c168..c52b50c 100644
> --- a/fs/nfs/nfs4xdr.c
> +++ b/fs/nfs/nfs4xdr.c
> @@ -2638,11 +2638,13 @@ static int decode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
> hdr->status = be32_to_cpup(p++);
> hdr->taglen = be32_to_cpup(p);
>
> - p = xdr_inline_decode(xdr, hdr->taglen + 4);
> + p = xdr_inline_decode(xdr, hdr->taglen);
> if (unlikely(!p))
> goto out_overflow;
> hdr->tag = (char *)p;
> - p += XDR_QUADLEN(hdr->taglen);
> + p = xdr_inline_decode(xdr, 4);
> + if (unlikely(!p))
> + goto out_overflow;
> hdr->nops = be32_to_cpup(p);
> if (unlikely(hdr->nops < 1))
> return nfs4_stat_to_errno(hdr->status);
There is a similar instance in fs/nfsd/nfs4callback.c: see
decode_cb_compound4res().
Might as well fix both in the same way.
--
Trond Myklebust
Linux NFS client maintainer
NetApp
Trond.Myklebust@netapp.com
www.netapp.com
next prev parent reply other threads:[~2011-03-16 22:58 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-03-16 10:36 Bug in xdr_copy_to_scratch??? NeilBrown
2011-03-16 13:42 ` Trond Myklebust
2011-03-16 22:38 ` NeilBrown
2011-03-16 22:57 ` Trond Myklebust [this message]
2011-03-17 18:01 ` Trond Myklebust
2011-03-17 23:16 ` NeilBrown
2011-03-17 23:22 ` Trond Myklebust
2011-03-17 23:27 ` Trond Myklebust
2011-03-18 0:51 ` NeilBrown
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=1300316222.8295.5.camel@lade.trondhjem.org \
--to=trond.myklebust@netapp.com \
--cc=linux-nfs@vger.kernel.org \
--cc=neilb@suse.de \
/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).