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