svn commit: r305453 - in head/sys: dev/hyperv/netvsc net
Sepherosa Ziehau
sephe at FreeBSD.org
Tue Sep 6 03:20:08 UTC 2016
Author: sephe
Date: Tue Sep 6 03:20:06 2016
New Revision: 305453
URL: https://svnweb.freebsd.org/changeset/base/305453
Log:
hyperv/hn: Stringent RNDIS packet message length/offset check.
While I'm here, use definition in net/rndis.h
MFC after: 1 week
Sponsored by: Microsoft
Differential Revision: https://reviews.freebsd.org/D7782
Modified:
head/sys/dev/hyperv/netvsc/hv_rndis_filter.c
head/sys/net/rndis.h
Modified: head/sys/dev/hyperv/netvsc/hv_rndis_filter.c
==============================================================================
--- head/sys/dev/hyperv/netvsc/hv_rndis_filter.c Tue Sep 6 01:10:51 2016 (r305452)
+++ head/sys/dev/hyperv/netvsc/hv_rndis_filter.c Tue Sep 6 03:20:06 2016 (r305453)
@@ -159,39 +159,22 @@ hv_rf_receive_indicate_status(struct hn_
}
static int
-hv_rf_find_recvinfo(const rndis_packet *rpkt, struct hn_recvinfo *info)
+hn_rndis_rxinfo(const void *info_data, int info_dlen, struct hn_recvinfo *info)
{
- const struct rndis_pktinfo *pi;
- uint32_t mask = 0, len;
+ const struct rndis_pktinfo *pi = info_data;
+ uint32_t mask = 0;
- info->vlan_info = HN_NDIS_VLAN_INFO_INVALID;
- info->csum_info = HN_NDIS_RXCSUM_INFO_INVALID;
- info->hash_info = HN_NDIS_HASH_INFO_INVALID;
-
- if (rpkt->per_pkt_info_offset == 0)
- return (0);
- if (__predict_false(rpkt->per_pkt_info_offset &
- (RNDIS_PKTINFO_ALIGN - 1)))
- return (EINVAL);
- if (__predict_false(rpkt->per_pkt_info_offset <
- RNDIS_PACKET_MSG_OFFSET_MIN))
- return (EINVAL);
-
- pi = (const struct rndis_pktinfo *)
- ((const uint8_t *)rpkt + rpkt->per_pkt_info_offset);
- len = rpkt->per_pkt_info_length;
-
- while (len != 0) {
+ while (info_dlen != 0) {
const void *data;
uint32_t dlen;
- if (__predict_false(len < sizeof(*pi)))
+ if (__predict_false(info_dlen < sizeof(*pi)))
return (EINVAL);
- if (__predict_false(len < pi->rm_size))
+ if (__predict_false(info_dlen < pi->rm_size))
return (EINVAL);
- len -= pi->rm_size;
+ info_dlen -= pi->rm_size;
- if (__predict_false(pi->rm_size & (RNDIS_PKTINFO_ALIGN - 1)))
+ if (__predict_false(pi->rm_size & RNDIS_PKTINFO_SIZE_ALIGNMASK))
return (EINVAL);
if (__predict_false(pi->rm_size < pi->rm_pktinfooffset))
return (EINVAL);
@@ -249,43 +232,183 @@ next:
return (0);
}
+static __inline bool
+hn_rndis_check_overlap(int off, int len, int check_off, int check_len)
+{
+
+ if (off < check_off) {
+ if (__predict_true(off + len <= check_off))
+ return (false);
+ } else if (off > check_off) {
+ if (__predict_true(check_off + check_len <= off))
+ return (false);
+ }
+ return (true);
+}
+
/*
* RNDIS filter receive data
*/
static void
hv_rf_receive_data(struct hn_rx_ring *rxr, const void *data, int dlen)
{
- const rndis_msg *message = data;
- const rndis_packet *rndis_pkt;
- uint32_t data_offset;
+ const struct rndis_packet_msg *pkt;
struct hn_recvinfo info;
-
- rndis_pkt = &message->msg.packet;
+ int data_off, pktinfo_off, data_len, pktinfo_len;
/*
- * Fixme: Handle multiple rndis pkt msgs that may be enclosed in this
- * netvsc packet (ie tot_data_buf_len != message_length)
+ * Check length.
*/
+ if (__predict_false(dlen < sizeof(*pkt))) {
+ if_printf(rxr->hn_ifp, "invalid RNDIS packet msg\n");
+ return;
+ }
+ pkt = data;
- /* Remove rndis header, then pass data packet up the stack */
- data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset;
+ if (__predict_false(dlen < pkt->rm_len)) {
+ if_printf(rxr->hn_ifp, "truncated RNDIS packet msg, "
+ "dlen %d, msglen %u\n", dlen, pkt->rm_len);
+ return;
+ }
+ if (__predict_false(pkt->rm_len <
+ pkt->rm_datalen + pkt->rm_oobdatalen + pkt->rm_pktinfolen)) {
+ if_printf(rxr->hn_ifp, "invalid RNDIS packet msglen, "
+ "msglen %u, data %u, oob %u, pktinfo %u\n",
+ pkt->rm_len, pkt->rm_datalen, pkt->rm_oobdatalen,
+ pkt->rm_pktinfolen);
+ return;
+ }
+ if (__predict_false(pkt->rm_datalen == 0)) {
+ if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, no data\n");
+ return;
+ }
- dlen -= data_offset;
- if (dlen < rndis_pkt->data_length) {
- if_printf(rxr->hn_ifp,
- "total length %u is less than data length %u\n",
- dlen, rndis_pkt->data_length);
+ /*
+ * Check offests.
+ */
+#define IS_OFFSET_INVALID(ofs) \
+ ((ofs) < RNDIS_PACKET_MSG_OFFSET_MIN || \
+ ((ofs) & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK))
+
+ /* XXX Hyper-V does not meet data offset alignment requirement */
+ if (__predict_false(pkt->rm_dataoffset < RNDIS_PACKET_MSG_OFFSET_MIN)) {
+ if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
+ "data offset %u\n", pkt->rm_dataoffset);
return;
}
+ if (__predict_false(pkt->rm_oobdataoffset > 0 &&
+ IS_OFFSET_INVALID(pkt->rm_oobdataoffset))) {
+ if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
+ "oob offset %u\n", pkt->rm_oobdataoffset);
+ return;
+ }
+ if (__predict_true(pkt->rm_pktinfooffset > 0) &&
+ __predict_false(IS_OFFSET_INVALID(pkt->rm_pktinfooffset))) {
+ if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
+ "pktinfo offset %u\n", pkt->rm_pktinfooffset);
+ return;
+ }
+
+#undef IS_OFFSET_INVALID
+
+ data_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_dataoffset);
+ data_len = pkt->rm_datalen;
+ pktinfo_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_pktinfooffset);
+ pktinfo_len = pkt->rm_pktinfolen;
+
+ /*
+ * Check OOB coverage.
+ */
+ if (__predict_false(pkt->rm_oobdatalen != 0)) {
+ int oob_off, oob_len;
+
+ if_printf(rxr->hn_ifp, "got oobdata\n");
+ oob_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_oobdataoffset);
+ oob_len = pkt->rm_oobdatalen;
+
+ if (__predict_false(oob_off + oob_len > pkt->rm_len)) {
+ if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
+ "oob overflow, msglen %u, oob abs %d len %d\n",
+ pkt->rm_len, oob_off, oob_len);
+ return;
+ }
+
+ /*
+ * Check against data.
+ */
+ if (hn_rndis_check_overlap(oob_off, oob_len,
+ data_off, data_len)) {
+ if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
+ "oob overlaps data, oob abs %d len %d, "
+ "data abs %d len %d\n",
+ oob_off, oob_len, data_off, data_len);
+ return;
+ }
+
+ /*
+ * Check against pktinfo.
+ */
+ if (pktinfo_len != 0 &&
+ hn_rndis_check_overlap(oob_off, oob_len,
+ pktinfo_off, pktinfo_len)) {
+ if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
+ "oob overlaps pktinfo, oob abs %d len %d, "
+ "pktinfo abs %d len %d\n",
+ oob_off, oob_len, pktinfo_off, pktinfo_len);
+ return;
+ }
+ }
- dlen = rndis_pkt->data_length;
- data = (const uint8_t *)data + data_offset;
+ /*
+ * Check per-packet-info coverage and find useful per-packet-info.
+ */
+ info.vlan_info = HN_NDIS_VLAN_INFO_INVALID;
+ info.csum_info = HN_NDIS_RXCSUM_INFO_INVALID;
+ info.hash_info = HN_NDIS_HASH_INFO_INVALID;
+ if (__predict_true(pktinfo_len != 0)) {
+ bool overlap;
+ int error;
+
+ if (__predict_false(pktinfo_off + pktinfo_len > pkt->rm_len)) {
+ if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
+ "pktinfo overflow, msglen %u, "
+ "pktinfo abs %d len %d\n",
+ pkt->rm_len, pktinfo_off, pktinfo_len);
+ return;
+ }
+
+ /*
+ * Check packet info coverage.
+ */
+ overlap = hn_rndis_check_overlap(pktinfo_off, pktinfo_len,
+ data_off, data_len);
+ if (__predict_false(overlap)) {
+ if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
+ "pktinfo overlap data, pktinfo abs %d len %d, "
+ "data abs %d len %d\n",
+ pktinfo_off, pktinfo_len, data_off, data_len);
+ return;
+ }
+
+ /*
+ * Find useful per-packet-info.
+ */
+ error = hn_rndis_rxinfo(((const uint8_t *)pkt) + pktinfo_off,
+ pktinfo_len, &info);
+ if (__predict_false(error)) {
+ if_printf(rxr->hn_ifp, "invalid RNDIS packet msg "
+ "pktinfo\n");
+ return;
+ }
+ }
- if (hv_rf_find_recvinfo(rndis_pkt, &info)) {
- if_printf(rxr->hn_ifp, "recvinfo parsing failed\n");
+ if (__predict_false(data_off + data_len > pkt->rm_len)) {
+ if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
+ "data overflow, msglen %u, data abs %d len %d\n",
+ pkt->rm_len, data_off, data_len);
return;
}
- netvsc_recv(rxr, data, dlen, &info);
+ netvsc_recv(rxr, ((const uint8_t *)pkt) + data_off, data_len, &info);
}
/*
@@ -565,7 +688,7 @@ hn_rndis_query(struct hn_softc *sc, uint
* Check output data length and offset.
*/
/* ofs is the offset from the beginning of comp. */
- ofs = RNDIS_QUERY_COMP_INFOBUFABS(comp->rm_infobufoffset);
+ ofs = RNDIS_QUERY_COMP_INFOBUFOFFSET_ABS(comp->rm_infobufoffset);
if (ofs < sizeof(*comp) || ofs + comp->rm_infobuflen > comp_len) {
if_printf(sc->hn_ifp, "RNDIS query invalid comp ib off/len, "
"%u/%u\n", comp->rm_infobufoffset, comp->rm_infobuflen);
Modified: head/sys/net/rndis.h
==============================================================================
--- head/sys/net/rndis.h Tue Sep 6 01:10:51 2016 (r305452)
+++ head/sys/net/rndis.h Tue Sep 6 03:20:06 2016 (r305453)
@@ -127,6 +127,14 @@ struct rndis_packet_msg {
(sizeof(struct rndis_packet_msg) - \
__offsetof(struct rndis_packet_msg, rm_dataoffset))
+/* Offset from the beginning of rndis_packet_msg. */
+#define RNDIS_PACKET_MSG_OFFSET_ABS(ofs) \
+ ((ofs) + __offsetof(struct rndis_packet_msg, rm_dataoffset))
+
+#define RNDIS_PACKET_MSG_OFFSET_ALIGN 4
+#define RNDIS_PACKET_MSG_OFFSET_ALIGNMASK \
+ (RNDIS_PACKET_MSG_OFFSET_ALIGN - 1)
+
/* Per-packet-info for RNDIS data message */
struct rndis_pktinfo {
uint32_t rm_size;
@@ -137,7 +145,8 @@ struct rndis_pktinfo {
#define RNDIS_PKTINFO_OFFSET \
__offsetof(struct rndis_pktinfo, rm_data[0])
-#define RNDIS_PKTINFO_ALIGN 4
+#define RNDIS_PKTINFO_SIZE_ALIGN 4
+#define RNDIS_PKTINFO_SIZE_ALIGNMASK (RNDIS_PKTINFO_SIZE_ALIGN - 1)
#define NDIS_PKTINFO_TYPE_CSUM 0
#define NDIS_PKTINFO_TYPE_IPSEC 1
@@ -236,7 +245,8 @@ struct rndis_query_comp {
uint32_t rm_infobufoffset;
};
-#define RNDIS_QUERY_COMP_INFOBUFABS(ofs) \
+/* infobuf offset from the beginning of rndis_query_comp. */
+#define RNDIS_QUERY_COMP_INFOBUFOFFSET_ABS(ofs) \
((ofs) + __offsetof(struct rndis_query_req, rm_rid))
/* Send a set object request. */
More information about the svn-src-all
mailing list