--- a/sys/vfs/nfs/krpc_subr.c +++ b/sys/vfs/nfs/krpc_subr.c @@ -198,7 +198,6 @@ struct timeval tv; struct sockbuf sio; int error, rcvflg, timo, secs, len; - static u_int32_t static_xid = ~0xFF; u_int32_t xid; u_int16_t tport; u_int32_t saddr; @@ -278,9 +277,7 @@ mhead->m_len = sizeof(*call); bzero((caddr_t)call, sizeof(*call)); /* rpc_call part */ - do { - xid = atomic_fetchadd_int(&static_xid, 1); - } while (xid == 0); + xid = karc4random(); call->rp_xid = txdr_unsigned(xid); /* call->rp_direction = 0; */ call->rp_rpcvers = txdr_unsigned(2); @@ -363,13 +360,47 @@ } if (error) goto out; - len -= sio.sb_cc; + /* + * The reply must come from the server we asked + * (address AND port): this UDP socket is never + * connect()ed, so without a source check any + * LAN host that guesses the xid becomes the + * boot client's RPC authority. The xid is now + * karc4random()-backed, but check the origin + * anyway. + */ + if (from == NULL || + from->sa_len < sizeof(struct sockaddr_in) || + ((struct sockaddr_in *)(void *)from)-> + sin_addr.s_addr != sa->sin_addr.s_addr || + ((struct sockaddr_in *)(void *)from)-> + sin_port != sa->sin_port) { + if (m) { + m_freem(m); + m = NULL; + } + continue; + } + /* + * sbinit() zeroes sb_cc and soreceive() counts + * the DELIVERED bytes into it, so the residual + * math below could never gate a UDP reply (the + * max datagram is far below 65520). Use the + * received length. + */ + len = (int)sio.sb_cc; m = sio.sb_mb; - /* Does the reply contain at least a header? */ - if (len < MIN_REPLY_HDR) + /* + * rp_status lives at offset 20..23 of the + * 24-byte struct and is read below BEFORE any + * m_pullup(), so require the full header up + * front and pull it into the first mbuf now. + */ + if (len < (int)sizeof(struct rpc_reply)) continue; - if (m->m_len < MIN_REPLY_HDR) + m = m_pullup(m, sizeof(struct rpc_reply)); + if (m == NULL) continue; reply = mtod(m, struct rpc_reply *); @@ -391,6 +422,10 @@ if (reply->rp_status != 0) { error = fxdr_unsigned(u_int32_t, reply->rp_status); if (error == RPC_PROGMISMATCH) { + if (m) { + m_freem(m); + m = NULL; + } error = EBADRPC; goto out; }