nfs_readlink_reply() reads the symlink length from the server into a signed
int rlen and bounds it with
if (((uchar *)&rpc_pkt.u.reply.data[0] - (uchar *)&rpc_pkt + rlen) >
len)
return -NFS_RPC_DROP;
This misses two cases. A negative rlen makes the sum smaller than len, so the
check passes; rlen is then used as an unsigned size_t in memcpy(), and in the
relative-symlink branch pathlen + rlen also stays below the buffer size, so a
length of -1 drives a memcpy() off nfs_path_buff. The bound is also measured
from the reply header rather than from the symlink data, which begins a few
words later, so a large positive rlen reads past the end of the received
reply.
A malicious server answers the READ with an ISDIR status to move the client
into the readlink state, then returns such a reply.
Reject a negative length and measure the bound from the symlink data.
Fixes: cf3a4f1e86ec ("CVE-2019-14195: nfs: fix unbounded memcpy with
unvalidated length at nfs_readlink_reply")
Signed-off-by: Shahriyar Jalayeri <[email protected]>
---
net/nfs-common.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/net/nfs-common.c b/net/nfs-common.c
index 020b0185ad1..6a536cd5229 100644
--- a/net/nfs-common.c
+++ b/net/nfs-common.c
@@ -666,7 +666,10 @@ static int nfs_readlink_reply(uchar *pkt, unsigned int len)
/* new path length */
rlen = ntohl(rpc_pkt.u.reply.data[1 + nfsv3_data_offset]);
- if (((uchar *)&rpc_pkt.u.reply.data[0] - (uchar *)&rpc_pkt + rlen) >
len)
+ /* reject a negative length or one that runs past the packet */
+ if (rlen < 0 ||
+ ((uchar *)&rpc_pkt.u.reply.data[2 + nfsv3_data_offset] -
+ (uchar *)&rpc_pkt + rlen) > len)
return -NFS_RPC_DROP;
if (*((char *)&rpc_pkt.u.reply.data[2 + nfsv3_data_offset]) != '/') {
--
2.43.0