Skip to content

Commit 0ba35fe

Browse files
andrea-parrikuba-moo
authored andcommitted
hv_netvsc: Copy packets sent by Hyper-V out of the receive buffer
Pointers to receive-buffer packets sent by Hyper-V are used within the guest VM. Hyper-V can send packets with erroneous values or modify packet fields after they are processed by the guest. To defend against these scenarios, copy (sections of) the incoming packet after validating their length and offset fields in netvsc_filter_receive(). In this way, the packet can no longer be modified by the host. Reported-by: Juan Vazquez <[email protected]> Signed-off-by: Andrea Parri (Microsoft) <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 46eb3c1 commit 0ba35fe

File tree

4 files changed

+150
-86
lines changed

4 files changed

+150
-86
lines changed

drivers/net/hyperv/hyperv_net.h

Lines changed: 48 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,43 @@ struct ndis_recv_scale_param { /* NDIS_RECEIVE_SCALE_PARAMETERS */
105105
u32 processor_masks_entry_size;
106106
};
107107

108-
/* Fwd declaration */
109-
struct ndis_tcp_ip_checksum_info;
110-
struct ndis_pkt_8021q_info;
108+
struct ndis_tcp_ip_checksum_info {
109+
union {
110+
struct {
111+
u32 is_ipv4:1;
112+
u32 is_ipv6:1;
113+
u32 tcp_checksum:1;
114+
u32 udp_checksum:1;
115+
u32 ip_header_checksum:1;
116+
u32 reserved:11;
117+
u32 tcp_header_offset:10;
118+
} transmit;
119+
struct {
120+
u32 tcp_checksum_failed:1;
121+
u32 udp_checksum_failed:1;
122+
u32 ip_checksum_failed:1;
123+
u32 tcp_checksum_succeeded:1;
124+
u32 udp_checksum_succeeded:1;
125+
u32 ip_checksum_succeeded:1;
126+
u32 loopback:1;
127+
u32 tcp_checksum_value_invalid:1;
128+
u32 ip_checksum_value_invalid:1;
129+
} receive;
130+
u32 value;
131+
};
132+
};
133+
134+
struct ndis_pkt_8021q_info {
135+
union {
136+
struct {
137+
u32 pri:3; /* User Priority */
138+
u32 cfi:1; /* Canonical Format ID */
139+
u32 vlanid:12; /* VLAN ID */
140+
u32 reserved:16;
141+
};
142+
u32 value;
143+
};
144+
};
111145

112146
/*
113147
* Represent netvsc packet which contains 1 RNDIS and 1 ethernet frame
@@ -194,7 +228,8 @@ int netvsc_send(struct net_device *net,
194228
struct sk_buff *skb,
195229
bool xdp_tx);
196230
void netvsc_linkstatus_callback(struct net_device *net,
197-
struct rndis_message *resp);
231+
struct rndis_message *resp,
232+
void *data);
198233
int netvsc_recv_callback(struct net_device *net,
199234
struct netvsc_device *nvdev,
200235
struct netvsc_channel *nvchan);
@@ -884,16 +919,21 @@ struct multi_recv_comp {
884919
#define NVSP_RSC_MAX 562 /* Max #RSC frags in a vmbus xfer page pkt */
885920

886921
struct nvsc_rsc {
887-
const struct ndis_pkt_8021q_info *vlan;
888-
const struct ndis_tcp_ip_checksum_info *csum_info;
889-
const u32 *hash_info;
922+
struct ndis_pkt_8021q_info vlan;
923+
struct ndis_tcp_ip_checksum_info csum_info;
924+
u32 hash_info;
925+
u8 ppi_flags; /* valid/present bits for the above PPIs */
890926
u8 is_last; /* last RNDIS msg in a vmtransfer_page */
891927
u32 cnt; /* #fragments in an RSC packet */
892928
u32 pktlen; /* Full packet length */
893929
void *data[NVSP_RSC_MAX];
894930
u32 len[NVSP_RSC_MAX];
895931
};
896932

933+
#define NVSC_RSC_VLAN BIT(0) /* valid/present bit for 'vlan' */
934+
#define NVSC_RSC_CSUM_INFO BIT(1) /* valid/present bit for 'csum_info' */
935+
#define NVSC_RSC_HASH_INFO BIT(2) /* valid/present bit for 'hash_info' */
936+
897937
struct netvsc_stats {
898938
u64 packets;
899939
u64 bytes;
@@ -1002,6 +1042,7 @@ struct net_device_context {
10021042
struct netvsc_channel {
10031043
struct vmbus_channel *channel;
10041044
struct netvsc_device *net_device;
1045+
void *recv_buf; /* buffer to copy packets out from the receive buffer */
10051046
const struct vmpacket_descriptor *desc;
10061047
struct napi_struct napi;
10071048
struct multi_send_data msd;
@@ -1234,18 +1275,6 @@ struct rndis_pktinfo_id {
12341275
u16 pkt_id;
12351276
};
12361277

1237-
struct ndis_pkt_8021q_info {
1238-
union {
1239-
struct {
1240-
u32 pri:3; /* User Priority */
1241-
u32 cfi:1; /* Canonical Format ID */
1242-
u32 vlanid:12; /* VLAN ID */
1243-
u32 reserved:16;
1244-
};
1245-
u32 value;
1246-
};
1247-
};
1248-
12491278
struct ndis_object_header {
12501279
u8 type;
12511280
u8 revision;
@@ -1436,32 +1465,6 @@ struct ndis_offload_params {
14361465
};
14371466
};
14381467

1439-
struct ndis_tcp_ip_checksum_info {
1440-
union {
1441-
struct {
1442-
u32 is_ipv4:1;
1443-
u32 is_ipv6:1;
1444-
u32 tcp_checksum:1;
1445-
u32 udp_checksum:1;
1446-
u32 ip_header_checksum:1;
1447-
u32 reserved:11;
1448-
u32 tcp_header_offset:10;
1449-
} transmit;
1450-
struct {
1451-
u32 tcp_checksum_failed:1;
1452-
u32 udp_checksum_failed:1;
1453-
u32 ip_checksum_failed:1;
1454-
u32 tcp_checksum_succeeded:1;
1455-
u32 udp_checksum_succeeded:1;
1456-
u32 ip_checksum_succeeded:1;
1457-
u32 loopback:1;
1458-
u32 tcp_checksum_value_invalid:1;
1459-
u32 ip_checksum_value_invalid:1;
1460-
} receive;
1461-
u32 value;
1462-
};
1463-
};
1464-
14651468
struct ndis_tcp_lso_info {
14661469
union {
14671470
struct {

drivers/net/hyperv/netvsc.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ static void free_netvsc_device(struct rcu_head *head)
131131

132132
for (i = 0; i < VRSS_CHANNEL_MAX; i++) {
133133
xdp_rxq_info_unreg(&nvdev->chan_table[i].xdp_rxq);
134+
kfree(nvdev->chan_table[i].recv_buf);
134135
vfree(nvdev->chan_table[i].mrc.slots);
135136
}
136137

@@ -1284,6 +1285,19 @@ static int netvsc_receive(struct net_device *ndev,
12841285
continue;
12851286
}
12861287

1288+
/* We're going to copy (sections of) the packet into nvchan->recv_buf;
1289+
* make sure that nvchan->recv_buf is large enough to hold the packet.
1290+
*/
1291+
if (unlikely(buflen > net_device->recv_section_size)) {
1292+
nvchan->rsc.cnt = 0;
1293+
status = NVSP_STAT_FAIL;
1294+
netif_err(net_device_ctx, rx_err, ndev,
1295+
"Packet too big: buflen=%u recv_section_size=%u\n",
1296+
buflen, net_device->recv_section_size);
1297+
1298+
continue;
1299+
}
1300+
12871301
data = recv_buf + offset;
12881302

12891303
nvchan->rsc.is_last = (i == count - 1);
@@ -1535,6 +1549,12 @@ struct netvsc_device *netvsc_device_add(struct hv_device *device,
15351549
for (i = 0; i < VRSS_CHANNEL_MAX; i++) {
15361550
struct netvsc_channel *nvchan = &net_device->chan_table[i];
15371551

1552+
nvchan->recv_buf = kzalloc(device_info->recv_section_size, GFP_KERNEL);
1553+
if (nvchan->recv_buf == NULL) {
1554+
ret = -ENOMEM;
1555+
goto cleanup2;
1556+
}
1557+
15381558
nvchan->channel = device->channel;
15391559
nvchan->net_device = net_device;
15401560
u64_stats_init(&nvchan->tx_stats.syncp);

drivers/net/hyperv/netvsc_drv.c

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -743,7 +743,8 @@ static netdev_tx_t netvsc_start_xmit(struct sk_buff *skb,
743743
* netvsc_linkstatus_callback - Link up/down notification
744744
*/
745745
void netvsc_linkstatus_callback(struct net_device *net,
746-
struct rndis_message *resp)
746+
struct rndis_message *resp,
747+
void *data)
747748
{
748749
struct rndis_indicate_status *indicate = &resp->msg.indicate_status;
749750
struct net_device_context *ndev_ctx = netdev_priv(net);
@@ -757,6 +758,9 @@ void netvsc_linkstatus_callback(struct net_device *net,
757758
return;
758759
}
759760

761+
/* Copy the RNDIS indicate status into nvchan->recv_buf */
762+
memcpy(indicate, data + RNDIS_HEADER_SIZE, sizeof(*indicate));
763+
760764
/* Update the physical link speed when changing to another vSwitch */
761765
if (indicate->status == RNDIS_STATUS_LINK_SPEED_CHANGE) {
762766
u32 speed;
@@ -771,8 +775,7 @@ void netvsc_linkstatus_callback(struct net_device *net,
771775
return;
772776
}
773777

774-
speed = *(u32 *)((void *)indicate
775-
+ indicate->status_buf_offset) / 10000;
778+
speed = *(u32 *)(data + RNDIS_HEADER_SIZE + indicate->status_buf_offset) / 10000;
776779
ndev_ctx->speed = speed;
777780
return;
778781
}
@@ -827,10 +830,11 @@ static struct sk_buff *netvsc_alloc_recv_skb(struct net_device *net,
827830
struct xdp_buff *xdp)
828831
{
829832
struct napi_struct *napi = &nvchan->napi;
830-
const struct ndis_pkt_8021q_info *vlan = nvchan->rsc.vlan;
833+
const struct ndis_pkt_8021q_info *vlan = &nvchan->rsc.vlan;
831834
const struct ndis_tcp_ip_checksum_info *csum_info =
832-
nvchan->rsc.csum_info;
833-
const u32 *hash_info = nvchan->rsc.hash_info;
835+
&nvchan->rsc.csum_info;
836+
const u32 *hash_info = &nvchan->rsc.hash_info;
837+
u8 ppi_flags = nvchan->rsc.ppi_flags;
834838
struct sk_buff *skb;
835839
void *xbuf = xdp->data_hard_start;
836840
int i;
@@ -874,7 +878,7 @@ static struct sk_buff *netvsc_alloc_recv_skb(struct net_device *net,
874878
* We compute it here if the flags are set, because on Linux, the IP
875879
* checksum is always checked.
876880
*/
877-
if (csum_info && csum_info->receive.ip_checksum_value_invalid &&
881+
if ((ppi_flags & NVSC_RSC_CSUM_INFO) && csum_info->receive.ip_checksum_value_invalid &&
878882
csum_info->receive.ip_checksum_succeeded &&
879883
skb->protocol == htons(ETH_P_IP)) {
880884
/* Check that there is enough space to hold the IP header. */
@@ -886,16 +890,16 @@ static struct sk_buff *netvsc_alloc_recv_skb(struct net_device *net,
886890
}
887891

888892
/* Do L4 checksum offload if enabled and present. */
889-
if (csum_info && (net->features & NETIF_F_RXCSUM)) {
893+
if ((ppi_flags & NVSC_RSC_CSUM_INFO) && (net->features & NETIF_F_RXCSUM)) {
890894
if (csum_info->receive.tcp_checksum_succeeded ||
891895
csum_info->receive.udp_checksum_succeeded)
892896
skb->ip_summed = CHECKSUM_UNNECESSARY;
893897
}
894898

895-
if (hash_info && (net->features & NETIF_F_RXHASH))
899+
if ((ppi_flags & NVSC_RSC_HASH_INFO) && (net->features & NETIF_F_RXHASH))
896900
skb_set_hash(skb, *hash_info, PKT_HASH_TYPE_L4);
897901

898-
if (vlan) {
902+
if (ppi_flags & NVSC_RSC_VLAN) {
899903
u16 vlan_tci = vlan->vlanid | (vlan->pri << VLAN_PRIO_SHIFT) |
900904
(vlan->cfi ? VLAN_CFI_MASK : 0);
901905

0 commit comments

Comments
 (0)