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 <[email protected]>
Co-developed-by: Andrea Parri (Microsoft) <[email protected]>
Signed-off-by: Andrea Parri (Microsoft) <[email protected]>
Reviewed-by: Michael Kelley <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Wei Liu <[email protected]>
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index d1e59dbe..3932446 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -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);
@@ -1663,14 +1672,43 @@ 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) \