Hi Jerome,

Thanks for the suggestion. Yes I think your fix is more robust. Do you want
me to give another patch here?

Best,
Murtaza

On Thu, Jul 23, 2026 at 8:10 AM Jerome Forissier <[email protected]>
wrote:

> Hi Murtaza,
>
> On 22/07/2026 22:47, Murtaza Munaim wrote:
> > nfs_read_reply() reads the NFS READ reply count into a signed int rlen
> > via ntohl() and then bounds-checks it before passing it to store_block()
> > as an unsigned length:
> >
> >       if (((uchar *)&rpc_pkt.u.reply.data[0] - (uchar *)&rpc_pkt + rlen)
> > len)
> >               return -9999;
> >       if (store_block(data_ptr, nfs_offset, rlen))
> >               return -9999;
> >
> > The left-hand side of the check is a pointer difference (ptrdiff_t) plus
> > the signed rlen, compared against the unsigned int len. A malicious NFS
> > server can set the READ count field to a value with the top bit set, so
> > that rlen becomes negative.
> >
> > On ILP32 targets the pointer difference is a 32-bit signed value and the
> > comparison against unsigned len wraps the negative sum to a large
> > unsigned quantity, so the check triggers and the reply is rejected. On
> > LP64 targets the pointer difference is a 64-bit ptrdiff_t and len
> > promotes to signed long; the sum stays negative, the check passes, and
> > the negative rlen is then converted to a ~2-4 GB size_t inside
> > store_block(), where memcpy() copies far past the packet buffer and the
> > image load address. The commit that added this check only sanitised the
> > length on ILP32.
> >
> > Reject negative rlen explicitly before the existing check so it is sound
> > on all targets.
> >
> > Fixes: aa207cf3a6d6 ("CVE-2019-14194/CVE-2019-14198: nfs: fix unbounded
> memcpy with a failed length check at nfs_read_reply")
> > Signed-off-by: Murtaza Munaim <[email protected]>
> > ---
> >  net/nfs-common.c | 11 +++++++++++
> >  1 file changed, 11 insertions(+)
> >
> > diff --git a/net/nfs-common.c b/net/nfs-common.c
> > index 637fcfd9bb8..111b95459c6 100644
> > --- a/net/nfs-common.c
> > +++ b/net/nfs-common.c
> > @@ -744,6 +744,17 @@ static int nfs_read_reply(uchar *pkt, unsigned int
> len)
> >                       &rpc_pkt.u.reply.data[4 + nfsv3_data_offset];
> >       }
> >
> > +     /*
> > +      * rlen is read from the wire and used below as an unsigned length
> > +      * for store_block()/memcpy(). The length check that follows
> compares
> > +      * a pointer difference (ptrdiff_t) plus rlen against len; on LP64
> a
> > +      * negative rlen keeps the left-hand side negative, so the check
> > +      * passes and rlen wraps to a huge size_t in store_block(). Reject
> > +      * negative values explicitly so the check is sound on all targets.
> > +      */
> > +     if (rlen < 0)
> > +             return -9999;
> > +
> >       if (((uchar *)&rpc_pkt.u.reply.data[0] - (uchar *)&rpc_pkt + rlen)
> > len)
> >               return -9999;
>
> Is this correct? Shouldn't data_ptr be used instead? How about:
>
> diff --git a/net/nfs-common.c b/net/nfs-common.c
> index 72d8fd823e33..34b276517987 100644
> --- a/net/nfs-common.c
> +++ b/net/nfs-common.c
> @@ -694,8 +694,9 @@ static int nfs_readlink_reply(uchar *pkt, unsigned int
> len)
>  static int nfs_read_reply(uchar *pkt, unsigned int len)
>  {
>         struct rpc_t rpc_pkt;
> -       int rlen;
> +       unsigned int rlen;
>         uchar *data_ptr;
> +       size_t data_offset;
>
>         memcpy(&rpc_pkt.u.data[0], pkt, sizeof(rpc_pkt.u.reply));
>
> @@ -738,7 +739,8 @@ static int nfs_read_reply(uchar *pkt, unsigned int len)
>                         &rpc_pkt.u.reply.data[4 + nfsv3_data_offset];
>         }
>
> -       if (((uchar *)&rpc_pkt.u.reply.data[0] - (uchar *)&rpc_pkt + rlen)
> > len)
> +       data_offset = data_ptr - (uchar *)&rpc_pkt;
> +       if (data_offset > len || rlen > len - data_offset)
>                 return -9999;
>
>         if (store_block(data_ptr, nfs_offset, rlen))
>
>
> Thanks,
> --
> Jerome
>
>

Reply via email to