Drivers: hv: vmbus: Copy packets sent by Hyper-V out of the ring buffer

Pointers to ring-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, return a copy of the incoming VMBus packet
after validating its length and offset fields in hv_pkt_iter_first().
In this way, the packet can no longer be modified by the host.

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>
Reviewed-by: Michael Kelley <mikelley@microsoft.com>
Link: https://lore.kernel.org/r/20210408161439.341988-1-parri.andrea@gmail.com
Signed-off-by: Wei Liu <wei.liu@kernel.org>
This commit is contained in:
Andres Beltran 2021-04-08 18:14:39 +02:00 committed by Wei Liu
parent 03b30cc38d
commit adae1e931a
11 changed files with 143 additions and 25 deletions

View file

@ -181,6 +181,10 @@ struct hv_ring_buffer_info {
* being freed while the ring buffer is being accessed.
*/
struct mutex ring_buffer_mutex;
/* Buffer that holds a copy of an incoming host packet */
void *pkt_buffer;
u32 pkt_buffer_size;
};
@ -799,6 +803,8 @@ struct vmbus_device {
bool allowed_in_isolated;
};
#define VMBUS_DEFAULT_MAX_PKT_SIZE 4096
struct vmbus_channel {
struct list_head listentry;
@ -1021,6 +1027,9 @@ struct vmbus_channel {
/* request/transaction ids for VMBus */
struct vmbus_requestor requestor;
u32 rqstor_size;
/* The max size of a packet on this channel */
u32 max_pkt_size;
};
u64 vmbus_next_request_id(struct vmbus_requestor *rqstor, u64 rqst_addr);
@ -1662,15 +1671,44 @@ static inline u32 hv_pkt_datalen(const struct vmpacket_descriptor *desc)
}
struct vmpacket_descriptor *
hv_pkt_iter_first_raw(struct vmbus_channel *channel);
struct vmpacket_descriptor *
hv_pkt_iter_first(struct vmbus_channel *channel);
struct vmpacket_descriptor *
__hv_pkt_iter_next(struct vmbus_channel *channel,
const struct vmpacket_descriptor *pkt);
const struct vmpacket_descriptor *pkt,
bool copy);
void hv_pkt_iter_close(struct vmbus_channel *channel);
static inline struct vmpacket_descriptor *
hv_pkt_iter_next_pkt(struct vmbus_channel *channel,
const struct vmpacket_descriptor *pkt,
bool copy)
{
struct vmpacket_descriptor *nxt;
nxt = __hv_pkt_iter_next(channel, pkt, copy);
if (!nxt)
hv_pkt_iter_close(channel);
return nxt;
}
/*
* Get next packet descriptor without copying it out of the ring buffer
* If at end of list, return NULL and update host.
*/
static inline struct vmpacket_descriptor *
hv_pkt_iter_next_raw(struct vmbus_channel *channel,
const struct vmpacket_descriptor *pkt)
{
return hv_pkt_iter_next_pkt(channel, pkt, false);
}
/*
* Get next packet descriptor from iterator
* If at end of list, return NULL and update host.
@ -1679,13 +1717,7 @@ static inline struct vmpacket_descriptor *
hv_pkt_iter_next(struct vmbus_channel *channel,
const struct vmpacket_descriptor *pkt)
{
struct vmpacket_descriptor *nxt;
nxt = __hv_pkt_iter_next(channel, pkt);
if (!nxt)
hv_pkt_iter_close(channel);
return nxt;
return hv_pkt_iter_next_pkt(channel, pkt, true);
}
#define foreach_vmbus_pkt(pkt, channel) \