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
This commit is contained in:
parent
7be8de4271
commit
b349357819
@ -159,39 +159,22 @@ hv_rf_receive_indicate_status(struct hn_softc *sc, const void *data, int dlen)
|
||||
}
|
||||
|
||||
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 @@ hv_rf_find_recvinfo(const rndis_packet *rpkt, struct hn_recvinfo *info)
|
||||
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;
|
||||
|
||||
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);
|
||||
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 = rndis_pkt->data_length;
|
||||
data = (const uint8_t *)data + data_offset;
|
||||
/*
|
||||
* Check offests.
|
||||
*/
|
||||
#define IS_OFFSET_INVALID(ofs) \
|
||||
((ofs) < RNDIS_PACKET_MSG_OFFSET_MIN || \
|
||||
((ofs) & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK))
|
||||
|
||||
if (hv_rf_find_recvinfo(rndis_pkt, &info)) {
|
||||
if_printf(rxr->hn_ifp, "recvinfo parsing failed\n");
|
||||
/* 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;
|
||||
}
|
||||
netvsc_recv(rxr, data, dlen, &info);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 (__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, ((const uint8_t *)pkt) + data_off, data_len, &info);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -565,7 +688,7 @@ hn_rndis_query(struct hn_softc *sc, uint32_t oid,
|
||||
* 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);
|
||||
|
@ -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. */
|
||||
|
Loading…
Reference in New Issue
Block a user