Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: 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/20201109100704.9152-1-parri.andrea@gmail.com
Signed-off-by: Wei Liu <wei.liu@kernel.org>

authored by

Andres Beltran and committed by
Wei Liu
06caa778 a8c32099

+327 -201
+19 -5
drivers/hv/channel_mgmt.c
··· 190 190 * vmbus_prep_negotiate_resp() - Create default response for Negotiate message 191 191 * @icmsghdrp: Pointer to msg header structure 192 192 * @buf: Raw buffer channel data 193 + * @buflen: Length of the raw buffer channel data. 193 194 * @fw_version: The framework versions we can support. 194 195 * @fw_vercnt: The size of @fw_version. 195 196 * @srv_version: The service versions we can support. ··· 203 202 * Set up and fill in default negotiate response message. 204 203 * Mainly used by Hyper-V drivers. 205 204 */ 206 - bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, 207 - u8 *buf, const int *fw_version, int fw_vercnt, 205 + bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, u8 *buf, 206 + u32 buflen, const int *fw_version, int fw_vercnt, 208 207 const int *srv_version, int srv_vercnt, 209 208 int *nego_fw_version, int *nego_srv_version) 210 209 { ··· 216 215 bool found_match = false; 217 216 struct icmsg_negotiate *negop; 218 217 218 + /* Check that there's enough space for icframe_vercnt, icmsg_vercnt */ 219 + if (buflen < ICMSG_HDR + offsetof(struct icmsg_negotiate, reserved)) { 220 + pr_err_ratelimited("Invalid icmsg negotiate\n"); 221 + return false; 222 + } 223 + 219 224 icmsghdrp->icmsgsize = 0x10; 220 - negop = (struct icmsg_negotiate *)&buf[ 221 - sizeof(struct vmbuspipe_hdr) + 222 - sizeof(struct icmsg_hdr)]; 225 + negop = (struct icmsg_negotiate *)&buf[ICMSG_HDR]; 223 226 224 227 icframe_major = negop->icframe_vercnt; 225 228 icframe_minor = 0; 226 229 227 230 icmsg_major = negop->icmsg_vercnt; 228 231 icmsg_minor = 0; 232 + 233 + /* Validate negop packet */ 234 + if (icframe_major > IC_VERSION_NEGOTIATION_MAX_VER_COUNT || 235 + icmsg_major > IC_VERSION_NEGOTIATION_MAX_VER_COUNT || 236 + ICMSG_NEGOTIATE_PKT_SIZE(icframe_major, icmsg_major) > buflen) { 237 + pr_err_ratelimited("Invalid icmsg negotiate - icframe_major: %u, icmsg_major: %u\n", 238 + icframe_major, icmsg_major); 239 + goto fw_error; 240 + } 229 241 230 242 /* 231 243 * Select the framework version number we will
+28 -8
drivers/hv/hv_fcopy.c
··· 235 235 if (fcopy_transaction.state > HVUTIL_READY) 236 236 return; 237 237 238 - vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 2, &recvlen, 239 - &requestid); 240 - if (recvlen <= 0) 238 + if (vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 2, &recvlen, &requestid)) { 239 + pr_err_ratelimited("Fcopy request received. Could not read into recv buf\n"); 241 240 return; 241 + } 242 + 243 + if (!recvlen) 244 + return; 245 + 246 + /* Ensure recvlen is big enough to read header data */ 247 + if (recvlen < ICMSG_HDR) { 248 + pr_err_ratelimited("Fcopy request received. Packet length too small: %d\n", 249 + recvlen); 250 + return; 251 + } 242 252 243 253 icmsghdr = (struct icmsg_hdr *)&recv_buffer[ 244 254 sizeof(struct vmbuspipe_hdr)]; 255 + 245 256 if (icmsghdr->icmsgtype == ICMSGTYPE_NEGOTIATE) { 246 - if (vmbus_prep_negotiate_resp(icmsghdr, recv_buffer, 257 + if (vmbus_prep_negotiate_resp(icmsghdr, 258 + recv_buffer, recvlen, 247 259 fw_versions, FW_VER_COUNT, 248 260 fcopy_versions, FCOPY_VER_COUNT, 249 261 NULL, &fcopy_srv_version)) { ··· 264 252 fcopy_srv_version >> 16, 265 253 fcopy_srv_version & 0xFFFF); 266 254 } 267 - } else { 268 - fcopy_msg = (struct hv_fcopy_hdr *)&recv_buffer[ 269 - sizeof(struct vmbuspipe_hdr) + 270 - sizeof(struct icmsg_hdr)]; 255 + } else if (icmsghdr->icmsgtype == ICMSGTYPE_FCOPY) { 256 + /* Ensure recvlen is big enough to contain hv_fcopy_hdr */ 257 + if (recvlen < ICMSG_HDR + sizeof(struct hv_fcopy_hdr)) { 258 + pr_err_ratelimited("Invalid Fcopy hdr. Packet length too small: %u\n", 259 + recvlen); 260 + return; 261 + } 262 + fcopy_msg = (struct hv_fcopy_hdr *)&recv_buffer[ICMSG_HDR]; 271 263 272 264 /* 273 265 * Stash away this global state for completing the ··· 295 279 schedule_work(&fcopy_send_work); 296 280 schedule_delayed_work(&fcopy_timeout_work, 297 281 HV_UTIL_TIMEOUT * HZ); 282 + return; 283 + } else { 284 + pr_err_ratelimited("Fcopy request received. Invalid msg type: %d\n", 285 + icmsghdr->icmsgtype); 298 286 return; 299 287 } 300 288 icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
+79 -63
drivers/hv/hv_kvp.c
··· 662 662 if (kvp_transaction.state > HVUTIL_READY) 663 663 return; 664 664 665 - vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 4, &recvlen, 666 - &requestid); 667 - 668 - if (recvlen > 0) { 669 - icmsghdrp = (struct icmsg_hdr *)&recv_buffer[ 670 - sizeof(struct vmbuspipe_hdr)]; 671 - 672 - if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { 673 - if (vmbus_prep_negotiate_resp(icmsghdrp, 674 - recv_buffer, fw_versions, FW_VER_COUNT, 675 - kvp_versions, KVP_VER_COUNT, 676 - NULL, &kvp_srv_version)) { 677 - pr_info("KVP IC version %d.%d\n", 678 - kvp_srv_version >> 16, 679 - kvp_srv_version & 0xFFFF); 680 - } 681 - } else { 682 - kvp_msg = (struct hv_kvp_msg *)&recv_buffer[ 683 - sizeof(struct vmbuspipe_hdr) + 684 - sizeof(struct icmsg_hdr)]; 685 - 686 - /* 687 - * Stash away this global state for completing the 688 - * transaction; note transactions are serialized. 689 - */ 690 - 691 - kvp_transaction.recv_len = recvlen; 692 - kvp_transaction.recv_req_id = requestid; 693 - kvp_transaction.kvp_msg = kvp_msg; 694 - 695 - if (kvp_transaction.state < HVUTIL_READY) { 696 - /* Userspace is not registered yet */ 697 - kvp_respond_to_host(NULL, HV_E_FAIL); 698 - return; 699 - } 700 - kvp_transaction.state = HVUTIL_HOSTMSG_RECEIVED; 701 - 702 - /* 703 - * Get the information from the 704 - * user-mode component. 705 - * component. This transaction will be 706 - * completed when we get the value from 707 - * the user-mode component. 708 - * Set a timeout to deal with 709 - * user-mode not responding. 710 - */ 711 - schedule_work(&kvp_sendkey_work); 712 - schedule_delayed_work(&kvp_timeout_work, 713 - HV_UTIL_TIMEOUT * HZ); 714 - 715 - return; 716 - 717 - } 718 - 719 - icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION 720 - | ICMSGHDRFLAG_RESPONSE; 721 - 722 - vmbus_sendpacket(channel, recv_buffer, 723 - recvlen, requestid, 724 - VM_PKT_DATA_INBAND, 0); 725 - 726 - host_negotiatied = NEGO_FINISHED; 727 - hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper); 665 + if (vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 4, &recvlen, &requestid)) { 666 + pr_err_ratelimited("KVP request received. Could not read into recv buf\n"); 667 + return; 728 668 } 729 669 670 + if (!recvlen) 671 + return; 672 + 673 + /* Ensure recvlen is big enough to read header data */ 674 + if (recvlen < ICMSG_HDR) { 675 + pr_err_ratelimited("KVP request received. Packet length too small: %d\n", 676 + recvlen); 677 + return; 678 + } 679 + 680 + icmsghdrp = (struct icmsg_hdr *)&recv_buffer[sizeof(struct vmbuspipe_hdr)]; 681 + 682 + if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { 683 + if (vmbus_prep_negotiate_resp(icmsghdrp, 684 + recv_buffer, recvlen, 685 + fw_versions, FW_VER_COUNT, 686 + kvp_versions, KVP_VER_COUNT, 687 + NULL, &kvp_srv_version)) { 688 + pr_info("KVP IC version %d.%d\n", 689 + kvp_srv_version >> 16, 690 + kvp_srv_version & 0xFFFF); 691 + } 692 + } else if (icmsghdrp->icmsgtype == ICMSGTYPE_KVPEXCHANGE) { 693 + /* 694 + * recvlen is not checked against sizeof(struct kvp_msg) because kvp_msg contains 695 + * a union of structs and the msg type received is not known. Code using this 696 + * struct should provide validation when accessing its fields. 697 + */ 698 + kvp_msg = (struct hv_kvp_msg *)&recv_buffer[ICMSG_HDR]; 699 + 700 + /* 701 + * Stash away this global state for completing the 702 + * transaction; note transactions are serialized. 703 + */ 704 + 705 + kvp_transaction.recv_len = recvlen; 706 + kvp_transaction.recv_req_id = requestid; 707 + kvp_transaction.kvp_msg = kvp_msg; 708 + 709 + if (kvp_transaction.state < HVUTIL_READY) { 710 + /* Userspace is not registered yet */ 711 + kvp_respond_to_host(NULL, HV_E_FAIL); 712 + return; 713 + } 714 + kvp_transaction.state = HVUTIL_HOSTMSG_RECEIVED; 715 + 716 + /* 717 + * Get the information from the 718 + * user-mode component. 719 + * component. This transaction will be 720 + * completed when we get the value from 721 + * the user-mode component. 722 + * Set a timeout to deal with 723 + * user-mode not responding. 724 + */ 725 + schedule_work(&kvp_sendkey_work); 726 + schedule_delayed_work(&kvp_timeout_work, 727 + HV_UTIL_TIMEOUT * HZ); 728 + 729 + return; 730 + 731 + } else { 732 + pr_err_ratelimited("KVP request received. Invalid msg type: %d\n", 733 + icmsghdrp->icmsgtype); 734 + return; 735 + } 736 + 737 + icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION 738 + | ICMSGHDRFLAG_RESPONSE; 739 + 740 + vmbus_sendpacket(channel, recv_buffer, 741 + recvlen, requestid, 742 + VM_PKT_DATA_INBAND, 0); 743 + 744 + host_negotiatied = NEGO_FINISHED; 745 + hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper); 730 746 } 731 747 732 748 static void kvp_on_reset(void)
+56 -41
drivers/hv/hv_snapshot.c
··· 298 298 if (vss_transaction.state > HVUTIL_READY) 299 299 return; 300 300 301 - vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 2, &recvlen, 302 - &requestid); 303 - 304 - if (recvlen > 0) { 305 - icmsghdrp = (struct icmsg_hdr *)&recv_buffer[ 306 - sizeof(struct vmbuspipe_hdr)]; 307 - 308 - if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { 309 - if (vmbus_prep_negotiate_resp(icmsghdrp, 310 - recv_buffer, fw_versions, FW_VER_COUNT, 311 - vss_versions, VSS_VER_COUNT, 312 - NULL, &vss_srv_version)) { 313 - 314 - pr_info("VSS IC version %d.%d\n", 315 - vss_srv_version >> 16, 316 - vss_srv_version & 0xFFFF); 317 - } 318 - } else { 319 - vss_msg = (struct hv_vss_msg *)&recv_buffer[ 320 - sizeof(struct vmbuspipe_hdr) + 321 - sizeof(struct icmsg_hdr)]; 322 - 323 - /* 324 - * Stash away this global state for completing the 325 - * transaction; note transactions are serialized. 326 - */ 327 - 328 - vss_transaction.recv_len = recvlen; 329 - vss_transaction.recv_req_id = requestid; 330 - vss_transaction.msg = (struct hv_vss_msg *)vss_msg; 331 - 332 - schedule_work(&vss_handle_request_work); 333 - return; 334 - } 335 - 336 - icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION 337 - | ICMSGHDRFLAG_RESPONSE; 338 - 339 - vmbus_sendpacket(channel, recv_buffer, 340 - recvlen, requestid, 341 - VM_PKT_DATA_INBAND, 0); 301 + if (vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 2, &recvlen, &requestid)) { 302 + pr_err_ratelimited("VSS request received. Could not read into recv buf\n"); 303 + return; 342 304 } 343 305 306 + if (!recvlen) 307 + return; 308 + 309 + /* Ensure recvlen is big enough to read header data */ 310 + if (recvlen < ICMSG_HDR) { 311 + pr_err_ratelimited("VSS request received. Packet length too small: %d\n", 312 + recvlen); 313 + return; 314 + } 315 + 316 + icmsghdrp = (struct icmsg_hdr *)&recv_buffer[sizeof(struct vmbuspipe_hdr)]; 317 + 318 + if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { 319 + if (vmbus_prep_negotiate_resp(icmsghdrp, 320 + recv_buffer, recvlen, 321 + fw_versions, FW_VER_COUNT, 322 + vss_versions, VSS_VER_COUNT, 323 + NULL, &vss_srv_version)) { 324 + 325 + pr_info("VSS IC version %d.%d\n", 326 + vss_srv_version >> 16, 327 + vss_srv_version & 0xFFFF); 328 + } 329 + } else if (icmsghdrp->icmsgtype == ICMSGTYPE_VSS) { 330 + /* Ensure recvlen is big enough to contain hv_vss_msg */ 331 + if (recvlen < ICMSG_HDR + sizeof(struct hv_vss_msg)) { 332 + pr_err_ratelimited("Invalid VSS msg. Packet length too small: %u\n", 333 + recvlen); 334 + return; 335 + } 336 + vss_msg = (struct hv_vss_msg *)&recv_buffer[ICMSG_HDR]; 337 + 338 + /* 339 + * Stash away this global state for completing the 340 + * transaction; note transactions are serialized. 341 + */ 342 + 343 + vss_transaction.recv_len = recvlen; 344 + vss_transaction.recv_req_id = requestid; 345 + vss_transaction.msg = (struct hv_vss_msg *)vss_msg; 346 + 347 + schedule_work(&vss_handle_request_work); 348 + return; 349 + } else { 350 + pr_err_ratelimited("VSS request received. Invalid msg type: %d\n", 351 + icmsghdrp->icmsgtype); 352 + return; 353 + } 354 + 355 + icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | 356 + ICMSGHDRFLAG_RESPONSE; 357 + vmbus_sendpacket(channel, recv_buffer, recvlen, requestid, 358 + VM_PKT_DATA_INBAND, 0); 344 359 } 345 360 346 361 static void vss_on_reset(void)
+137 -83
drivers/hv/hv_util.c
··· 195 195 196 196 struct icmsg_hdr *icmsghdrp; 197 197 198 - vmbus_recvpacket(channel, shut_txf_buf, 199 - HV_HYP_PAGE_SIZE, &recvlen, &requestid); 198 + if (vmbus_recvpacket(channel, shut_txf_buf, HV_HYP_PAGE_SIZE, &recvlen, &requestid)) { 199 + pr_err_ratelimited("Shutdown request received. Could not read into shut txf buf\n"); 200 + return; 201 + } 200 202 201 - if (recvlen > 0) { 202 - icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[ 203 - sizeof(struct vmbuspipe_hdr)]; 203 + if (!recvlen) 204 + return; 204 205 205 - if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { 206 - if (vmbus_prep_negotiate_resp(icmsghdrp, shut_txf_buf, 207 - fw_versions, FW_VER_COUNT, 208 - sd_versions, SD_VER_COUNT, 209 - NULL, &sd_srv_version)) { 210 - pr_info("Shutdown IC version %d.%d\n", 211 - sd_srv_version >> 16, 212 - sd_srv_version & 0xFFFF); 213 - } 214 - } else { 215 - shutdown_msg = 216 - (struct shutdown_msg_data *)&shut_txf_buf[ 217 - sizeof(struct vmbuspipe_hdr) + 218 - sizeof(struct icmsg_hdr)]; 206 + /* Ensure recvlen is big enough to read header data */ 207 + if (recvlen < ICMSG_HDR) { 208 + pr_err_ratelimited("Shutdown request received. Packet length too small: %d\n", 209 + recvlen); 210 + return; 211 + } 219 212 220 - /* 221 - * shutdown_msg->flags can be 0(shut down), 2(reboot), 222 - * or 4(hibernate). It may bitwise-OR 1, which means 223 - * performing the request by force. Linux always tries 224 - * to perform the request by force. 225 - */ 226 - switch (shutdown_msg->flags) { 227 - case 0: 228 - case 1: 229 - icmsghdrp->status = HV_S_OK; 230 - work = &shutdown_work; 231 - pr_info("Shutdown request received -" 232 - " graceful shutdown initiated\n"); 233 - break; 234 - case 2: 235 - case 3: 236 - icmsghdrp->status = HV_S_OK; 237 - work = &restart_work; 238 - pr_info("Restart request received -" 239 - " graceful restart initiated\n"); 240 - break; 241 - case 4: 242 - case 5: 243 - pr_info("Hibernation request received\n"); 244 - icmsghdrp->status = hibernation_supported ? 245 - HV_S_OK : HV_E_FAIL; 246 - if (hibernation_supported) 247 - work = &hibernate_context.work; 248 - break; 249 - default: 250 - icmsghdrp->status = HV_E_FAIL; 251 - pr_info("Shutdown request received -" 252 - " Invalid request\n"); 253 - break; 254 - } 213 + icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[sizeof(struct vmbuspipe_hdr)]; 214 + 215 + if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { 216 + if (vmbus_prep_negotiate_resp(icmsghdrp, 217 + shut_txf_buf, recvlen, 218 + fw_versions, FW_VER_COUNT, 219 + sd_versions, SD_VER_COUNT, 220 + NULL, &sd_srv_version)) { 221 + pr_info("Shutdown IC version %d.%d\n", 222 + sd_srv_version >> 16, 223 + sd_srv_version & 0xFFFF); 224 + } 225 + } else if (icmsghdrp->icmsgtype == ICMSGTYPE_SHUTDOWN) { 226 + /* Ensure recvlen is big enough to contain shutdown_msg_data struct */ 227 + if (recvlen < ICMSG_HDR + sizeof(struct shutdown_msg_data)) { 228 + pr_err_ratelimited("Invalid shutdown msg data. Packet length too small: %u\n", 229 + recvlen); 230 + return; 255 231 } 256 232 257 - icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION 258 - | ICMSGHDRFLAG_RESPONSE; 233 + shutdown_msg = (struct shutdown_msg_data *)&shut_txf_buf[ICMSG_HDR]; 259 234 260 - vmbus_sendpacket(channel, shut_txf_buf, 261 - recvlen, requestid, 262 - VM_PKT_DATA_INBAND, 0); 235 + /* 236 + * shutdown_msg->flags can be 0(shut down), 2(reboot), 237 + * or 4(hibernate). It may bitwise-OR 1, which means 238 + * performing the request by force. Linux always tries 239 + * to perform the request by force. 240 + */ 241 + switch (shutdown_msg->flags) { 242 + case 0: 243 + case 1: 244 + icmsghdrp->status = HV_S_OK; 245 + work = &shutdown_work; 246 + pr_info("Shutdown request received - graceful shutdown initiated\n"); 247 + break; 248 + case 2: 249 + case 3: 250 + icmsghdrp->status = HV_S_OK; 251 + work = &restart_work; 252 + pr_info("Restart request received - graceful restart initiated\n"); 253 + break; 254 + case 4: 255 + case 5: 256 + pr_info("Hibernation request received\n"); 257 + icmsghdrp->status = hibernation_supported ? 258 + HV_S_OK : HV_E_FAIL; 259 + if (hibernation_supported) 260 + work = &hibernate_context.work; 261 + break; 262 + default: 263 + icmsghdrp->status = HV_E_FAIL; 264 + pr_info("Shutdown request received - Invalid request\n"); 265 + break; 266 + } 267 + } else { 268 + icmsghdrp->status = HV_E_FAIL; 269 + pr_err_ratelimited("Shutdown request received. Invalid msg type: %d\n", 270 + icmsghdrp->icmsgtype); 263 271 } 272 + 273 + icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION 274 + | ICMSGHDRFLAG_RESPONSE; 275 + 276 + vmbus_sendpacket(channel, shut_txf_buf, 277 + recvlen, requestid, 278 + VM_PKT_DATA_INBAND, 0); 264 279 265 280 if (work) 266 281 schedule_work(work); ··· 411 396 HV_HYP_PAGE_SIZE, &recvlen, 412 397 &requestid); 413 398 if (ret) { 414 - pr_warn_once("TimeSync IC pkt recv failed (Err: %d)\n", 415 - ret); 399 + pr_err_ratelimited("TimeSync IC pkt recv failed (Err: %d)\n", 400 + ret); 416 401 break; 417 402 } 418 403 419 404 if (!recvlen) 420 405 break; 421 406 407 + /* Ensure recvlen is big enough to read header data */ 408 + if (recvlen < ICMSG_HDR) { 409 + pr_err_ratelimited("Timesync request received. Packet length too small: %d\n", 410 + recvlen); 411 + break; 412 + } 413 + 422 414 icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[ 423 415 sizeof(struct vmbuspipe_hdr)]; 424 416 425 417 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { 426 - if (vmbus_prep_negotiate_resp(icmsghdrp, time_txf_buf, 418 + if (vmbus_prep_negotiate_resp(icmsghdrp, 419 + time_txf_buf, recvlen, 427 420 fw_versions, FW_VER_COUNT, 428 421 ts_versions, TS_VER_COUNT, 429 422 NULL, &ts_srv_version)) { ··· 439 416 ts_srv_version >> 16, 440 417 ts_srv_version & 0xFFFF); 441 418 } 442 - } else { 419 + } else if (icmsghdrp->icmsgtype == ICMSGTYPE_TIMESYNC) { 443 420 if (ts_srv_version > TS_VERSION_3) { 444 - refdata = (struct ictimesync_ref_data *) 445 - &time_txf_buf[ 446 - sizeof(struct vmbuspipe_hdr) + 447 - sizeof(struct icmsg_hdr)]; 421 + /* Ensure recvlen is big enough to read ictimesync_ref_data */ 422 + if (recvlen < ICMSG_HDR + sizeof(struct ictimesync_ref_data)) { 423 + pr_err_ratelimited("Invalid ictimesync ref data. Length too small: %u\n", 424 + recvlen); 425 + break; 426 + } 427 + refdata = (struct ictimesync_ref_data *)&time_txf_buf[ICMSG_HDR]; 448 428 449 429 adj_guesttime(refdata->parenttime, 450 430 refdata->vmreferencetime, 451 431 refdata->flags); 452 432 } else { 453 - timedatap = (struct ictimesync_data *) 454 - &time_txf_buf[ 455 - sizeof(struct vmbuspipe_hdr) + 456 - sizeof(struct icmsg_hdr)]; 433 + /* Ensure recvlen is big enough to read ictimesync_data */ 434 + if (recvlen < ICMSG_HDR + sizeof(struct ictimesync_data)) { 435 + pr_err_ratelimited("Invalid ictimesync data. Length too small: %u\n", 436 + recvlen); 437 + break; 438 + } 439 + timedatap = (struct ictimesync_data *)&time_txf_buf[ICMSG_HDR]; 440 + 457 441 adj_guesttime(timedatap->parenttime, 458 442 hv_read_reference_counter(), 459 443 timedatap->flags); 460 444 } 445 + } else { 446 + icmsghdrp->status = HV_E_FAIL; 447 + pr_err_ratelimited("Timesync request received. Invalid msg type: %d\n", 448 + icmsghdrp->icmsgtype); 461 449 } 462 450 463 451 icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION 464 452 | ICMSGHDRFLAG_RESPONSE; 465 453 466 454 vmbus_sendpacket(channel, time_txf_buf, 467 - recvlen, requestid, 468 - VM_PKT_DATA_INBAND, 0); 455 + recvlen, requestid, 456 + VM_PKT_DATA_INBAND, 0); 469 457 } 470 458 } 471 459 ··· 496 462 497 463 while (1) { 498 464 499 - vmbus_recvpacket(channel, hbeat_txf_buf, 500 - HV_HYP_PAGE_SIZE, &recvlen, &requestid); 465 + if (vmbus_recvpacket(channel, hbeat_txf_buf, HV_HYP_PAGE_SIZE, 466 + &recvlen, &requestid)) { 467 + pr_err_ratelimited("Heartbeat request received. Could not read into hbeat txf buf\n"); 468 + return; 469 + } 501 470 502 471 if (!recvlen) 503 472 break; 473 + 474 + /* Ensure recvlen is big enough to read header data */ 475 + if (recvlen < ICMSG_HDR) { 476 + pr_err_ratelimited("Hearbeat request received. Packet length too small: %d\n", 477 + recvlen); 478 + break; 479 + } 504 480 505 481 icmsghdrp = (struct icmsg_hdr *)&hbeat_txf_buf[ 506 482 sizeof(struct vmbuspipe_hdr)]; 507 483 508 484 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { 509 485 if (vmbus_prep_negotiate_resp(icmsghdrp, 510 - hbeat_txf_buf, 486 + hbeat_txf_buf, recvlen, 511 487 fw_versions, FW_VER_COUNT, 512 488 hb_versions, HB_VER_COUNT, 513 489 NULL, &hb_srv_version)) { ··· 526 482 hb_srv_version >> 16, 527 483 hb_srv_version & 0xFFFF); 528 484 } 529 - } else { 530 - heartbeat_msg = 531 - (struct heartbeat_msg_data *)&hbeat_txf_buf[ 532 - sizeof(struct vmbuspipe_hdr) + 533 - sizeof(struct icmsg_hdr)]; 485 + } else if (icmsghdrp->icmsgtype == ICMSGTYPE_HEARTBEAT) { 486 + /* 487 + * Ensure recvlen is big enough to read seq_num. Reserved area is not 488 + * included in the check as the host may not fill it up entirely 489 + */ 490 + if (recvlen < ICMSG_HDR + sizeof(u64)) { 491 + pr_err_ratelimited("Invalid heartbeat msg data. Length too small: %u\n", 492 + recvlen); 493 + break; 494 + } 495 + heartbeat_msg = (struct heartbeat_msg_data *)&hbeat_txf_buf[ICMSG_HDR]; 534 496 535 497 heartbeat_msg->seq_num += 1; 498 + } else { 499 + icmsghdrp->status = HV_E_FAIL; 500 + pr_err_ratelimited("Heartbeat request received. Invalid msg type: %d\n", 501 + icmsghdrp->icmsgtype); 536 502 } 537 503 538 504 icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION 539 505 | ICMSGHDRFLAG_RESPONSE; 540 506 541 507 vmbus_sendpacket(channel, hbeat_txf_buf, 542 - recvlen, requestid, 543 - VM_PKT_DATA_INBAND, 0); 508 + recvlen, requestid, 509 + VM_PKT_DATA_INBAND, 0); 544 510 } 545 511 } 546 512
+8 -1
include/linux/hyperv.h
··· 1480 1480 #define ICMSGTYPE_SHUTDOWN 3 1481 1481 #define ICMSGTYPE_TIMESYNC 4 1482 1482 #define ICMSGTYPE_VSS 5 1483 + #define ICMSGTYPE_FCOPY 7 1483 1484 1484 1485 #define ICMSGHDRFLAG_TRANSACTION 1 1485 1486 #define ICMSGHDRFLAG_REQUEST 2 ··· 1523 1522 u8 icflags; 1524 1523 u8 reserved[2]; 1525 1524 } __packed; 1525 + 1526 + #define IC_VERSION_NEGOTIATION_MAX_VER_COUNT 100 1527 + #define ICMSG_HDR (sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr)) 1528 + #define ICMSG_NEGOTIATE_PKT_SIZE(icframe_vercnt, icmsg_vercnt) \ 1529 + (ICMSG_HDR + offsetof(struct icmsg_negotiate, icversion_data) + \ 1530 + (((icframe_vercnt) + (icmsg_vercnt)) * sizeof(struct ic_version))) 1526 1531 1527 1532 struct icmsg_negotiate { 1528 1533 u16 icframe_vercnt; ··· 1585 1578 }; 1586 1579 1587 1580 #define MAX_SRV_VER 0x7ffffff 1588 - extern bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, u8 *buf, 1581 + extern bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, u8 *buf, u32 buflen, 1589 1582 const int *fw_version, int fw_vercnt, 1590 1583 const int *srv_version, int srv_vercnt, 1591 1584 int *nego_fw_version, int *nego_srv_version);