diff mbox series

[v1,07/13] NFSD add ca_source_server<> to COPY

Message ID 20181019152905.32418-8-olga.kornievskaia@gmail.com (mailing list archive)
State New, archived
Headers show
Series server-side support for "inter" SSC copy | expand

Commit Message

Olga Kornievskaia Oct. 19, 2018, 3:28 p.m. UTC
Note: followed conventions and have struct nfsd4_compoundargs pointer as a
parameter even though it is unused.

Signed-off-by: Andy Adamson <andros@netapp.com>
Signed-off-by: Olga Kornievskaia <kolga@netapp.com>
---
 fs/nfsd/nfs4xdr.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 fs/nfsd/xdr4.h    |  1 +
 2 files changed, 70 insertions(+), 2 deletions(-)

Comments

J. Bruce Fields Nov. 1, 2018, 8:48 p.m. UTC | #1
On Fri, Oct 19, 2018 at 11:28:59AM -0400, Olga Kornievskaia wrote:
> Note: followed conventions and have struct nfsd4_compoundargs pointer as a
> parameter even though it is unused.

It's used--see the definition of READ_BUF.

(I'm not a fan of those macros and they'll probably be replaced some
day.)

--b.

> 
> Signed-off-by: Andy Adamson <andros@netapp.com>
> Signed-off-by: Olga Kornievskaia <kolga@netapp.com>
> ---
>  fs/nfsd/nfs4xdr.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
>  fs/nfsd/xdr4.h    |  1 +
>  2 files changed, 70 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> index 3de42a7..9f6886f 100644
> --- a/fs/nfsd/nfs4xdr.c
> +++ b/fs/nfsd/nfs4xdr.c
> @@ -40,6 +40,7 @@
>  #include <linux/utsname.h>
>  #include <linux/pagemap.h>
>  #include <linux/sunrpc/svcauth_gss.h>
> +#include <linux/sunrpc/addr.h>
>  
>  #include "idmap.h"
>  #include "acl.h"
> @@ -1743,11 +1744,58 @@ static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, str
>  	DECODE_TAIL;
>  }
>  
> +static __be32 nfsd4_decode_nl4_server(struct nfsd4_compoundargs *argp,
> +				      struct nl4_server *ns)
> +{
> +	DECODE_HEAD;
> +	struct nfs42_netaddr *naddr;
> +
> +	READ_BUF(4);
> +	ns->nl4_type = be32_to_cpup(p++);
> +
> +	/* currently support for 1 inter-server source server */
> +	switch (ns->nl4_type) {
> +	case NL4_NAME:
> +	case NL4_URL:
> +		READ_BUF(4);
> +		ns->u.nl4_str_sz = be32_to_cpup(p++);
> +		if (ns->u.nl4_str_sz > NFS4_OPAQUE_LIMIT)
> +			goto xdr_error;
> +
> +		READ_BUF(ns->u.nl4_str_sz);
> +		COPYMEM(ns->u.nl4_str,
> +			ns->u.nl4_str_sz);
> +		break;
> +	case NL4_NETADDR:
> +		naddr = &ns->u.nl4_addr;
> +
> +		READ_BUF(4);
> +		naddr->netid_len = be32_to_cpup(p++);
> +		if (naddr->netid_len > RPCBIND_MAXNETIDLEN)
> +			goto xdr_error;
> +
> +		READ_BUF(naddr->netid_len + 4); /* 4 for uaddr len */
> +		COPYMEM(naddr->netid, naddr->netid_len);
> +
> +		naddr->addr_len = be32_to_cpup(p++);
> +		if (naddr->addr_len > RPCBIND_MAXUADDRLEN)
> +			goto xdr_error;
> +
> +		READ_BUF(naddr->addr_len);
> +		COPYMEM(naddr->addr, naddr->addr_len);
> +		break;
> +	default:
> +		goto xdr_error;
> +	}
> +	DECODE_TAIL;
> +}
> +
>  static __be32
>  nfsd4_decode_copy(struct nfsd4_compoundargs *argp, struct nfsd4_copy *copy)
>  {
>  	DECODE_HEAD;
> -	unsigned int tmp;
> +	struct nl4_server *ns;
> +	int i, count;
>  
>  	status = nfsd4_decode_stateid(argp, &copy->cp_src_stateid);
>  	if (status)
> @@ -1762,8 +1810,24 @@ static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, str
>  	p = xdr_decode_hyper(p, &copy->cp_count);
>  	p++; /* ca_consecutive: we always do consecutive copies */
>  	copy->cp_synchronous = be32_to_cpup(p++);
> -	tmp = be32_to_cpup(p); /* Source server list not supported */
> +	count = be32_to_cpup(p++);
> +
> +	if (count == 0) /* intra-server copy */
> +		goto intra;
>  
> +	/* decode all the supplied server addresses but use first */
> +	copy->cp_src = kmalloc(count * sizeof(struct nl4_server), GFP_KERNEL);
> +	if (copy->cp_src == NULL)
> +		return nfserrno(-ENOMEM);
> +
> +	ns = copy->cp_src;
> +	for (i = 0; i < count; i++) {
> +		status = nfsd4_decode_nl4_server(argp, ns);
> +		if (status)
> +			return status;
> +		ns++;
> +	}
> +intra:
>  	DECODE_TAIL;
>  }
>  
> @@ -4273,6 +4337,9 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
>  	p = xdr_reserve_space(&resp->xdr, 4 + 4);
>  	*p++ = xdr_one; /* cr_consecutive */
>  	*p++ = cpu_to_be32(copy->cp_synchronous);
> +
> +	/* allocated in nfsd4_decode_copy */
> +	kfree(copy->cp_src);
>  	return 0;
>  }
>  
> diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
> index feeb6d4..b4d1140 100644
> --- a/fs/nfsd/xdr4.h
> +++ b/fs/nfsd/xdr4.h
> @@ -521,6 +521,7 @@ struct nfsd4_copy {
>  	u64		cp_src_pos;
>  	u64		cp_dst_pos;
>  	u64		cp_count;
> +	struct nl4_server *cp_src;
>  
>  	/* both */
>  	bool		cp_synchronous;
> -- 
> 1.8.3.1
Olga Kornievskaia Nov. 1, 2018, 9 p.m. UTC | #2
On Thu, Nov 1, 2018 at 4:48 PM J. Bruce Fields <bfields@fieldses.org> wrote:
>
> On Fri, Oct 19, 2018 at 11:28:59AM -0400, Olga Kornievskaia wrote:
> > Note: followed conventions and have struct nfsd4_compoundargs pointer as a
> > parameter even though it is unused.
>
> It's used--see the definition of READ_BUF.

Sigh. It's a leftover comment from Andy. I'll remove it.

> (I'm not a fan of those macros and they'll probably be replaced some
> day.)

READ_BUF is used thru out so I'm assuming you are not against this
patch using it, correct?

>
> --b.
>
> >
> > Signed-off-by: Andy Adamson <andros@netapp.com>
> > Signed-off-by: Olga Kornievskaia <kolga@netapp.com>
> > ---
> >  fs/nfsd/nfs4xdr.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
> >  fs/nfsd/xdr4.h    |  1 +
> >  2 files changed, 70 insertions(+), 2 deletions(-)
> >
> > diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> > index 3de42a7..9f6886f 100644
> > --- a/fs/nfsd/nfs4xdr.c
> > +++ b/fs/nfsd/nfs4xdr.c
> > @@ -40,6 +40,7 @@
> >  #include <linux/utsname.h>
> >  #include <linux/pagemap.h>
> >  #include <linux/sunrpc/svcauth_gss.h>
> > +#include <linux/sunrpc/addr.h>
> >
> >  #include "idmap.h"
> >  #include "acl.h"
> > @@ -1743,11 +1744,58 @@ static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, str
> >       DECODE_TAIL;
> >  }
> >
> > +static __be32 nfsd4_decode_nl4_server(struct nfsd4_compoundargs *argp,
> > +                                   struct nl4_server *ns)
> > +{
> > +     DECODE_HEAD;
> > +     struct nfs42_netaddr *naddr;
> > +
> > +     READ_BUF(4);
> > +     ns->nl4_type = be32_to_cpup(p++);
> > +
> > +     /* currently support for 1 inter-server source server */
> > +     switch (ns->nl4_type) {
> > +     case NL4_NAME:
> > +     case NL4_URL:
> > +             READ_BUF(4);
> > +             ns->u.nl4_str_sz = be32_to_cpup(p++);
> > +             if (ns->u.nl4_str_sz > NFS4_OPAQUE_LIMIT)
> > +                     goto xdr_error;
> > +
> > +             READ_BUF(ns->u.nl4_str_sz);
> > +             COPYMEM(ns->u.nl4_str,
> > +                     ns->u.nl4_str_sz);
> > +             break;
> > +     case NL4_NETADDR:
> > +             naddr = &ns->u.nl4_addr;
> > +
> > +             READ_BUF(4);
> > +             naddr->netid_len = be32_to_cpup(p++);
> > +             if (naddr->netid_len > RPCBIND_MAXNETIDLEN)
> > +                     goto xdr_error;
> > +
> > +             READ_BUF(naddr->netid_len + 4); /* 4 for uaddr len */
> > +             COPYMEM(naddr->netid, naddr->netid_len);
> > +
> > +             naddr->addr_len = be32_to_cpup(p++);
> > +             if (naddr->addr_len > RPCBIND_MAXUADDRLEN)
> > +                     goto xdr_error;
> > +
> > +             READ_BUF(naddr->addr_len);
> > +             COPYMEM(naddr->addr, naddr->addr_len);
> > +             break;
> > +     default:
> > +             goto xdr_error;
> > +     }
> > +     DECODE_TAIL;
> > +}
> > +
> >  static __be32
> >  nfsd4_decode_copy(struct nfsd4_compoundargs *argp, struct nfsd4_copy *copy)
> >  {
> >       DECODE_HEAD;
> > -     unsigned int tmp;
> > +     struct nl4_server *ns;
> > +     int i, count;
> >
> >       status = nfsd4_decode_stateid(argp, &copy->cp_src_stateid);
> >       if (status)
> > @@ -1762,8 +1810,24 @@ static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, str
> >       p = xdr_decode_hyper(p, &copy->cp_count);
> >       p++; /* ca_consecutive: we always do consecutive copies */
> >       copy->cp_synchronous = be32_to_cpup(p++);
> > -     tmp = be32_to_cpup(p); /* Source server list not supported */
> > +     count = be32_to_cpup(p++);
> > +
> > +     if (count == 0) /* intra-server copy */
> > +             goto intra;
> >
> > +     /* decode all the supplied server addresses but use first */
> > +     copy->cp_src = kmalloc(count * sizeof(struct nl4_server), GFP_KERNEL);
> > +     if (copy->cp_src == NULL)
> > +             return nfserrno(-ENOMEM);
> > +
> > +     ns = copy->cp_src;
> > +     for (i = 0; i < count; i++) {
> > +             status = nfsd4_decode_nl4_server(argp, ns);
> > +             if (status)
> > +                     return status;
> > +             ns++;
> > +     }
> > +intra:
> >       DECODE_TAIL;
> >  }
> >
> > @@ -4273,6 +4337,9 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
> >       p = xdr_reserve_space(&resp->xdr, 4 + 4);
> >       *p++ = xdr_one; /* cr_consecutive */
> >       *p++ = cpu_to_be32(copy->cp_synchronous);
> > +
> > +     /* allocated in nfsd4_decode_copy */
> > +     kfree(copy->cp_src);
> >       return 0;
> >  }
> >
> > diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
> > index feeb6d4..b4d1140 100644
> > --- a/fs/nfsd/xdr4.h
> > +++ b/fs/nfsd/xdr4.h
> > @@ -521,6 +521,7 @@ struct nfsd4_copy {
> >       u64             cp_src_pos;
> >       u64             cp_dst_pos;
> >       u64             cp_count;
> > +     struct nl4_server *cp_src;
> >
> >       /* both */
> >       bool            cp_synchronous;
> > --
> > 1.8.3.1
J. Bruce Fields Nov. 2, 2018, 1:53 p.m. UTC | #3
On Thu, Nov 01, 2018 at 05:00:46PM -0400, Olga Kornievskaia wrote:
> On Thu, Nov 1, 2018 at 4:48 PM J. Bruce Fields <bfields@fieldses.org> wrote:
> >
> > On Fri, Oct 19, 2018 at 11:28:59AM -0400, Olga Kornievskaia wrote:
> > > Note: followed conventions and have struct nfsd4_compoundargs pointer as a
> > > parameter even though it is unused.
> >
> > It's used--see the definition of READ_BUF.
> 
> Sigh. It's a leftover comment from Andy. I'll remove it.
> 
> > (I'm not a fan of those macros and they'll probably be replaced some
> > day.)
> 
> READ_BUF is used thru out so I'm assuming you are not against this
> patch using it, correct?

Correct, replacing those macros is a project for another day.

--b.

> 
> >
> > --b.
> >
> > >
> > > Signed-off-by: Andy Adamson <andros@netapp.com>
> > > Signed-off-by: Olga Kornievskaia <kolga@netapp.com>
> > > ---
> > >  fs/nfsd/nfs4xdr.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
> > >  fs/nfsd/xdr4.h    |  1 +
> > >  2 files changed, 70 insertions(+), 2 deletions(-)
> > >
> > > diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> > > index 3de42a7..9f6886f 100644
> > > --- a/fs/nfsd/nfs4xdr.c
> > > +++ b/fs/nfsd/nfs4xdr.c
> > > @@ -40,6 +40,7 @@
> > >  #include <linux/utsname.h>
> > >  #include <linux/pagemap.h>
> > >  #include <linux/sunrpc/svcauth_gss.h>
> > > +#include <linux/sunrpc/addr.h>
> > >
> > >  #include "idmap.h"
> > >  #include "acl.h"
> > > @@ -1743,11 +1744,58 @@ static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, str
> > >       DECODE_TAIL;
> > >  }
> > >
> > > +static __be32 nfsd4_decode_nl4_server(struct nfsd4_compoundargs *argp,
> > > +                                   struct nl4_server *ns)
> > > +{
> > > +     DECODE_HEAD;
> > > +     struct nfs42_netaddr *naddr;
> > > +
> > > +     READ_BUF(4);
> > > +     ns->nl4_type = be32_to_cpup(p++);
> > > +
> > > +     /* currently support for 1 inter-server source server */
> > > +     switch (ns->nl4_type) {
> > > +     case NL4_NAME:
> > > +     case NL4_URL:
> > > +             READ_BUF(4);
> > > +             ns->u.nl4_str_sz = be32_to_cpup(p++);
> > > +             if (ns->u.nl4_str_sz > NFS4_OPAQUE_LIMIT)
> > > +                     goto xdr_error;
> > > +
> > > +             READ_BUF(ns->u.nl4_str_sz);
> > > +             COPYMEM(ns->u.nl4_str,
> > > +                     ns->u.nl4_str_sz);
> > > +             break;
> > > +     case NL4_NETADDR:
> > > +             naddr = &ns->u.nl4_addr;
> > > +
> > > +             READ_BUF(4);
> > > +             naddr->netid_len = be32_to_cpup(p++);
> > > +             if (naddr->netid_len > RPCBIND_MAXNETIDLEN)
> > > +                     goto xdr_error;
> > > +
> > > +             READ_BUF(naddr->netid_len + 4); /* 4 for uaddr len */
> > > +             COPYMEM(naddr->netid, naddr->netid_len);
> > > +
> > > +             naddr->addr_len = be32_to_cpup(p++);
> > > +             if (naddr->addr_len > RPCBIND_MAXUADDRLEN)
> > > +                     goto xdr_error;
> > > +
> > > +             READ_BUF(naddr->addr_len);
> > > +             COPYMEM(naddr->addr, naddr->addr_len);
> > > +             break;
> > > +     default:
> > > +             goto xdr_error;
> > > +     }
> > > +     DECODE_TAIL;
> > > +}
> > > +
> > >  static __be32
> > >  nfsd4_decode_copy(struct nfsd4_compoundargs *argp, struct nfsd4_copy *copy)
> > >  {
> > >       DECODE_HEAD;
> > > -     unsigned int tmp;
> > > +     struct nl4_server *ns;
> > > +     int i, count;
> > >
> > >       status = nfsd4_decode_stateid(argp, &copy->cp_src_stateid);
> > >       if (status)
> > > @@ -1762,8 +1810,24 @@ static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, str
> > >       p = xdr_decode_hyper(p, &copy->cp_count);
> > >       p++; /* ca_consecutive: we always do consecutive copies */
> > >       copy->cp_synchronous = be32_to_cpup(p++);
> > > -     tmp = be32_to_cpup(p); /* Source server list not supported */
> > > +     count = be32_to_cpup(p++);
> > > +
> > > +     if (count == 0) /* intra-server copy */
> > > +             goto intra;
> > >
> > > +     /* decode all the supplied server addresses but use first */
> > > +     copy->cp_src = kmalloc(count * sizeof(struct nl4_server), GFP_KERNEL);
> > > +     if (copy->cp_src == NULL)
> > > +             return nfserrno(-ENOMEM);
> > > +
> > > +     ns = copy->cp_src;
> > > +     for (i = 0; i < count; i++) {
> > > +             status = nfsd4_decode_nl4_server(argp, ns);
> > > +             if (status)
> > > +                     return status;
> > > +             ns++;
> > > +     }
> > > +intra:
> > >       DECODE_TAIL;
> > >  }
> > >
> > > @@ -4273,6 +4337,9 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
> > >       p = xdr_reserve_space(&resp->xdr, 4 + 4);
> > >       *p++ = xdr_one; /* cr_consecutive */
> > >       *p++ = cpu_to_be32(copy->cp_synchronous);
> > > +
> > > +     /* allocated in nfsd4_decode_copy */
> > > +     kfree(copy->cp_src);
> > >       return 0;
> > >  }
> > >
> > > diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
> > > index feeb6d4..b4d1140 100644
> > > --- a/fs/nfsd/xdr4.h
> > > +++ b/fs/nfsd/xdr4.h
> > > @@ -521,6 +521,7 @@ struct nfsd4_copy {
> > >       u64             cp_src_pos;
> > >       u64             cp_dst_pos;
> > >       u64             cp_count;
> > > +     struct nl4_server *cp_src;
> > >
> > >       /* both */
> > >       bool            cp_synchronous;
> > > --
> > > 1.8.3.1
J. Bruce Fields Nov. 2, 2018, 2:03 p.m. UTC | #4
On Fri, Oct 19, 2018 at 11:28:59AM -0400, Olga Kornievskaia wrote:
> @@ -1762,8 +1810,24 @@ static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, str
>  	p = xdr_decode_hyper(p, &copy->cp_count);
>  	p++; /* ca_consecutive: we always do consecutive copies */
>  	copy->cp_synchronous = be32_to_cpup(p++);
> -	tmp = be32_to_cpup(p); /* Source server list not supported */
> +	count = be32_to_cpup(p++);
> +
> +	if (count == 0) /* intra-server copy */
> +		goto intra;
>  
> +	/* decode all the supplied server addresses but use first */
> +	copy->cp_src = kmalloc(count * sizeof(struct nl4_server), GFP_KERNEL);

The client could pass an arbitrarily large count here.  I don't know if
the ability to force the server to attempt a large kmalloc() is really
useful to an attacker, but I'd definitely rather not allow it.

Possibly more serious: if that multiplication overflows, then in theory
it might be possible to make the kmalloc() succeed and allocate too
little memory, after which the following loop could overwrite memory
past the end of the allocation.

As long as we're only using the first address, maybe it's simplest just
not to bother allocating memory for the rest.  Just copy the first one,
and for the rest call nfsd4_decode_nl4_server() with a dummy struct
nl4_server.

--b.

> +	if (copy->cp_src == NULL)
> +		return nfserrno(-ENOMEM);
> +
> +	ns = copy->cp_src;
> +	for (i = 0; i < count; i++) {
> +		status = nfsd4_decode_nl4_server(argp, ns);
> +		if (status)
> +			return status;
> +		ns++;
> +	}
> +intra:
>  	DECODE_TAIL;
>  }
>  
> @@ -4273,6 +4337,9 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
>  	p = xdr_reserve_space(&resp->xdr, 4 + 4);
>  	*p++ = xdr_one; /* cr_consecutive */
>  	*p++ = cpu_to_be32(copy->cp_synchronous);
> +
> +	/* allocated in nfsd4_decode_copy */
> +	kfree(copy->cp_src);
>  	return 0;
>  }
>  
> diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
> index feeb6d4..b4d1140 100644
> --- a/fs/nfsd/xdr4.h
> +++ b/fs/nfsd/xdr4.h
> @@ -521,6 +521,7 @@ struct nfsd4_copy {
>  	u64		cp_src_pos;
>  	u64		cp_dst_pos;
>  	u64		cp_count;
> +	struct nl4_server *cp_src;
>  
>  	/* both */
>  	bool		cp_synchronous;
> -- 
> 1.8.3.1
J. Bruce Fields Nov. 2, 2018, 3:46 p.m. UTC | #5
On Fri, Oct 19, 2018 at 11:28:59AM -0400, Olga Kornievskaia wrote:
> @@ -4273,6 +4337,9 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
>  	p = xdr_reserve_space(&resp->xdr, 4 + 4);
>  	*p++ = xdr_one; /* cr_consecutive */
>  	*p++ = cpu_to_be32(copy->cp_synchronous);
> +
> +	/* allocated in nfsd4_decode_copy */
> +	kfree(copy->cp_src);

This can result in a leak--for example, if we decode the compound
succesfully, but processing fails before we could to this op, then we'll
never call this encoder, so we'll allocate without freeing.

I think simplest would be to replace this:

> diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
> index feeb6d4..b4d1140 100644
> --- a/fs/nfsd/xdr4.h
> +++ b/fs/nfsd/xdr4.h
> @@ -521,6 +521,7 @@ struct nfsd4_copy {
>  	u64		cp_src_pos;
>  	u64		cp_dst_pos;
>  	u64		cp_count;
> +	struct nl4_server *cp_src;

by just a

	struct nl4_server cp_src;

since it sounds like you really only need one of them, not a whole array
(at least for now).

--b.

>  
>  	/* both */
>  	bool		cp_synchronous;
> -- 
> 1.8.3.1
Olga Kornievskaia Nov. 2, 2018, 4:35 p.m. UTC | #6
On Fri, Nov 2, 2018 at 11:46 AM J. Bruce Fields <bfields@fieldses.org> wrote:
>
> On Fri, Oct 19, 2018 at 11:28:59AM -0400, Olga Kornievskaia wrote:
> > @@ -4273,6 +4337,9 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
> >       p = xdr_reserve_space(&resp->xdr, 4 + 4);
> >       *p++ = xdr_one; /* cr_consecutive */
> >       *p++ = cpu_to_be32(copy->cp_synchronous);
> > +
> > +     /* allocated in nfsd4_decode_copy */
> > +     kfree(copy->cp_src);
>
> This can result in a leak--for example, if we decode the compound
> succesfully, but processing fails before we could to this op, then we'll
> never call this encoder, so we'll allocate without freeing.
>
> I think simplest would be to replace this:
>
> > diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
> > index feeb6d4..b4d1140 100644
> > --- a/fs/nfsd/xdr4.h
> > +++ b/fs/nfsd/xdr4.h
> > @@ -521,6 +521,7 @@ struct nfsd4_copy {
> >       u64             cp_src_pos;
> >       u64             cp_dst_pos;
> >       u64             cp_count;
> > +     struct nl4_server *cp_src;
>
> by just a
>
>         struct nl4_server cp_src;
>
> since it sounds like you really only need one of them, not a whole array
> (at least for now).

So this is problematic as the presence of this memory is what is used
to distinguish "inter" from "intra".

Can things really fail between the xdr and calling of the operation?

What gets freed in the encoder is the "copy" of the what was decoded
in the decoder. But really freeing in the encoder is the wrong place.
Encoder doesn't need to free. I already free the "copy" of the
copy->cp_src in the cleanup_async_copy(). However, what is missing is
freeing the original copy->cp_src which needs to be freed in the
dup_copy_fields().

To clarify:
copy->cp_src gets allocated in the decoder
during the process of the copy:
1. it gets copied to the kthread and the original copy->cp_src needs
to be freed. Or during any error it will be freed.
2. cleanup_async_copy frees the copy of the copy->cp_src.
(need to remove the kfree from the encoder).

>
> --b.
>
> >
> >       /* both */
> >       bool            cp_synchronous;
> > --
> > 1.8.3.1
Olga Kornievskaia Nov. 2, 2018, 4:36 p.m. UTC | #7
On Fri, Nov 2, 2018 at 10:04 AM J. Bruce Fields <bfields@fieldses.org> wrote:
>
> On Fri, Oct 19, 2018 at 11:28:59AM -0400, Olga Kornievskaia wrote:
> > @@ -1762,8 +1810,24 @@ static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, str
> >       p = xdr_decode_hyper(p, &copy->cp_count);
> >       p++; /* ca_consecutive: we always do consecutive copies */
> >       copy->cp_synchronous = be32_to_cpup(p++);
> > -     tmp = be32_to_cpup(p); /* Source server list not supported */
> > +     count = be32_to_cpup(p++);
> > +
> > +     if (count == 0) /* intra-server copy */
> > +             goto intra;
> >
> > +     /* decode all the supplied server addresses but use first */
> > +     copy->cp_src = kmalloc(count * sizeof(struct nl4_server), GFP_KERNEL);
>
> The client could pass an arbitrarily large count here.  I don't know if
> the ability to force the server to attempt a large kmalloc() is really
> useful to an attacker, but I'd definitely rather not allow it.
>
> Possibly more serious: if that multiplication overflows, then in theory
> it might be possible to make the kmalloc() succeed and allocate too
> little memory, after which the following loop could overwrite memory
> past the end of the allocation.
>
> As long as we're only using the first address, maybe it's simplest just
> not to bother allocating memory for the rest.  Just copy the first one,
> and for the rest call nfsd4_decode_nl4_server() with a dummy struct
> nl4_server.

Ok will try.

>
> --b.
>
> > +     if (copy->cp_src == NULL)
> > +             return nfserrno(-ENOMEM);
> > +
> > +     ns = copy->cp_src;
> > +     for (i = 0; i < count; i++) {
> > +             status = nfsd4_decode_nl4_server(argp, ns);
> > +             if (status)
> > +                     return status;
> > +             ns++;
> > +     }
> > +intra:
> >       DECODE_TAIL;
> >  }
> >
> > @@ -4273,6 +4337,9 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
> >       p = xdr_reserve_space(&resp->xdr, 4 + 4);
> >       *p++ = xdr_one; /* cr_consecutive */
> >       *p++ = cpu_to_be32(copy->cp_synchronous);
> > +
> > +     /* allocated in nfsd4_decode_copy */
> > +     kfree(copy->cp_src);
> >       return 0;
> >  }
> >
> > diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
> > index feeb6d4..b4d1140 100644
> > --- a/fs/nfsd/xdr4.h
> > +++ b/fs/nfsd/xdr4.h
> > @@ -521,6 +521,7 @@ struct nfsd4_copy {
> >       u64             cp_src_pos;
> >       u64             cp_dst_pos;
> >       u64             cp_count;
> > +     struct nl4_server *cp_src;
> >
> >       /* both */
> >       bool            cp_synchronous;
> > --
> > 1.8.3.1
J. Bruce Fields Nov. 2, 2018, 4:49 p.m. UTC | #8
On Fri, Nov 02, 2018 at 12:35:26PM -0400, Olga Kornievskaia wrote:
> On Fri, Nov 2, 2018 at 11:46 AM J. Bruce Fields <bfields@fieldses.org> wrote:
> >
> > On Fri, Oct 19, 2018 at 11:28:59AM -0400, Olga Kornievskaia wrote:
> > > @@ -4273,6 +4337,9 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
> > >       p = xdr_reserve_space(&resp->xdr, 4 + 4);
> > >       *p++ = xdr_one; /* cr_consecutive */
> > >       *p++ = cpu_to_be32(copy->cp_synchronous);
> > > +
> > > +     /* allocated in nfsd4_decode_copy */
> > > +     kfree(copy->cp_src);
> >
> > This can result in a leak--for example, if we decode the compound
> > succesfully, but processing fails before we could to this op, then we'll
> > never call this encoder, so we'll allocate without freeing.
> >
> > I think simplest would be to replace this:
> >
> > > diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
> > > index feeb6d4..b4d1140 100644
> > > --- a/fs/nfsd/xdr4.h
> > > +++ b/fs/nfsd/xdr4.h
> > > @@ -521,6 +521,7 @@ struct nfsd4_copy {
> > >       u64             cp_src_pos;
> > >       u64             cp_dst_pos;
> > >       u64             cp_count;
> > > +     struct nl4_server *cp_src;
> >
> > by just a
> >
> >         struct nl4_server cp_src;
> >
> > since it sounds like you really only need one of them, not a whole array
> > (at least for now).
> 
> So this is problematic as the presence of this memory is what is used
> to distinguish "inter" from "intra".

It would be easy enough to add a new bit for that.

> Can things really fail between the xdr and calling of the operation?

Yes.  Consider a PUTFH+SAVEFH+COPY compound.  We decode the whole thing
before starting any processing.  Then we call nfsd4_putfh() and
fh_verify fails (maybe the filehandle is stale or something).  Then
neither nfsd4_copy() nor nfsd4_encode_copy() will be called.

If you absolutely have to do this, you can look at SAVEMEM.

--b.

> What gets freed in the encoder is the "copy" of the what was decoded
> in the decoder. But really freeing in the encoder is the wrong place.
> Encoder doesn't need to free. I already free the "copy" of the
> copy->cp_src in the cleanup_async_copy(). However, what is missing is
> freeing the original copy->cp_src which needs to be freed in the
> dup_copy_fields().
> 
> To clarify:
> copy->cp_src gets allocated in the decoder
> during the process of the copy:
> 1. it gets copied to the kthread and the original copy->cp_src needs
> to be freed. Or during any error it will be freed.
> 2. cleanup_async_copy frees the copy of the copy->cp_src.
> (need to remove the kfree from the encoder).
> 
> >
> > --b.
> >
> > >
> > >       /* both */
> > >       bool            cp_synchronous;
> > > --
> > > 1.8.3.1
Olga Kornievskaia Nov. 2, 2018, 5:04 p.m. UTC | #9
On Fri, Nov 2, 2018 at 12:49 PM J. Bruce Fields <bfields@fieldses.org> wrote:
>
> On Fri, Nov 02, 2018 at 12:35:26PM -0400, Olga Kornievskaia wrote:
> > On Fri, Nov 2, 2018 at 11:46 AM J. Bruce Fields <bfields@fieldses.org> wrote:
> > >
> > > On Fri, Oct 19, 2018 at 11:28:59AM -0400, Olga Kornievskaia wrote:
> > > > @@ -4273,6 +4337,9 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
> > > >       p = xdr_reserve_space(&resp->xdr, 4 + 4);
> > > >       *p++ = xdr_one; /* cr_consecutive */
> > > >       *p++ = cpu_to_be32(copy->cp_synchronous);
> > > > +
> > > > +     /* allocated in nfsd4_decode_copy */
> > > > +     kfree(copy->cp_src);
> > >
> > > This can result in a leak--for example, if we decode the compound
> > > succesfully, but processing fails before we could to this op, then we'll
> > > never call this encoder, so we'll allocate without freeing.
> > >
> > > I think simplest would be to replace this:
> > >
> > > > diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
> > > > index feeb6d4..b4d1140 100644
> > > > --- a/fs/nfsd/xdr4.h
> > > > +++ b/fs/nfsd/xdr4.h
> > > > @@ -521,6 +521,7 @@ struct nfsd4_copy {
> > > >       u64             cp_src_pos;
> > > >       u64             cp_dst_pos;
> > > >       u64             cp_count;
> > > > +     struct nl4_server *cp_src;
> > >
> > > by just a
> > >
> > >         struct nl4_server cp_src;
> > >
> > > since it sounds like you really only need one of them, not a whole array
> > > (at least for now).
> >
> > So this is problematic as the presence of this memory is what is used
> > to distinguish "inter" from "intra".
>
> It would be easy enough to add a new bit for that.
>
> > Can things really fail between the xdr and calling of the operation?
>
> Yes.  Consider a PUTFH+SAVEFH+COPY compound.  We decode the whole thing
> before starting any processing.  Then we call nfsd4_putfh() and
> fh_verify fails (maybe the filehandle is stale or something).  Then
> neither nfsd4_copy() nor nfsd4_encode_copy() will be called.
>
> If you absolutely have to do this, you can look at SAVEMEM.

Using a new bit is good enough for me.

>
> --b.
>
> > What gets freed in the encoder is the "copy" of the what was decoded
> > in the decoder. But really freeing in the encoder is the wrong place.
> > Encoder doesn't need to free. I already free the "copy" of the
> > copy->cp_src in the cleanup_async_copy(). However, what is missing is
> > freeing the original copy->cp_src which needs to be freed in the
> > dup_copy_fields().
> >
> > To clarify:
> > copy->cp_src gets allocated in the decoder
> > during the process of the copy:
> > 1. it gets copied to the kthread and the original copy->cp_src needs
> > to be freed. Or during any error it will be freed.
> > 2. cleanup_async_copy frees the copy of the copy->cp_src.
> > (need to remove the kfree from the encoder).
> >
> > >
> > > --b.
> > >
> > > >
> > > >       /* both */
> > > >       bool            cp_synchronous;
> > > > --
> > > > 1.8.3.1
diff mbox series

Patch

diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 3de42a7..9f6886f 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -40,6 +40,7 @@ 
 #include <linux/utsname.h>
 #include <linux/pagemap.h>
 #include <linux/sunrpc/svcauth_gss.h>
+#include <linux/sunrpc/addr.h>
 
 #include "idmap.h"
 #include "acl.h"
@@ -1743,11 +1744,58 @@  static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, str
 	DECODE_TAIL;
 }
 
+static __be32 nfsd4_decode_nl4_server(struct nfsd4_compoundargs *argp,
+				      struct nl4_server *ns)
+{
+	DECODE_HEAD;
+	struct nfs42_netaddr *naddr;
+
+	READ_BUF(4);
+	ns->nl4_type = be32_to_cpup(p++);
+
+	/* currently support for 1 inter-server source server */
+	switch (ns->nl4_type) {
+	case NL4_NAME:
+	case NL4_URL:
+		READ_BUF(4);
+		ns->u.nl4_str_sz = be32_to_cpup(p++);
+		if (ns->u.nl4_str_sz > NFS4_OPAQUE_LIMIT)
+			goto xdr_error;
+
+		READ_BUF(ns->u.nl4_str_sz);
+		COPYMEM(ns->u.nl4_str,
+			ns->u.nl4_str_sz);
+		break;
+	case NL4_NETADDR:
+		naddr = &ns->u.nl4_addr;
+
+		READ_BUF(4);
+		naddr->netid_len = be32_to_cpup(p++);
+		if (naddr->netid_len > RPCBIND_MAXNETIDLEN)
+			goto xdr_error;
+
+		READ_BUF(naddr->netid_len + 4); /* 4 for uaddr len */
+		COPYMEM(naddr->netid, naddr->netid_len);
+
+		naddr->addr_len = be32_to_cpup(p++);
+		if (naddr->addr_len > RPCBIND_MAXUADDRLEN)
+			goto xdr_error;
+
+		READ_BUF(naddr->addr_len);
+		COPYMEM(naddr->addr, naddr->addr_len);
+		break;
+	default:
+		goto xdr_error;
+	}
+	DECODE_TAIL;
+}
+
 static __be32
 nfsd4_decode_copy(struct nfsd4_compoundargs *argp, struct nfsd4_copy *copy)
 {
 	DECODE_HEAD;
-	unsigned int tmp;
+	struct nl4_server *ns;
+	int i, count;
 
 	status = nfsd4_decode_stateid(argp, &copy->cp_src_stateid);
 	if (status)
@@ -1762,8 +1810,24 @@  static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, str
 	p = xdr_decode_hyper(p, &copy->cp_count);
 	p++; /* ca_consecutive: we always do consecutive copies */
 	copy->cp_synchronous = be32_to_cpup(p++);
-	tmp = be32_to_cpup(p); /* Source server list not supported */
+	count = be32_to_cpup(p++);
+
+	if (count == 0) /* intra-server copy */
+		goto intra;
 
+	/* decode all the supplied server addresses but use first */
+	copy->cp_src = kmalloc(count * sizeof(struct nl4_server), GFP_KERNEL);
+	if (copy->cp_src == NULL)
+		return nfserrno(-ENOMEM);
+
+	ns = copy->cp_src;
+	for (i = 0; i < count; i++) {
+		status = nfsd4_decode_nl4_server(argp, ns);
+		if (status)
+			return status;
+		ns++;
+	}
+intra:
 	DECODE_TAIL;
 }
 
@@ -4273,6 +4337,9 @@  static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
 	p = xdr_reserve_space(&resp->xdr, 4 + 4);
 	*p++ = xdr_one; /* cr_consecutive */
 	*p++ = cpu_to_be32(copy->cp_synchronous);
+
+	/* allocated in nfsd4_decode_copy */
+	kfree(copy->cp_src);
 	return 0;
 }
 
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index feeb6d4..b4d1140 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -521,6 +521,7 @@  struct nfsd4_copy {
 	u64		cp_src_pos;
 	u64		cp_dst_pos;
 	u64		cp_count;
+	struct nl4_server *cp_src;
 
 	/* both */
 	bool		cp_synchronous;