From: Stefan Berger <stefanb@linux.ibm.com>
To: qemu-devel@nongnu.org
Cc: berrange@redhat.com, marcandre.lureau@gmail.com,
Stefan Berger <stefanb@linux.ibm.com>
Subject: [PATCH v4 2/2] tpm_emulator: Read control channel response in 2 passes
Date: Wed, 16 Oct 2024 13:51:29 -0400 [thread overview]
Message-ID: <20241016175129.1319176-3-stefanb@linux.ibm.com> (raw)
In-Reply-To: <20241016175129.1319176-1-stefanb@linux.ibm.com>
Error responses from swtpm are typically only 4 bytes long with the
exception of a few commands that return more bytes. Therefore, read the
entire response in 2 steps and stop if the first few bytes indicate an
error response with no subsequent bytes readable. Read the rest in a 2nd
step, if needed. This avoids getting stuck while waiting for too many
bytes in case of an error. The 'getting stuck' condition has not been
observed in practice so far, though.
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2615
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
backends/tpm/tpm_emulator.c | 61 +++++++++++++++++++++++++++----------
1 file changed, 45 insertions(+), 16 deletions(-)
diff --git a/backends/tpm/tpm_emulator.c b/backends/tpm/tpm_emulator.c
index b0e2fb3fc7..8ad54f49a5 100644
--- a/backends/tpm/tpm_emulator.c
+++ b/backends/tpm/tpm_emulator.c
@@ -123,12 +123,14 @@ static const char *tpm_emulator_strerror(uint32_t tpm_result)
}
static int tpm_emulator_ctrlcmd(TPMEmulator *tpm, unsigned long cmd, void *msg,
- size_t msg_len_in, size_t msg_len_out)
+ size_t msg_len_in, size_t msg_len_out_err,
+ size_t msg_len_out_total)
{
CharBackend *dev = &tpm->ctrl_chr;
uint32_t cmd_no = cpu_to_be32(cmd);
ssize_t n = sizeof(uint32_t) + msg_len_in;
uint8_t *buf = NULL;
+ ptm_res res;
WITH_QEMU_LOCK_GUARD(&tpm->mutex) {
buf = g_alloca(n);
@@ -140,8 +142,24 @@ static int tpm_emulator_ctrlcmd(TPMEmulator *tpm, unsigned long cmd, void *msg,
return -1;
}
- if (msg_len_out != 0) {
- n = qemu_chr_fe_read_all(dev, msg, msg_len_out);
+ if (msg_len_out_total > 0) {
+ assert(msg_len_out_total >= msg_len_out_err);
+
+ n = qemu_chr_fe_read_all(dev, (uint8_t *)msg, msg_len_out_err);
+ if (n <= 0) {
+ return -1;
+ }
+ if (msg_len_out_err == msg_len_out_total) {
+ return 0;
+ }
+ /* result error code is always in the first 4 bytes */
+ memcpy(&res, msg, sizeof(res));
+ if (res) {
+ return 0;
+ }
+
+ n = qemu_chr_fe_read_all(dev, (uint8_t *)msg + msg_len_out_err,
+ msg_len_out_total - msg_len_out_err);
if (n <= 0) {
return -1;
}
@@ -204,7 +222,8 @@ static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number,
memset(&loc, 0, sizeof(loc));
loc.u.req.loc = locty_number;
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_LOCALITY, &loc,
- sizeof(loc), sizeof(loc)) < 0) {
+ sizeof(loc), sizeof(loc.u.resp.tpm_result),
+ sizeof(loc)) < 0) {
error_setg(errp, "tpm-emulator: could not set locality : %s",
strerror(errno));
return -1;
@@ -241,8 +260,9 @@ static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu)
{
ptm_cap_n cap_n;
- if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_CAPABILITY,
- &cap_n, 0, sizeof(cap_n)) < 0) {
+ if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_CAPABILITY, &cap_n, 0,
+ sizeof(cap_n.u.resp.tpm_result),
+ sizeof(cap_n)) < 0) {
error_report("tpm-emulator: probing failed : %s", strerror(errno));
return -1;
}
@@ -292,7 +312,8 @@ static int tpm_emulator_stop_tpm(TPMBackend *tb)
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
ptm_res res;
- if (tpm_emulator_ctrlcmd(tpm_emu, CMD_STOP, &res, 0, sizeof(res)) < 0) {
+ if (tpm_emulator_ctrlcmd(tpm_emu, CMD_STOP, &res, 0,
+ sizeof(ptm_res), sizeof(res)) < 0) {
error_report("tpm-emulator: Could not stop TPM: %s",
strerror(errno));
return -1;
@@ -319,8 +340,9 @@ static int tpm_emulator_lock_storage(TPMEmulator *tpm_emu)
/* give failing side 300 * 10ms time to release lock */
pls.u.req.retries = cpu_to_be32(300);
- if (tpm_emulator_ctrlcmd(tpm_emu, CMD_LOCK_STORAGE, &pls,
- sizeof(pls.u.req), sizeof(pls.u.resp)) < 0) {
+ if (tpm_emulator_ctrlcmd(tpm_emu, CMD_LOCK_STORAGE, &pls, sizeof(pls.u.req),
+ sizeof(pls.u.resp.tpm_result),
+ sizeof(pls.u.resp)) < 0) {
error_report("tpm-emulator: Could not lock storage within 3 seconds: "
"%s", strerror(errno));
return -1;
@@ -351,7 +373,8 @@ static int tpm_emulator_set_buffer_size(TPMBackend *tb,
psbs.u.req.buffersize = cpu_to_be32(wanted_size);
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_BUFFERSIZE, &psbs,
- sizeof(psbs.u.req), sizeof(psbs.u.resp)) < 0) {
+ sizeof(psbs.u.req), sizeof(psbs.u.resp.tpm_result),
+ sizeof(psbs.u.resp)) < 0) {
error_report("tpm-emulator: Could not set buffer size: %s",
strerror(errno));
return -1;
@@ -398,6 +421,7 @@ static int tpm_emulator_startup_tpm_resume(TPMBackend *tb, size_t buffersize,
}
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_INIT, &init, sizeof(init),
+ sizeof(init.u.resp.tpm_result),
sizeof(init)) < 0) {
error_report("tpm-emulator: could not send INIT: %s",
strerror(errno));
@@ -439,8 +463,9 @@ static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb)
return tpm_emu->established_flag;
}
- if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_TPMESTABLISHED, &est,
- 0, sizeof(est)) < 0) {
+ if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_TPMESTABLISHED, &est, 0,
+ sizeof(est) /* always returns resp.bit */,
+ sizeof(est)) < 0) {
error_report("tpm-emulator: Could not get the TPM established flag: %s",
strerror(errno));
return false;
@@ -468,6 +493,7 @@ static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb,
reset_est.u.req.loc = tpm_emu->cur_locty_number;
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_RESET_TPMESTABLISHED,
&reset_est, sizeof(reset_est),
+ sizeof(reset_est.u.resp.tpm_result),
sizeof(reset_est)) < 0) {
error_report("tpm-emulator: Could not reset the establishment bit: %s",
strerror(errno));
@@ -499,7 +525,7 @@ static void tpm_emulator_cancel_cmd(TPMBackend *tb)
/* FIXME: make the function non-blocking, or it may block a VCPU */
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_CANCEL_TPM_CMD, &res, 0,
- sizeof(res)) < 0) {
+ sizeof(ptm_res), sizeof(res)) < 0) {
error_report("tpm-emulator: Could not cancel command: %s",
strerror(errno));
} else if (res != 0) {
@@ -559,7 +585,7 @@ static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_emu)
qemu_chr_fe_set_msgfds(&tpm_emu->ctrl_chr, fds + 1, 1);
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_DATAFD, &res, 0,
- sizeof(res)) < 0 || res != 0) {
+ sizeof(ptm_res), sizeof(res)) < 0 || res != 0) {
error_report("tpm-emulator: Failed to send CMD_SET_DATAFD: %s",
strerror(errno));
goto err_exit;
@@ -706,6 +732,8 @@ static int tpm_emulator_get_state_blob(TPMEmulator *tpm_emu,
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_STATEBLOB,
&pgs, sizeof(pgs.u.req),
+ /* always returns up to resp.data */
+ offsetof(ptm_getstate, u.resp.data),
offsetof(ptm_getstate, u.resp.data)) < 0) {
error_report("tpm-emulator: could not get state blob type %d : %s",
type, strerror(errno));
@@ -808,7 +836,7 @@ static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu,
/* write the header only */
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_STATEBLOB, &pss,
- offsetof(ptm_setstate, u.req.data), 0) < 0) {
+ offsetof(ptm_setstate, u.req.data), 0, 0) < 0) {
error_report("tpm-emulator: could not set state blob type %d : %s",
type, strerror(errno));
return -1;
@@ -992,7 +1020,8 @@ static void tpm_emulator_shutdown(TPMEmulator *tpm_emu)
return;
}
- if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SHUTDOWN, &res, 0, sizeof(res)) < 0) {
+ if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SHUTDOWN, &res, 0,
+ sizeof(ptm_res), sizeof(res)) < 0) {
error_report("tpm-emulator: Could not cleanly shutdown the TPM: %s",
strerror(errno));
} else if (res != 0) {
--
2.47.0
next prev parent reply other threads:[~2024-10-16 17:52 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-10-16 17:51 [PATCH v4 0/2] tpm: Resolve potential blocking-forever issue Stefan Berger
2024-10-16 17:51 ` [PATCH v4 1/2] tpm: Use new ptm_cap_n structure for PTM_GET_CAPABILITY Stefan Berger
2024-10-17 9:48 ` Daniel P. Berrangé
2024-10-16 17:51 ` Stefan Berger [this message]
2024-10-17 9:50 ` [PATCH v4 2/2] tpm_emulator: Read control channel response in 2 passes Daniel P. Berrangé
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20241016175129.1319176-3-stefanb@linux.ibm.com \
--to=stefanb@linux.ibm.com \
--cc=berrange@redhat.com \
--cc=marcandre.lureau@gmail.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).