Skip to content

Commit 4414418

Browse files
Andres Beltrandavem330
authored andcommitted
hv_netvsc: Add validation for untrusted Hyper-V values
For additional robustness in the face of Hyper-V errors or malicious behavior, validate all values that originate from packets that Hyper-V has sent to the guest in the host-to-guest ring buffer. Ensure that invalid values cannot cause indexing off the end of an array, or subvert an existing validation via integer overflow. Ensure that outgoing packets do not have any leftover guest memory that has not been zeroed out. Signed-off-by: Andres Beltran <lkmlabelt@gmail.com> Co-developed-by: Andrea Parri (Microsoft) <parri.andrea@gmail.com> Signed-off-by: Andrea Parri (Microsoft) <parri.andrea@gmail.com> Cc: "David S. Miller" <davem@davemloft.net> Cc: Jakub Kicinski <kuba@kernel.org> Cc: netdev@vger.kernel.org Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent fd944dc commit 4414418

File tree

4 files changed

+188
-20
lines changed

4 files changed

+188
-20
lines changed

drivers/net/hyperv/hyperv_net.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,10 @@ struct nvsp_message {
847847

848848
#define NETVSC_XDP_HDRM 256
849849

850+
#define NETVSC_XFER_HEADER_SIZE(rng_cnt) \
851+
(offsetof(struct vmtransfer_page_packet_header, ranges) + \
852+
(rng_cnt) * sizeof(struct vmtransfer_page_range))
853+
850854
struct multi_send_data {
851855
struct sk_buff *skb; /* skb containing the pkt */
852856
struct hv_netvsc_packet *pkt; /* netvsc pkt pending */

drivers/net/hyperv/netvsc.c

Lines changed: 111 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,15 @@ static int netvsc_init_buf(struct hv_device *device,
388388
net_device->recv_section_size = resp->sections[0].sub_alloc_size;
389389
net_device->recv_section_cnt = resp->sections[0].num_sub_allocs;
390390

391+
/* Ensure buffer will not overflow */
392+
if (net_device->recv_section_size < NETVSC_MTU_MIN || (u64)net_device->recv_section_size *
393+
(u64)net_device->recv_section_cnt > (u64)buf_size) {
394+
netdev_err(ndev, "invalid recv_section_size %u\n",
395+
net_device->recv_section_size);
396+
ret = -EINVAL;
397+
goto cleanup;
398+
}
399+
391400
/* Setup receive completion ring.
392401
* Add 1 to the recv_section_cnt because at least one entry in a
393402
* ring buffer has to be empty.
@@ -460,6 +469,12 @@ static int netvsc_init_buf(struct hv_device *device,
460469
/* Parse the response */
461470
net_device->send_section_size = init_packet->msg.
462471
v1_msg.send_send_buf_complete.section_size;
472+
if (net_device->send_section_size < NETVSC_MTU_MIN) {
473+
netdev_err(ndev, "invalid send_section_size %u\n",
474+
net_device->send_section_size);
475+
ret = -EINVAL;
476+
goto cleanup;
477+
}
463478

464479
/* Section count is simply the size divided by the section size. */
465480
net_device->send_section_cnt = buf_size / net_device->send_section_size;
@@ -731,12 +746,49 @@ static void netvsc_send_completion(struct net_device *ndev,
731746
int budget)
732747
{
733748
const struct nvsp_message *nvsp_packet = hv_pkt_data(desc);
749+
u32 msglen = hv_pkt_datalen(desc);
750+
751+
/* Ensure packet is big enough to read header fields */
752+
if (msglen < sizeof(struct nvsp_message_header)) {
753+
netdev_err(ndev, "nvsp_message length too small: %u\n", msglen);
754+
return;
755+
}
734756

735757
switch (nvsp_packet->hdr.msg_type) {
736758
case NVSP_MSG_TYPE_INIT_COMPLETE:
759+
if (msglen < sizeof(struct nvsp_message_header) +
760+
sizeof(struct nvsp_message_init_complete)) {
761+
netdev_err(ndev, "nvsp_msg length too small: %u\n",
762+
msglen);
763+
return;
764+
}
765+
fallthrough;
766+
737767
case NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE:
768+
if (msglen < sizeof(struct nvsp_message_header) +
769+
sizeof(struct nvsp_1_message_send_receive_buffer_complete)) {
770+
netdev_err(ndev, "nvsp_msg1 length too small: %u\n",
771+
msglen);
772+
return;
773+
}
774+
fallthrough;
775+
738776
case NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE:
777+
if (msglen < sizeof(struct nvsp_message_header) +
778+
sizeof(struct nvsp_1_message_send_send_buffer_complete)) {
779+
netdev_err(ndev, "nvsp_msg1 length too small: %u\n",
780+
msglen);
781+
return;
782+
}
783+
fallthrough;
784+
739785
case NVSP_MSG5_TYPE_SUBCHANNEL:
786+
if (msglen < sizeof(struct nvsp_message_header) +
787+
sizeof(struct nvsp_5_subchannel_complete)) {
788+
netdev_err(ndev, "nvsp_msg5 length too small: %u\n",
789+
msglen);
790+
return;
791+
}
740792
/* Copy the response back */
741793
memcpy(&net_device->channel_init_pkt, nvsp_packet,
742794
sizeof(struct nvsp_message));
@@ -1117,19 +1169,28 @@ static void enq_receive_complete(struct net_device *ndev,
11171169
static int netvsc_receive(struct net_device *ndev,
11181170
struct netvsc_device *net_device,
11191171
struct netvsc_channel *nvchan,
1120-
const struct vmpacket_descriptor *desc,
1121-
const struct nvsp_message *nvsp)
1172+
const struct vmpacket_descriptor *desc)
11221173
{
11231174
struct net_device_context *net_device_ctx = netdev_priv(ndev);
11241175
struct vmbus_channel *channel = nvchan->channel;
11251176
const struct vmtransfer_page_packet_header *vmxferpage_packet
11261177
= container_of(desc, const struct vmtransfer_page_packet_header, d);
1178+
const struct nvsp_message *nvsp = hv_pkt_data(desc);
1179+
u32 msglen = hv_pkt_datalen(desc);
11271180
u16 q_idx = channel->offermsg.offer.sub_channel_index;
11281181
char *recv_buf = net_device->recv_buf;
11291182
u32 status = NVSP_STAT_SUCCESS;
11301183
int i;
11311184
int count = 0;
11321185

1186+
/* Ensure packet is big enough to read header fields */
1187+
if (msglen < sizeof(struct nvsp_message_header)) {
1188+
netif_err(net_device_ctx, rx_err, ndev,
1189+
"invalid nvsp header, length too small: %u\n",
1190+
msglen);
1191+
return 0;
1192+
}
1193+
11331194
/* Make sure this is a valid nvsp packet */
11341195
if (unlikely(nvsp->hdr.msg_type != NVSP_MSG1_TYPE_SEND_RNDIS_PKT)) {
11351196
netif_err(net_device_ctx, rx_err, ndev,
@@ -1138,6 +1199,14 @@ static int netvsc_receive(struct net_device *ndev,
11381199
return 0;
11391200
}
11401201

1202+
/* Validate xfer page pkt header */
1203+
if ((desc->offset8 << 3) < sizeof(struct vmtransfer_page_packet_header)) {
1204+
netif_err(net_device_ctx, rx_err, ndev,
1205+
"Invalid xfer page pkt, offset too small: %u\n",
1206+
desc->offset8 << 3);
1207+
return 0;
1208+
}
1209+
11411210
if (unlikely(vmxferpage_packet->xfer_pageset_id != NETVSC_RECEIVE_BUFFER_ID)) {
11421211
netif_err(net_device_ctx, rx_err, ndev,
11431212
"Invalid xfer page set id - expecting %x got %x\n",
@@ -1148,14 +1217,23 @@ static int netvsc_receive(struct net_device *ndev,
11481217

11491218
count = vmxferpage_packet->range_cnt;
11501219

1220+
/* Check count for a valid value */
1221+
if (NETVSC_XFER_HEADER_SIZE(count) > desc->offset8 << 3) {
1222+
netif_err(net_device_ctx, rx_err, ndev,
1223+
"Range count is not valid: %d\n",
1224+
count);
1225+
return 0;
1226+
}
1227+
11511228
/* Each range represents 1 RNDIS pkt that contains 1 ethernet frame */
11521229
for (i = 0; i < count; i++) {
11531230
u32 offset = vmxferpage_packet->ranges[i].byte_offset;
11541231
u32 buflen = vmxferpage_packet->ranges[i].byte_count;
11551232
void *data;
11561233
int ret;
11571234

1158-
if (unlikely(offset + buflen > net_device->recv_buf_size)) {
1235+
if (unlikely(offset > net_device->recv_buf_size ||
1236+
buflen > net_device->recv_buf_size - offset)) {
11591237
nvchan->rsc.cnt = 0;
11601238
status = NVSP_STAT_FAIL;
11611239
netif_err(net_device_ctx, rx_err, ndev,
@@ -1194,6 +1272,13 @@ static void netvsc_send_table(struct net_device *ndev,
11941272
u32 count, offset, *tab;
11951273
int i;
11961274

1275+
/* Ensure packet is big enough to read send_table fields */
1276+
if (msglen < sizeof(struct nvsp_message_header) +
1277+
sizeof(struct nvsp_5_send_indirect_table)) {
1278+
netdev_err(ndev, "nvsp_v5_msg length too small: %u\n", msglen);
1279+
return;
1280+
}
1281+
11971282
count = nvmsg->msg.v5_msg.send_table.count;
11981283
offset = nvmsg->msg.v5_msg.send_table.offset;
11991284

@@ -1225,10 +1310,18 @@ static void netvsc_send_table(struct net_device *ndev,
12251310
}
12261311

12271312
static void netvsc_send_vf(struct net_device *ndev,
1228-
const struct nvsp_message *nvmsg)
1313+
const struct nvsp_message *nvmsg,
1314+
u32 msglen)
12291315
{
12301316
struct net_device_context *net_device_ctx = netdev_priv(ndev);
12311317

1318+
/* Ensure packet is big enough to read its fields */
1319+
if (msglen < sizeof(struct nvsp_message_header) +
1320+
sizeof(struct nvsp_4_send_vf_association)) {
1321+
netdev_err(ndev, "nvsp_v4_msg length too small: %u\n", msglen);
1322+
return;
1323+
}
1324+
12321325
net_device_ctx->vf_alloc = nvmsg->msg.v4_msg.vf_assoc.allocated;
12331326
net_device_ctx->vf_serial = nvmsg->msg.v4_msg.vf_assoc.serial;
12341327
netdev_info(ndev, "VF slot %u %s\n",
@@ -1238,16 +1331,24 @@ static void netvsc_send_vf(struct net_device *ndev,
12381331

12391332
static void netvsc_receive_inband(struct net_device *ndev,
12401333
struct netvsc_device *nvscdev,
1241-
const struct nvsp_message *nvmsg,
1242-
u32 msglen)
1334+
const struct vmpacket_descriptor *desc)
12431335
{
1336+
const struct nvsp_message *nvmsg = hv_pkt_data(desc);
1337+
u32 msglen = hv_pkt_datalen(desc);
1338+
1339+
/* Ensure packet is big enough to read header fields */
1340+
if (msglen < sizeof(struct nvsp_message_header)) {
1341+
netdev_err(ndev, "inband nvsp_message length too small: %u\n", msglen);
1342+
return;
1343+
}
1344+
12441345
switch (nvmsg->hdr.msg_type) {
12451346
case NVSP_MSG5_TYPE_SEND_INDIRECTION_TABLE:
12461347
netvsc_send_table(ndev, nvscdev, nvmsg, msglen);
12471348
break;
12481349

12491350
case NVSP_MSG4_TYPE_SEND_VF_ASSOCIATION:
1250-
netvsc_send_vf(ndev, nvmsg);
1351+
netvsc_send_vf(ndev, nvmsg, msglen);
12511352
break;
12521353
}
12531354
}
@@ -1261,23 +1362,20 @@ static int netvsc_process_raw_pkt(struct hv_device *device,
12611362
{
12621363
struct vmbus_channel *channel = nvchan->channel;
12631364
const struct nvsp_message *nvmsg = hv_pkt_data(desc);
1264-
u32 msglen = hv_pkt_datalen(desc);
12651365

12661366
trace_nvsp_recv(ndev, channel, nvmsg);
12671367

12681368
switch (desc->type) {
12691369
case VM_PKT_COMP:
1270-
netvsc_send_completion(ndev, net_device, channel,
1271-
desc, budget);
1370+
netvsc_send_completion(ndev, net_device, channel, desc, budget);
12721371
break;
12731372

12741373
case VM_PKT_DATA_USING_XFER_PAGES:
1275-
return netvsc_receive(ndev, net_device, nvchan,
1276-
desc, nvmsg);
1374+
return netvsc_receive(ndev, net_device, nvchan, desc);
12771375
break;
12781376

12791377
case VM_PKT_DATA_INBAND:
1280-
netvsc_receive_inband(ndev, net_device, nvmsg, msglen);
1378+
netvsc_receive_inband(ndev, net_device, desc);
12811379
break;
12821380

12831381
default:

drivers/net/hyperv/netvsc_drv.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,13 @@ void netvsc_linkstatus_callback(struct net_device *net,
748748
struct netvsc_reconfig *event;
749749
unsigned long flags;
750750

751+
/* Ensure the packet is big enough to access its fields */
752+
if (resp->msg_len - RNDIS_HEADER_SIZE < sizeof(struct rndis_indicate_status)) {
753+
netdev_err(net, "invalid rndis_indicate_status packet, len: %u\n",
754+
resp->msg_len);
755+
return;
756+
}
757+
751758
/* Update the physical link speed when changing to another vSwitch */
752759
if (indicate->status == RNDIS_STATUS_LINK_SPEED_CHANGE) {
753760
u32 speed;

drivers/net/hyperv/rndis_filter.c

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,16 @@ static void rndis_filter_receive_response(struct net_device *ndev,
275275
return;
276276
}
277277

278+
/* Ensure the packet is big enough to read req_id. Req_id is the 1st
279+
* field in any request/response message, so the payload should have at
280+
* least sizeof(u32) bytes
281+
*/
282+
if (resp->msg_len - RNDIS_HEADER_SIZE < sizeof(u32)) {
283+
netdev_err(ndev, "rndis msg_len too small: %u\n",
284+
resp->msg_len);
285+
return;
286+
}
287+
278288
spin_lock_irqsave(&dev->request_lock, flags);
279289
list_for_each_entry(request, &dev->req_list, list_ent) {
280290
/*
@@ -331,20 +341,46 @@ static void rndis_filter_receive_response(struct net_device *ndev,
331341
* Get the Per-Packet-Info with the specified type
332342
* return NULL if not found.
333343
*/
334-
static inline void *rndis_get_ppi(struct rndis_packet *rpkt,
335-
u32 type, u8 internal)
344+
static inline void *rndis_get_ppi(struct net_device *ndev,
345+
struct rndis_packet *rpkt,
346+
u32 rpkt_len, u32 type, u8 internal)
336347
{
337348
struct rndis_per_packet_info *ppi;
338349
int len;
339350

340351
if (rpkt->per_pkt_info_offset == 0)
341352
return NULL;
342353

354+
/* Validate info_offset and info_len */
355+
if (rpkt->per_pkt_info_offset < sizeof(struct rndis_packet) ||
356+
rpkt->per_pkt_info_offset > rpkt_len) {
357+
netdev_err(ndev, "Invalid per_pkt_info_offset: %u\n",
358+
rpkt->per_pkt_info_offset);
359+
return NULL;
360+
}
361+
362+
if (rpkt->per_pkt_info_len > rpkt_len - rpkt->per_pkt_info_offset) {
363+
netdev_err(ndev, "Invalid per_pkt_info_len: %u\n",
364+
rpkt->per_pkt_info_len);
365+
return NULL;
366+
}
367+
343368
ppi = (struct rndis_per_packet_info *)((ulong)rpkt +
344369
rpkt->per_pkt_info_offset);
345370
len = rpkt->per_pkt_info_len;
346371

347372
while (len > 0) {
373+
/* Validate ppi_offset and ppi_size */
374+
if (ppi->size > len) {
375+
netdev_err(ndev, "Invalid ppi size: %u\n", ppi->size);
376+
continue;
377+
}
378+
379+
if (ppi->ppi_offset >= ppi->size) {
380+
netdev_err(ndev, "Invalid ppi_offset: %u\n", ppi->ppi_offset);
381+
continue;
382+
}
383+
348384
if (ppi->type == type && ppi->internal == internal)
349385
return (void *)((ulong)ppi + ppi->ppi_offset);
350386
len -= ppi->size;
@@ -388,14 +424,29 @@ static int rndis_filter_receive_data(struct net_device *ndev,
388424
const struct ndis_pkt_8021q_info *vlan;
389425
const struct rndis_pktinfo_id *pktinfo_id;
390426
const u32 *hash_info;
391-
u32 data_offset;
427+
u32 data_offset, rpkt_len;
392428
void *data;
393429
bool rsc_more = false;
394430
int ret;
395431

432+
/* Ensure data_buflen is big enough to read header fields */
433+
if (data_buflen < RNDIS_HEADER_SIZE + sizeof(struct rndis_packet)) {
434+
netdev_err(ndev, "invalid rndis pkt, data_buflen too small: %u\n",
435+
data_buflen);
436+
return NVSP_STAT_FAIL;
437+
}
438+
439+
/* Validate rndis_pkt offset */
440+
if (rndis_pkt->data_offset >= data_buflen - RNDIS_HEADER_SIZE) {
441+
netdev_err(ndev, "invalid rndis packet offset: %u\n",
442+
rndis_pkt->data_offset);
443+
return NVSP_STAT_FAIL;
444+
}
445+
396446
/* Remove the rndis header and pass it back up the stack */
397447
data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset;
398448

449+
rpkt_len = data_buflen - RNDIS_HEADER_SIZE;
399450
data_buflen -= data_offset;
400451

401452
/*
@@ -410,13 +461,13 @@ static int rndis_filter_receive_data(struct net_device *ndev,
410461
return NVSP_STAT_FAIL;
411462
}
412463

413-
vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO, 0);
464+
vlan = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, IEEE_8021Q_INFO, 0);
414465

415-
csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO, 0);
466+
csum_info = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, TCPIP_CHKSUM_PKTINFO, 0);
416467

417-
hash_info = rndis_get_ppi(rndis_pkt, NBL_HASH_VALUE, 0);
468+
hash_info = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, NBL_HASH_VALUE, 0);
418469

419-
pktinfo_id = rndis_get_ppi(rndis_pkt, RNDIS_PKTINFO_ID, 1);
470+
pktinfo_id = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, RNDIS_PKTINFO_ID, 1);
420471

421472
data = (void *)msg + data_offset;
422473

@@ -474,6 +525,14 @@ int rndis_filter_receive(struct net_device *ndev,
474525
if (netif_msg_rx_status(net_device_ctx))
475526
dump_rndis_message(ndev, rndis_msg);
476527

528+
/* Validate incoming rndis_message packet */
529+
if (buflen < RNDIS_HEADER_SIZE || rndis_msg->msg_len < RNDIS_HEADER_SIZE ||
530+
buflen < rndis_msg->msg_len) {
531+
netdev_err(ndev, "Invalid rndis_msg (buflen: %u, msg_len: %u)\n",
532+
buflen, rndis_msg->msg_len);
533+
return NVSP_STAT_FAIL;
534+
}
535+
477536
switch (rndis_msg->ndis_msg_type) {
478537
case RNDIS_MSG_PACKET:
479538
return rndis_filter_receive_data(ndev, net_dev, nvchan,

0 commit comments

Comments
 (0)