diff mbox series

nfsd: prevent integer overflow in decode_cb_compound4res()

Message ID 65cdbbab-7dca-4a73-af07-29863ab8f526@stanley.mountain (mailing list archive)
State New
Headers show
Series nfsd: prevent integer overflow in decode_cb_compound4res() | expand

Commit Message

Dan Carpenter Sept. 16, 2024, 3:14 p.m. UTC
If "length" is >= U32_MAX - 3 then the "length + 4" addition can result
in an integer overflow.  The impact of this bug is not totally clear to
me, but it's safer to not allow the integer overflow.

There is also some math in xdr_inline_decode() which could overflow, so
really it's ">= U32_MAX - 7" which is problematic.  Let's just check
against INT_MAX and make it easy.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
---
 fs/nfsd/nfs4callback.c | 2 ++
 1 file changed, 2 insertions(+)

Comments

Chuck Lever Sept. 16, 2024, 3:44 p.m. UTC | #1
On Mon, Sep 16, 2024 at 06:14:15PM +0300, Dan Carpenter wrote:
> If "length" is >= U32_MAX - 3 then the "length + 4" addition can result
> in an integer overflow.  The impact of this bug is not totally clear to
> me, but it's safer to not allow the integer overflow.
> 
> There is also some math in xdr_inline_decode() which could overflow, so
> really it's ">= U32_MAX - 7" which is problematic.  Let's just check
> against INT_MAX and make it easy.
> 
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
> ---
>  fs/nfsd/nfs4callback.c | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
> index 43b8320c8255..12b44c9246d1 100644
> --- a/fs/nfsd/nfs4callback.c
> +++ b/fs/nfsd/nfs4callback.c
> @@ -317,6 +317,8 @@ static int decode_cb_compound4res(struct xdr_stream *xdr,
>  	hdr->status = be32_to_cpup(p++);
>  	/* Ignore the tag */
>  	length = be32_to_cpup(p++);
> +	if (unlikely(length > INT_MAX))
> +		goto out_overflow;

I think we assume (wrongly) that xdr_inline_decode() will kick
back any length request that is larger than the xdr_stream's
xdr_buf. Here, the test could be the same: any @length value that is
larger than xdr->buf->len is bogus.

Another way to avoid the overflow would be to split the decode of
the tag and the following op count field. Untested:

        length = be32_to_cpup(p);
        p = xdr_inline_decode(xdr, length);
        if (unlikely(p == NULL))
                goto out_overflow;
	if (xdr_stream_decode_u32(xdr, &hdr->nops) < 0)
                goto out_overflow;

I have generally been using this approach in other NFSD XDR decoding
functions.

I wonder how many other xdr_inline_decode() call sites have a
similar issue.


>  	p = xdr_inline_decode(xdr, length + 4);
>  	if (unlikely(p == NULL))
>  		goto out_overflow;
> -- 
> 2.45.2
>
diff mbox series

Patch

diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index 43b8320c8255..12b44c9246d1 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -317,6 +317,8 @@  static int decode_cb_compound4res(struct xdr_stream *xdr,
 	hdr->status = be32_to_cpup(p++);
 	/* Ignore the tag */
 	length = be32_to_cpup(p++);
+	if (unlikely(length > INT_MAX))
+		goto out_overflow;
 	p = xdr_inline_decode(xdr, length + 4);
 	if (unlikely(p == NULL))
 		goto out_overflow;