nfs_read_reply() stores the server-supplied read length in a signed int
rlen and checks it with:

        if (((uchar *)&rpc_pkt.u.reply.data[0] - (uchar *)&rpc_pkt + rlen) > 
len)
                return -9999;

On an LP64 target the pointer subtraction is a 64-bit ptrdiff_t, so a
length with the top bit set makes rlen negative, the sum stays negative
and the check passes. store_block() then takes rlen as an unsigned int,
so 0x80000000 becomes a ~2 GB length. memcpy() reads past the 1152-byte
rpc_pkt stack buffer and writes past image_load_addr.

A large positive rlen is also unsafe. The check bounds it by the packet
length rather than by rpc_pkt, so on the NFSv3 path (data_ptr at offset
128) an rlen up to 1128 still reads past the end of rpc_pkt.

Bound the length by NFS_READ_SIZE, the amount a read ever requests,
before it is used. Both the classic and the lwIP NFS clients reach this
through nfs_pkt_recv(), so the single check covers both.

Fixes: aa207cf3a6d6 ("CVE-2019-14194/CVE-2019-14198: nfs: fix unbounded memcpy 
with a failed length check at nfs_read_reply")
Signed-off-by: Shahriyar Jalayeri <[email protected]>
---
 net/nfs-common.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/net/nfs-common.c b/net/nfs-common.c
index 72d8fd823e3..020b0185ad1 100644
--- a/net/nfs-common.c
+++ b/net/nfs-common.c
@@ -738,6 +738,10 @@ static int nfs_read_reply(uchar *pkt, unsigned int len)
                        &rpc_pkt.u.reply.data[4 + nfsv3_data_offset];
        }
 
+       /* reject a negative or too-large length */
+       if (rlen < 0 || rlen > NFS_READ_SIZE)
+               return -9999;
+
        if (((uchar *)&rpc_pkt.u.reply.data[0] - (uchar *)&rpc_pkt + rlen) > 
len)
                return -9999;
 

-- 
2.43.0

Reply via email to