"rpc mount export: RPC: Can't decode result" when export list is to long

Andrey Simonenko simon at comsys.ntu-kpi.kiev.ua
Mon Jan 31 13:45:28 UTC 2011


On Sat, Jan 29, 2011 at 03:17:25PM -0500, Rick Macklem wrote:
> Oops, I should have looked at the sources...
> 
> In mountd.c it limits the RPC size to 8800 for UDP and 9000
> for TCP. (UDPMSGSIZE and RPC_MAXDATASIZE respectively)
> 
> You could increase this by changing:
>   svc_dg_create(...,0, 0);
>  else
>   svc_vc_create(...,RPC_MAXDATASIZE, RPC_MAXDATASIZE);
> 
> to
>   svc_dg_create(..., 63 * 1024, 63 * 1024);
>  else
>   svc_vc_create(..., 200 * 1024, 200 * 1024);
> in mountd.c
> 
> But...
>  The Linux client might set a smaller limit in its client
>  side RPC library anyhow.
> 
> Again, I am surprised that the Linux automount checks the
> exports reported via mountd, but???
> 
> Did you check to see if the mounts work manually?
> (That would confirm if they are exported ok.)
> 
> rick
> ps: UDP datagrams are limited to 64K, but since I can't remember
>     if that size includes the UDP header, I went with 63K. For
>     TCP it is limited to 256K in the RPC library and to a somewhat
>     lower value with the default kern.ipc.maxsockbuf. (Basically
>     what the kernel sets sb_max_adj to.)

The problem is in the RPC library code, that was my first idea since I
could reproduce this mistake with my nfse.  Sometimes the answer for MOUNT
EXPORT request was truncated, sometimes the answer was damaged.  I wrote
a special client/server program that use RPC to reproduce this mistake.

I did not see this mistake if a TCP socket is blocking (just remove the
rpc_control(RPC_SVC_CONNMAXREC_SET) call from the mountd.c or nfse.c and
check result), but this mistake exists when a non-blocking socket for RPC
is used.

Here is the problem description.  The libc/rpc/svc_vc.c:write_vc() function
calls _write() and sends data to opened TCP connection.  For non-blocking
socket it has something like timeout in 2 seconds (actually write_vc() can
spend more real time for sending for non-blocking socket).  The i variable
is used for offset in a buffer and as a counter at the same time.  When
_write() fails this variable got the -1 value and this value as added
to the buffer address and to the counter (the buffer address is decreased
and the counter value actually is increased).  So we get buffer underflow.
As a result write_vc() can send data that does not belong to data that
were expected to be sent, so this is a security mistake for any program
that use RPC with a non-blocking TCP socket.

This this the update (this is the minimal version, without optimization):

--- svc_vc.c.orig	2009-08-03 11:13:06.000000000 +0300
+++ svc_vc.c	2011-01-31 11:31:28.000000000 +0200
@@ -546,7 +546,7 @@ write_vc(xprtp, buf, len)
 				cd->strm_stat = XPRT_DIED;
 				return (-1);
 			}
-			if (cd->nonblock && i != cnt) {
+			if (cd->nonblock) {
 				/*
 				 * For non-blocking connections, do not
 				 * take more than 2 seconds writing the
@@ -560,6 +560,7 @@ write_vc(xprtp, buf, len)
 					return (-1);
 				}
 			}
+			i = 0;
 		}
 	}

After this update I could not reproduce this mistake with nfse under
8-STABLE, for tests I created nfs.exports file with 10.000 pathnames,
each of them has 18 addresses in specification.

-------------------------------------------

I have another question.  Why does mountd need two versions for MOUNT EXPORT
request's answer (complete and brief)?  I checked other implementations of
showmount and non of them try to send second query if the first query failed.


More information about the freebsd-fs mailing list