Commit 06caa778 authored by Andres Beltran's avatar Andres Beltran Committed by Wei Liu

hv_utils: 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 the icversion_data
array in vmbus_prep_negotiate_resp().
Signed-off-by: default avatarAndres Beltran <lkmlabelt@gmail.com>
Co-developed-by: default avatarAndrea Parri (Microsoft) <parri.andrea@gmail.com>
Signed-off-by: default avatarAndrea Parri (Microsoft) <parri.andrea@gmail.com>
Reviewed-by: default avatarMichael Kelley <mikelley@microsoft.com>
Link: https://lore.kernel.org/r/20201109100704.9152-1-parri.andrea@gmail.comSigned-off-by: default avatarWei Liu <wei.liu@kernel.org>
parent a8c32099
...@@ -190,6 +190,7 @@ static u16 hv_get_dev_type(const struct vmbus_channel *channel) ...@@ -190,6 +190,7 @@ static u16 hv_get_dev_type(const struct vmbus_channel *channel)
* vmbus_prep_negotiate_resp() - Create default response for Negotiate message * vmbus_prep_negotiate_resp() - Create default response for Negotiate message
* @icmsghdrp: Pointer to msg header structure * @icmsghdrp: Pointer to msg header structure
* @buf: Raw buffer channel data * @buf: Raw buffer channel data
* @buflen: Length of the raw buffer channel data.
* @fw_version: The framework versions we can support. * @fw_version: The framework versions we can support.
* @fw_vercnt: The size of @fw_version. * @fw_vercnt: The size of @fw_version.
* @srv_version: The service versions we can support. * @srv_version: The service versions we can support.
...@@ -202,8 +203,8 @@ static u16 hv_get_dev_type(const struct vmbus_channel *channel) ...@@ -202,8 +203,8 @@ static u16 hv_get_dev_type(const struct vmbus_channel *channel)
* Set up and fill in default negotiate response message. * Set up and fill in default negotiate response message.
* Mainly used by Hyper-V drivers. * Mainly used by Hyper-V drivers.
*/ */
bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, u8 *buf,
u8 *buf, const int *fw_version, int fw_vercnt, u32 buflen, const int *fw_version, int fw_vercnt,
const int *srv_version, int srv_vercnt, const int *srv_version, int srv_vercnt,
int *nego_fw_version, int *nego_srv_version) int *nego_fw_version, int *nego_srv_version)
{ {
...@@ -215,10 +216,14 @@ bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, ...@@ -215,10 +216,14 @@ bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp,
bool found_match = false; bool found_match = false;
struct icmsg_negotiate *negop; struct icmsg_negotiate *negop;
/* Check that there's enough space for icframe_vercnt, icmsg_vercnt */
if (buflen < ICMSG_HDR + offsetof(struct icmsg_negotiate, reserved)) {
pr_err_ratelimited("Invalid icmsg negotiate\n");
return false;
}
icmsghdrp->icmsgsize = 0x10; icmsghdrp->icmsgsize = 0x10;
negop = (struct icmsg_negotiate *)&buf[ negop = (struct icmsg_negotiate *)&buf[ICMSG_HDR];
sizeof(struct vmbuspipe_hdr) +
sizeof(struct icmsg_hdr)];
icframe_major = negop->icframe_vercnt; icframe_major = negop->icframe_vercnt;
icframe_minor = 0; icframe_minor = 0;
...@@ -226,6 +231,15 @@ bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, ...@@ -226,6 +231,15 @@ bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp,
icmsg_major = negop->icmsg_vercnt; icmsg_major = negop->icmsg_vercnt;
icmsg_minor = 0; icmsg_minor = 0;
/* Validate negop packet */
if (icframe_major > IC_VERSION_NEGOTIATION_MAX_VER_COUNT ||
icmsg_major > IC_VERSION_NEGOTIATION_MAX_VER_COUNT ||
ICMSG_NEGOTIATE_PKT_SIZE(icframe_major, icmsg_major) > buflen) {
pr_err_ratelimited("Invalid icmsg negotiate - icframe_major: %u, icmsg_major: %u\n",
icframe_major, icmsg_major);
goto fw_error;
}
/* /*
* Select the framework version number we will * Select the framework version number we will
* support. * support.
......
...@@ -235,15 +235,27 @@ void hv_fcopy_onchannelcallback(void *context) ...@@ -235,15 +235,27 @@ void hv_fcopy_onchannelcallback(void *context)
if (fcopy_transaction.state > HVUTIL_READY) if (fcopy_transaction.state > HVUTIL_READY)
return; return;
vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 2, &recvlen, if (vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 2, &recvlen, &requestid)) {
&requestid); pr_err_ratelimited("Fcopy request received. Could not read into recv buf\n");
if (recvlen <= 0)
return; return;
}
if (!recvlen)
return;
/* Ensure recvlen is big enough to read header data */
if (recvlen < ICMSG_HDR) {
pr_err_ratelimited("Fcopy request received. Packet length too small: %d\n",
recvlen);
return;
}
icmsghdr = (struct icmsg_hdr *)&recv_buffer[ icmsghdr = (struct icmsg_hdr *)&recv_buffer[
sizeof(struct vmbuspipe_hdr)]; sizeof(struct vmbuspipe_hdr)];
if (icmsghdr->icmsgtype == ICMSGTYPE_NEGOTIATE) { if (icmsghdr->icmsgtype == ICMSGTYPE_NEGOTIATE) {
if (vmbus_prep_negotiate_resp(icmsghdr, recv_buffer, if (vmbus_prep_negotiate_resp(icmsghdr,
recv_buffer, recvlen,
fw_versions, FW_VER_COUNT, fw_versions, FW_VER_COUNT,
fcopy_versions, FCOPY_VER_COUNT, fcopy_versions, FCOPY_VER_COUNT,
NULL, &fcopy_srv_version)) { NULL, &fcopy_srv_version)) {
...@@ -252,10 +264,14 @@ void hv_fcopy_onchannelcallback(void *context) ...@@ -252,10 +264,14 @@ void hv_fcopy_onchannelcallback(void *context)
fcopy_srv_version >> 16, fcopy_srv_version >> 16,
fcopy_srv_version & 0xFFFF); fcopy_srv_version & 0xFFFF);
} }
} else { } else if (icmsghdr->icmsgtype == ICMSGTYPE_FCOPY) {
fcopy_msg = (struct hv_fcopy_hdr *)&recv_buffer[ /* Ensure recvlen is big enough to contain hv_fcopy_hdr */
sizeof(struct vmbuspipe_hdr) + if (recvlen < ICMSG_HDR + sizeof(struct hv_fcopy_hdr)) {
sizeof(struct icmsg_hdr)]; pr_err_ratelimited("Invalid Fcopy hdr. Packet length too small: %u\n",
recvlen);
return;
}
fcopy_msg = (struct hv_fcopy_hdr *)&recv_buffer[ICMSG_HDR];
/* /*
* Stash away this global state for completing the * Stash away this global state for completing the
...@@ -280,6 +296,10 @@ void hv_fcopy_onchannelcallback(void *context) ...@@ -280,6 +296,10 @@ void hv_fcopy_onchannelcallback(void *context)
schedule_delayed_work(&fcopy_timeout_work, schedule_delayed_work(&fcopy_timeout_work,
HV_UTIL_TIMEOUT * HZ); HV_UTIL_TIMEOUT * HZ);
return; return;
} else {
pr_err_ratelimited("Fcopy request received. Invalid msg type: %d\n",
icmsghdr->icmsgtype);
return;
} }
icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
vmbus_sendpacket(channel, recv_buffer, recvlen, requestid, vmbus_sendpacket(channel, recv_buffer, recvlen, requestid,
......
...@@ -662,71 +662,87 @@ void hv_kvp_onchannelcallback(void *context) ...@@ -662,71 +662,87 @@ void hv_kvp_onchannelcallback(void *context)
if (kvp_transaction.state > HVUTIL_READY) if (kvp_transaction.state > HVUTIL_READY)
return; return;
vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 4, &recvlen, if (vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 4, &recvlen, &requestid)) {
&requestid); pr_err_ratelimited("KVP request received. Could not read into recv buf\n");
return;
if (recvlen > 0) { }
icmsghdrp = (struct icmsg_hdr *)&recv_buffer[
sizeof(struct vmbuspipe_hdr)];
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
if (vmbus_prep_negotiate_resp(icmsghdrp,
recv_buffer, fw_versions, FW_VER_COUNT,
kvp_versions, KVP_VER_COUNT,
NULL, &kvp_srv_version)) {
pr_info("KVP IC version %d.%d\n",
kvp_srv_version >> 16,
kvp_srv_version & 0xFFFF);
}
} else {
kvp_msg = (struct hv_kvp_msg *)&recv_buffer[
sizeof(struct vmbuspipe_hdr) +
sizeof(struct icmsg_hdr)];
/* if (!recvlen)
* Stash away this global state for completing the return;
* transaction; note transactions are serialized.
*/
kvp_transaction.recv_len = recvlen; /* Ensure recvlen is big enough to read header data */
kvp_transaction.recv_req_id = requestid; if (recvlen < ICMSG_HDR) {
kvp_transaction.kvp_msg = kvp_msg; pr_err_ratelimited("KVP request received. Packet length too small: %d\n",
recvlen);
return;
}
if (kvp_transaction.state < HVUTIL_READY) { icmsghdrp = (struct icmsg_hdr *)&recv_buffer[sizeof(struct vmbuspipe_hdr)];
/* Userspace is not registered yet */
kvp_respond_to_host(NULL, HV_E_FAIL); if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
return; if (vmbus_prep_negotiate_resp(icmsghdrp,
} recv_buffer, recvlen,
kvp_transaction.state = HVUTIL_HOSTMSG_RECEIVED; fw_versions, FW_VER_COUNT,
kvp_versions, KVP_VER_COUNT,
NULL, &kvp_srv_version)) {
pr_info("KVP IC version %d.%d\n",
kvp_srv_version >> 16,
kvp_srv_version & 0xFFFF);
}
} else if (icmsghdrp->icmsgtype == ICMSGTYPE_KVPEXCHANGE) {
/*
* recvlen is not checked against sizeof(struct kvp_msg) because kvp_msg contains
* a union of structs and the msg type received is not known. Code using this
* struct should provide validation when accessing its fields.
*/
kvp_msg = (struct hv_kvp_msg *)&recv_buffer[ICMSG_HDR];
/* /*
* Get the information from the * Stash away this global state for completing the
* user-mode component. * transaction; note transactions are serialized.
* component. This transaction will be */
* completed when we get the value from
* the user-mode component.
* Set a timeout to deal with
* user-mode not responding.
*/
schedule_work(&kvp_sendkey_work);
schedule_delayed_work(&kvp_timeout_work,
HV_UTIL_TIMEOUT * HZ);
return; kvp_transaction.recv_len = recvlen;
kvp_transaction.recv_req_id = requestid;
kvp_transaction.kvp_msg = kvp_msg;
if (kvp_transaction.state < HVUTIL_READY) {
/* Userspace is not registered yet */
kvp_respond_to_host(NULL, HV_E_FAIL);
return;
} }
kvp_transaction.state = HVUTIL_HOSTMSG_RECEIVED;
icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION /*
| ICMSGHDRFLAG_RESPONSE; * Get the information from the
* user-mode component.
* component. This transaction will be
* completed when we get the value from
* the user-mode component.
* Set a timeout to deal with
* user-mode not responding.
*/
schedule_work(&kvp_sendkey_work);
schedule_delayed_work(&kvp_timeout_work,
HV_UTIL_TIMEOUT * HZ);
vmbus_sendpacket(channel, recv_buffer, return;
recvlen, requestid,
VM_PKT_DATA_INBAND, 0);
host_negotiatied = NEGO_FINISHED; } else {
hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper); pr_err_ratelimited("KVP request received. Invalid msg type: %d\n",
icmsghdrp->icmsgtype);
return;
} }
icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
| ICMSGHDRFLAG_RESPONSE;
vmbus_sendpacket(channel, recv_buffer,
recvlen, requestid,
VM_PKT_DATA_INBAND, 0);
host_negotiatied = NEGO_FINISHED;
hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper);
} }
static void kvp_on_reset(void) static void kvp_on_reset(void)
......
...@@ -298,49 +298,64 @@ void hv_vss_onchannelcallback(void *context) ...@@ -298,49 +298,64 @@ void hv_vss_onchannelcallback(void *context)
if (vss_transaction.state > HVUTIL_READY) if (vss_transaction.state > HVUTIL_READY)
return; return;
vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 2, &recvlen, if (vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 2, &recvlen, &requestid)) {
&requestid); pr_err_ratelimited("VSS request received. Could not read into recv buf\n");
return;
if (recvlen > 0) { }
icmsghdrp = (struct icmsg_hdr *)&recv_buffer[
sizeof(struct vmbuspipe_hdr)]; if (!recvlen)
return;
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
if (vmbus_prep_negotiate_resp(icmsghdrp, /* Ensure recvlen is big enough to read header data */
recv_buffer, fw_versions, FW_VER_COUNT, if (recvlen < ICMSG_HDR) {
vss_versions, VSS_VER_COUNT, pr_err_ratelimited("VSS request received. Packet length too small: %d\n",
NULL, &vss_srv_version)) { recvlen);
return;
pr_info("VSS IC version %d.%d\n", }
vss_srv_version >> 16,
vss_srv_version & 0xFFFF); icmsghdrp = (struct icmsg_hdr *)&recv_buffer[sizeof(struct vmbuspipe_hdr)];
}
} else { if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
vss_msg = (struct hv_vss_msg *)&recv_buffer[ if (vmbus_prep_negotiate_resp(icmsghdrp,
sizeof(struct vmbuspipe_hdr) + recv_buffer, recvlen,
sizeof(struct icmsg_hdr)]; fw_versions, FW_VER_COUNT,
vss_versions, VSS_VER_COUNT,
/* NULL, &vss_srv_version)) {
* Stash away this global state for completing the
* transaction; note transactions are serialized. pr_info("VSS IC version %d.%d\n",
*/ vss_srv_version >> 16,
vss_srv_version & 0xFFFF);
vss_transaction.recv_len = recvlen; }
vss_transaction.recv_req_id = requestid; } else if (icmsghdrp->icmsgtype == ICMSGTYPE_VSS) {
vss_transaction.msg = (struct hv_vss_msg *)vss_msg; /* Ensure recvlen is big enough to contain hv_vss_msg */
if (recvlen < ICMSG_HDR + sizeof(struct hv_vss_msg)) {
schedule_work(&vss_handle_request_work); pr_err_ratelimited("Invalid VSS msg. Packet length too small: %u\n",
recvlen);
return; return;
} }
vss_msg = (struct hv_vss_msg *)&recv_buffer[ICMSG_HDR];
/*
* Stash away this global state for completing the
* transaction; note transactions are serialized.
*/
icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION vss_transaction.recv_len = recvlen;
| ICMSGHDRFLAG_RESPONSE; vss_transaction.recv_req_id = requestid;
vss_transaction.msg = (struct hv_vss_msg *)vss_msg;
vmbus_sendpacket(channel, recv_buffer, schedule_work(&vss_handle_request_work);
recvlen, requestid, return;
VM_PKT_DATA_INBAND, 0); } else {
pr_err_ratelimited("VSS request received. Invalid msg type: %d\n",
icmsghdrp->icmsgtype);
return;
} }
icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION |
ICMSGHDRFLAG_RESPONSE;
vmbus_sendpacket(channel, recv_buffer, recvlen, requestid,
VM_PKT_DATA_INBAND, 0);
} }
static void vss_on_reset(void) static void vss_on_reset(void)
......
...@@ -195,73 +195,88 @@ static void shutdown_onchannelcallback(void *context) ...@@ -195,73 +195,88 @@ static void shutdown_onchannelcallback(void *context)
struct icmsg_hdr *icmsghdrp; struct icmsg_hdr *icmsghdrp;
vmbus_recvpacket(channel, shut_txf_buf, if (vmbus_recvpacket(channel, shut_txf_buf, HV_HYP_PAGE_SIZE, &recvlen, &requestid)) {
HV_HYP_PAGE_SIZE, &recvlen, &requestid); pr_err_ratelimited("Shutdown request received. Could not read into shut txf buf\n");
return;
}
if (recvlen > 0) { if (!recvlen)
icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[ return;
sizeof(struct vmbuspipe_hdr)];
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { /* Ensure recvlen is big enough to read header data */
if (vmbus_prep_negotiate_resp(icmsghdrp, shut_txf_buf, if (recvlen < ICMSG_HDR) {
fw_versions, FW_VER_COUNT, pr_err_ratelimited("Shutdown request received. Packet length too small: %d\n",
sd_versions, SD_VER_COUNT, recvlen);
NULL, &sd_srv_version)) { return;
pr_info("Shutdown IC version %d.%d\n", }
sd_srv_version >> 16,
sd_srv_version & 0xFFFF);
}
} else {
shutdown_msg =
(struct shutdown_msg_data *)&shut_txf_buf[
sizeof(struct vmbuspipe_hdr) +
sizeof(struct icmsg_hdr)];
/* icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[sizeof(struct vmbuspipe_hdr)];
* shutdown_msg->flags can be 0(shut down), 2(reboot),
* or 4(hibernate). It may bitwise-OR 1, which means if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
* performing the request by force. Linux always tries if (vmbus_prep_negotiate_resp(icmsghdrp,
* to perform the request by force. shut_txf_buf, recvlen,
*/ fw_versions, FW_VER_COUNT,
switch (shutdown_msg->flags) { sd_versions, SD_VER_COUNT,
case 0: NULL, &sd_srv_version)) {
case 1: pr_info("Shutdown IC version %d.%d\n",
icmsghdrp->status = HV_S_OK; sd_srv_version >> 16,
work = &shutdown_work; sd_srv_version & 0xFFFF);
pr_info("Shutdown request received -" }
" graceful shutdown initiated\n"); } else if (icmsghdrp->icmsgtype == ICMSGTYPE_SHUTDOWN) {
break; /* Ensure recvlen is big enough to contain shutdown_msg_data struct */
case 2: if (recvlen < ICMSG_HDR + sizeof(struct shutdown_msg_data)) {
case 3: pr_err_ratelimited("Invalid shutdown msg data. Packet length too small: %u\n",
icmsghdrp->status = HV_S_OK; recvlen);
work = &restart_work; return;
pr_info("Restart request received -"
" graceful restart initiated\n");
break;
case 4:
case 5:
pr_info("Hibernation request received\n");
icmsghdrp->status = hibernation_supported ?
HV_S_OK : HV_E_FAIL;
if (hibernation_supported)
work = &hibernate_context.work;
break;
default:
icmsghdrp->status = HV_E_FAIL;
pr_info("Shutdown request received -"
" Invalid request\n");
break;
}
} }
icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION shutdown_msg = (struct shutdown_msg_data *)&shut_txf_buf[ICMSG_HDR];
| ICMSGHDRFLAG_RESPONSE;
/*
vmbus_sendpacket(channel, shut_txf_buf, * shutdown_msg->flags can be 0(shut down), 2(reboot),
recvlen, requestid, * or 4(hibernate). It may bitwise-OR 1, which means
VM_PKT_DATA_INBAND, 0); * performing the request by force. Linux always tries
* to perform the request by force.
*/
switch (shutdown_msg->flags) {
case 0:
case 1:
icmsghdrp->status = HV_S_OK;
work = &shutdown_work;
pr_info("Shutdown request received - graceful shutdown initiated\n");
break;
case 2:
case 3:
icmsghdrp->status = HV_S_OK;
work = &restart_work;
pr_info("Restart request received - graceful restart initiated\n");
break;
case 4:
case 5:
pr_info("Hibernation request received\n");
icmsghdrp->status = hibernation_supported ?
HV_S_OK : HV_E_FAIL;
if (hibernation_supported)
work = &hibernate_context.work;
break;
default:
icmsghdrp->status = HV_E_FAIL;
pr_info("Shutdown request received - Invalid request\n");
break;
}
} else {
icmsghdrp->status = HV_E_FAIL;
pr_err_ratelimited("Shutdown request received. Invalid msg type: %d\n",
icmsghdrp->icmsgtype);
} }
icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
| ICMSGHDRFLAG_RESPONSE;
vmbus_sendpacket(channel, shut_txf_buf,
recvlen, requestid,
VM_PKT_DATA_INBAND, 0);
if (work) if (work)
schedule_work(work); schedule_work(work);
} }
...@@ -396,19 +411,27 @@ static void timesync_onchannelcallback(void *context) ...@@ -396,19 +411,27 @@ static void timesync_onchannelcallback(void *context)
HV_HYP_PAGE_SIZE, &recvlen, HV_HYP_PAGE_SIZE, &recvlen,
&requestid); &requestid);
if (ret) { if (ret) {
pr_warn_once("TimeSync IC pkt recv failed (Err: %d)\n", pr_err_ratelimited("TimeSync IC pkt recv failed (Err: %d)\n",
ret); ret);
break; break;
} }
if (!recvlen) if (!recvlen)
break; break;
/* Ensure recvlen is big enough to read header data */
if (recvlen < ICMSG_HDR) {
pr_err_ratelimited("Timesync request received. Packet length too small: %d\n",
recvlen);
break;
}
icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[ icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[
sizeof(struct vmbuspipe_hdr)]; sizeof(struct vmbuspipe_hdr)];
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
if (vmbus_prep_negotiate_resp(icmsghdrp, time_txf_buf, if (vmbus_prep_negotiate_resp(icmsghdrp,
time_txf_buf, recvlen,
fw_versions, FW_VER_COUNT, fw_versions, FW_VER_COUNT,
ts_versions, TS_VER_COUNT, ts_versions, TS_VER_COUNT,
NULL, &ts_srv_version)) { NULL, &ts_srv_version)) {
...@@ -416,33 +439,44 @@ static void timesync_onchannelcallback(void *context) ...@@ -416,33 +439,44 @@ static void timesync_onchannelcallback(void *context)
ts_srv_version >> 16, ts_srv_version >> 16,
ts_srv_version & 0xFFFF); ts_srv_version & 0xFFFF);
} }
} else { } else if (icmsghdrp->icmsgtype == ICMSGTYPE_TIMESYNC) {
if (ts_srv_version > TS_VERSION_3) { if (ts_srv_version > TS_VERSION_3) {
refdata = (struct ictimesync_ref_data *) /* Ensure recvlen is big enough to read ictimesync_ref_data */
&time_txf_buf[ if (recvlen < ICMSG_HDR + sizeof(struct ictimesync_ref_data)) {
sizeof(struct vmbuspipe_hdr) + pr_err_ratelimited("Invalid ictimesync ref data. Length too small: %u\n",
sizeof(struct icmsg_hdr)]; recvlen);
break;
}
refdata = (struct ictimesync_ref_data *)&time_txf_buf[ICMSG_HDR];
adj_guesttime(refdata->parenttime, adj_guesttime(refdata->parenttime,
refdata->vmreferencetime, refdata->vmreferencetime,
refdata->flags); refdata->flags);
} else { } else {
timedatap = (struct ictimesync_data *) /* Ensure recvlen is big enough to read ictimesync_data */
&time_txf_buf[ if (recvlen < ICMSG_HDR + sizeof(struct ictimesync_data)) {
sizeof(struct vmbuspipe_hdr) + pr_err_ratelimited("Invalid ictimesync data. Length too small: %u\n",
sizeof(struct icmsg_hdr)]; recvlen);
break;
}
timedatap = (struct ictimesync_data *)&time_txf_buf[ICMSG_HDR];
adj_guesttime(timedatap->parenttime, adj_guesttime(timedatap->parenttime,
hv_read_reference_counter(), hv_read_reference_counter(),
timedatap->flags); timedatap->flags);
} }
} else {
icmsghdrp->status = HV_E_FAIL;
pr_err_ratelimited("Timesync request received. Invalid msg type: %d\n",
icmsghdrp->icmsgtype);
} }
icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
| ICMSGHDRFLAG_RESPONSE; | ICMSGHDRFLAG_RESPONSE;
vmbus_sendpacket(channel, time_txf_buf, vmbus_sendpacket(channel, time_txf_buf,
recvlen, requestid, recvlen, requestid,
VM_PKT_DATA_INBAND, 0); VM_PKT_DATA_INBAND, 0);
} }
} }
...@@ -462,18 +496,28 @@ static void heartbeat_onchannelcallback(void *context) ...@@ -462,18 +496,28 @@ static void heartbeat_onchannelcallback(void *context)
while (1) { while (1) {
vmbus_recvpacket(channel, hbeat_txf_buf, if (vmbus_recvpacket(channel, hbeat_txf_buf, HV_HYP_PAGE_SIZE,
HV_HYP_PAGE_SIZE, &recvlen, &requestid); &recvlen, &requestid)) {
pr_err_ratelimited("Heartbeat request received. Could not read into hbeat txf buf\n");
return;
}
if (!recvlen) if (!recvlen)
break; break;
/* Ensure recvlen is big enough to read header data */
if (recvlen < ICMSG_HDR) {
pr_err_ratelimited("Hearbeat request received. Packet length too small: %d\n",
recvlen);
break;
}
icmsghdrp = (struct icmsg_hdr *)&hbeat_txf_buf[ icmsghdrp = (struct icmsg_hdr *)&hbeat_txf_buf[
sizeof(struct vmbuspipe_hdr)]; sizeof(struct vmbuspipe_hdr)];
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
if (vmbus_prep_negotiate_resp(icmsghdrp, if (vmbus_prep_negotiate_resp(icmsghdrp,
hbeat_txf_buf, hbeat_txf_buf, recvlen,
fw_versions, FW_VER_COUNT, fw_versions, FW_VER_COUNT,
hb_versions, HB_VER_COUNT, hb_versions, HB_VER_COUNT,
NULL, &hb_srv_version)) { NULL, &hb_srv_version)) {
...@@ -482,21 +526,31 @@ static void heartbeat_onchannelcallback(void *context) ...@@ -482,21 +526,31 @@ static void heartbeat_onchannelcallback(void *context)
hb_srv_version >> 16, hb_srv_version >> 16,
hb_srv_version & 0xFFFF); hb_srv_version & 0xFFFF);
} }
} else { } else if (icmsghdrp->icmsgtype == ICMSGTYPE_HEARTBEAT) {
heartbeat_msg = /*
(struct heartbeat_msg_data *)&hbeat_txf_buf[ * Ensure recvlen is big enough to read seq_num. Reserved area is not
sizeof(struct vmbuspipe_hdr) + * included in the check as the host may not fill it up entirely
sizeof(struct icmsg_hdr)]; */
if (recvlen < ICMSG_HDR + sizeof(u64)) {
pr_err_ratelimited("Invalid heartbeat msg data. Length too small: %u\n",
recvlen);
break;
}
heartbeat_msg = (struct heartbeat_msg_data *)&hbeat_txf_buf[ICMSG_HDR];
heartbeat_msg->seq_num += 1; heartbeat_msg->seq_num += 1;
} else {
icmsghdrp->status = HV_E_FAIL;
pr_err_ratelimited("Heartbeat request received. Invalid msg type: %d\n",
icmsghdrp->icmsgtype);
} }
icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
| ICMSGHDRFLAG_RESPONSE; | ICMSGHDRFLAG_RESPONSE;
vmbus_sendpacket(channel, hbeat_txf_buf, vmbus_sendpacket(channel, hbeat_txf_buf,
recvlen, requestid, recvlen, requestid,
VM_PKT_DATA_INBAND, 0); VM_PKT_DATA_INBAND, 0);
} }
} }
......
...@@ -1480,6 +1480,7 @@ void vmbus_free_mmio(resource_size_t start, resource_size_t size); ...@@ -1480,6 +1480,7 @@ void vmbus_free_mmio(resource_size_t start, resource_size_t size);
#define ICMSGTYPE_SHUTDOWN 3 #define ICMSGTYPE_SHUTDOWN 3
#define ICMSGTYPE_TIMESYNC 4 #define ICMSGTYPE_TIMESYNC 4
#define ICMSGTYPE_VSS 5 #define ICMSGTYPE_VSS 5
#define ICMSGTYPE_FCOPY 7
#define ICMSGHDRFLAG_TRANSACTION 1 #define ICMSGHDRFLAG_TRANSACTION 1
#define ICMSGHDRFLAG_REQUEST 2 #define ICMSGHDRFLAG_REQUEST 2
...@@ -1523,6 +1524,12 @@ struct icmsg_hdr { ...@@ -1523,6 +1524,12 @@ struct icmsg_hdr {
u8 reserved[2]; u8 reserved[2];
} __packed; } __packed;
#define IC_VERSION_NEGOTIATION_MAX_VER_COUNT 100
#define ICMSG_HDR (sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr))
#define ICMSG_NEGOTIATE_PKT_SIZE(icframe_vercnt, icmsg_vercnt) \
(ICMSG_HDR + offsetof(struct icmsg_negotiate, icversion_data) + \
(((icframe_vercnt) + (icmsg_vercnt)) * sizeof(struct ic_version)))
struct icmsg_negotiate { struct icmsg_negotiate {
u16 icframe_vercnt; u16 icframe_vercnt;
u16 icmsg_vercnt; u16 icmsg_vercnt;
...@@ -1578,7 +1585,7 @@ struct hyperv_service_callback { ...@@ -1578,7 +1585,7 @@ struct hyperv_service_callback {
}; };
#define MAX_SRV_VER 0x7ffffff #define MAX_SRV_VER 0x7ffffff
extern bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, u8 *buf, extern bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, u8 *buf, u32 buflen,
const int *fw_version, int fw_vercnt, const int *fw_version, int fw_vercnt,
const int *srv_version, int srv_vercnt, const int *srv_version, int srv_vercnt,
int *nego_fw_version, int *nego_srv_version); int *nego_fw_version, int *nego_srv_version);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment