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

virt/sev-guest: Prevent IV reuse in the SNP guest driver

The AMD Secure Processor (ASP) and an SNP guest use a series of
AES-GCM keys called VMPCKs to communicate securely with each other.
The IV to this scheme is a sequence number that both the ASP and the
guest track.

Currently, this sequence number in a guest request must exactly match
the sequence number tracked by the ASP. This means that if the guest
sees an error from the host during a request it can only retry that
exact request or disable the VMPCK to prevent an IV reuse. AES-GCM
cannot tolerate IV reuse, see: "Authentication Failures in NIST version
of GCM" - Antoine Joux et al.

In order to address this, make handle_guest_request() delete the VMPCK
on any non successful return. To allow userspace querying the cert_data
length make handle_guest_request() save the number of pages required by
the host, then have handle_guest_request() retry the request without
requesting the extended data, then return the number of pages required
back to userspace.

[ bp: Massage, incorporate Tom's review comments. ]

Fixes: fce96cf044308 ("virt: Add SEV-SNP guest driver")
Reported-by: Peter Gonda <pgonda@google.com>
Signed-off-by: Peter Gonda <pgonda@google.com>
Signed-off-by: Borislav Petkov <bp@suse.de>
Reviewed-by: Tom Lendacky <thomas.lendacky@amd.com>
Cc: stable@kernel.org
Link: https://lore.kernel.org/r/20221116175558.2373112-1-pgonda@google.com

authored by

Peter Gonda and committed by
Borislav Petkov
47894e0f eb708140

+70 -14
+70 -14
drivers/virt/coco/sev-guest/sev-guest.c
··· 67 67 return true; 68 68 } 69 69 70 + /* 71 + * If an error is received from the host or AMD Secure Processor (ASP) there 72 + * are two options. Either retry the exact same encrypted request or discontinue 73 + * using the VMPCK. 74 + * 75 + * This is because in the current encryption scheme GHCB v2 uses AES-GCM to 76 + * encrypt the requests. The IV for this scheme is the sequence number. GCM 77 + * cannot tolerate IV reuse. 78 + * 79 + * The ASP FW v1.51 only increments the sequence numbers on a successful 80 + * guest<->ASP back and forth and only accepts messages at its exact sequence 81 + * number. 82 + * 83 + * So if the sequence number were to be reused the encryption scheme is 84 + * vulnerable. If the sequence number were incremented for a fresh IV the ASP 85 + * will reject the request. 86 + */ 70 87 static void snp_disable_vmpck(struct snp_guest_dev *snp_dev) 71 88 { 89 + dev_alert(snp_dev->dev, "Disabling vmpck_id %d to prevent IV reuse.\n", 90 + vmpck_id); 72 91 memzero_explicit(snp_dev->vmpck, VMPCK_KEY_LEN); 73 92 snp_dev->vmpck = NULL; 74 93 } ··· 340 321 if (rc) 341 322 return rc; 342 323 343 - /* Call firmware to process the request */ 324 + /* 325 + * Call firmware to process the request. In this function the encrypted 326 + * message enters shared memory with the host. So after this call the 327 + * sequence number must be incremented or the VMPCK must be deleted to 328 + * prevent reuse of the IV. 329 + */ 344 330 rc = snp_issue_guest_request(exit_code, &snp_dev->input, &err); 331 + 332 + /* 333 + * If the extended guest request fails due to having too small of a 334 + * certificate data buffer, retry the same guest request without the 335 + * extended data request in order to increment the sequence number 336 + * and thus avoid IV reuse. 337 + */ 338 + if (exit_code == SVM_VMGEXIT_EXT_GUEST_REQUEST && 339 + err == SNP_GUEST_REQ_INVALID_LEN) { 340 + const unsigned int certs_npages = snp_dev->input.data_npages; 341 + 342 + exit_code = SVM_VMGEXIT_GUEST_REQUEST; 343 + 344 + /* 345 + * If this call to the firmware succeeds, the sequence number can 346 + * be incremented allowing for continued use of the VMPCK. If 347 + * there is an error reflected in the return value, this value 348 + * is checked further down and the result will be the deletion 349 + * of the VMPCK and the error code being propagated back to the 350 + * user as an ioctl() return code. 351 + */ 352 + rc = snp_issue_guest_request(exit_code, &snp_dev->input, &err); 353 + 354 + /* 355 + * Override the error to inform callers the given extended 356 + * request buffer size was too small and give the caller the 357 + * required buffer size. 358 + */ 359 + err = SNP_GUEST_REQ_INVALID_LEN; 360 + snp_dev->input.data_npages = certs_npages; 361 + } 362 + 345 363 if (fw_err) 346 364 *fw_err = err; 347 365 348 - if (rc) 349 - return rc; 366 + if (rc) { 367 + dev_alert(snp_dev->dev, 368 + "Detected error from ASP request. rc: %d, fw_err: %llu\n", 369 + rc, *fw_err); 370 + goto disable_vmpck; 371 + } 350 372 351 - /* 352 - * The verify_and_dec_payload() will fail only if the hypervisor is 353 - * actively modifying the message header or corrupting the encrypted payload. 354 - * This hints that hypervisor is acting in a bad faith. Disable the VMPCK so that 355 - * the key cannot be used for any communication. The key is disabled to ensure 356 - * that AES-GCM does not use the same IV while encrypting the request payload. 357 - */ 358 373 rc = verify_and_dec_payload(snp_dev, resp_buf, resp_sz); 359 374 if (rc) { 360 375 dev_alert(snp_dev->dev, 361 - "Detected unexpected decode failure, disabling the vmpck_id %d\n", 362 - vmpck_id); 363 - snp_disable_vmpck(snp_dev); 364 - return rc; 376 + "Detected unexpected decode failure from ASP. rc: %d\n", 377 + rc); 378 + goto disable_vmpck; 365 379 } 366 380 367 381 /* Increment to new message sequence after payload decryption was successful. */ 368 382 snp_inc_msg_seqno(snp_dev); 369 383 370 384 return 0; 385 + 386 + disable_vmpck: 387 + snp_disable_vmpck(snp_dev); 388 + return rc; 371 389 } 372 390 373 391 static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg)