* [PATCH 0/2] tpm: Resolve potential blocking forever issue
@ 2024-10-11 22:35 Stefan Berger
2024-10-11 22:35 ` [PATCH 1/2] tpm: Use new ptm_cap_n structure for PTM_GET_CAPABILITY Stefan Berger
2024-10-11 22:35 ` [PATCH 2/2] tpm_emulator: Read control channel response in 2 passes Stefan Berger
0 siblings, 2 replies; 4+ messages in thread
From: Stefan Berger @ 2024-10-11 22:35 UTC (permalink / raw)
To: qemu-devel; +Cc: berrange, marcandre.lureau, Stefan Berger
In case swtpm was to return a control channel message with an error code it
would only return 4 bytes. However, some of the commands expect a response
with more bytes and QEMU would get stuck in qemu_chr_fe_read_all() waiting
for bytes following the error code. Therefore, read the response in 2
passes stopping if an error code is received in the first 4 bytes to avoid
getting stuck.
Stefan
Stefan Berger (2):
tpm: Use new ptm_cap_n structure for PTM_GET_CAPABILITY
tpm_emulator: Read control channel response in 2 passes
backends/tpm/tpm_emulator.c | 39 +++++++++++++++++++++++++++++--------
backends/tpm/tpm_ioctl.h | 13 ++++++++++++-
backends/tpm/trace-events | 2 +-
3 files changed, 44 insertions(+), 10 deletions(-)
--
2.46.2
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 1/2] tpm: Use new ptm_cap_n structure for PTM_GET_CAPABILITY
2024-10-11 22:35 [PATCH 0/2] tpm: Resolve potential blocking forever issue Stefan Berger
@ 2024-10-11 22:35 ` Stefan Berger
2024-10-11 22:35 ` [PATCH 2/2] tpm_emulator: Read control channel response in 2 passes Stefan Berger
1 sibling, 0 replies; 4+ messages in thread
From: Stefan Berger @ 2024-10-11 22:35 UTC (permalink / raw)
To: qemu-devel; +Cc: berrange, marcandre.lureau, Stefan Berger
Use the new ptm_cap_n structure for getting the PTM_GET_CAPABILITY response
from swtpm. Previously only 17 bits could possibly have been set in ptm_cap
(=uint64_t) in big endian order and those bits are now found in the 2nd
32bit word in the response in the caps field.
This data structure makes it now clear that the 1st 32bit word carries the
tpm_result like all the other response structures of all other commands
do.
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
backends/tpm/tpm_emulator.c | 14 ++++++++------
backends/tpm/tpm_ioctl.h | 13 ++++++++++++-
backends/tpm/trace-events | 2 +-
3 files changed, 21 insertions(+), 8 deletions(-)
diff --git a/backends/tpm/tpm_emulator.c b/backends/tpm/tpm_emulator.c
index 5a8fba9bde..b0e2fb3fc7 100644
--- a/backends/tpm/tpm_emulator.c
+++ b/backends/tpm/tpm_emulator.c
@@ -72,7 +72,7 @@ struct TPMEmulator {
CharBackend ctrl_chr;
QIOChannel *data_ioc;
TPMVersion tpm_version;
- ptm_cap caps; /* capabilities of the TPM */
+ uint32_t caps; /* capabilities of the TPM */
uint8_t cur_locty_number; /* last set locality */
Error *migration_blocker;
@@ -239,13 +239,15 @@ static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd,
static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu)
{
+ ptm_cap_n cap_n;
+
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_CAPABILITY,
- &tpm_emu->caps, 0, sizeof(tpm_emu->caps)) < 0) {
+ &cap_n, 0, sizeof(cap_n)) < 0) {
error_report("tpm-emulator: probing failed : %s", strerror(errno));
return -1;
}
- tpm_emu->caps = be64_to_cpu(tpm_emu->caps);
+ tpm_emu->caps = be32_to_cpu(cap_n.u.resp.caps);
trace_tpm_emulator_probe_caps(tpm_emu->caps);
@@ -254,7 +256,7 @@ static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu)
static int tpm_emulator_check_caps(TPMEmulator *tpm_emu)
{
- ptm_cap caps = 0;
+ uint32_t caps = 0;
const char *tpm = NULL;
/* check for min. required capabilities */
@@ -527,8 +529,8 @@ static size_t tpm_emulator_get_buffer_size(TPMBackend *tb)
static int tpm_emulator_block_migration(TPMEmulator *tpm_emu)
{
Error *err = NULL;
- ptm_cap caps = PTM_CAP_GET_STATEBLOB | PTM_CAP_SET_STATEBLOB |
- PTM_CAP_STOP;
+ uint32_t caps = PTM_CAP_GET_STATEBLOB | PTM_CAP_SET_STATEBLOB |
+ PTM_CAP_STOP;
if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, caps)) {
error_setg(&tpm_emu->migration_blocker,
diff --git a/backends/tpm/tpm_ioctl.h b/backends/tpm/tpm_ioctl.h
index 1933ab6855..ee2dd15d35 100644
--- a/backends/tpm/tpm_ioctl.h
+++ b/backends/tpm/tpm_ioctl.h
@@ -29,6 +29,16 @@
typedef uint32_t ptm_res;
+/* PTM_GET_CAPABILITY: Get supported capabilities (ioctl's) */
+struct ptm_cap_n {
+ union {
+ struct {
+ ptm_res tpm_result; /* will always be TPM_SUCCESS (0) */
+ uint32_t caps;
+ } resp; /* response */
+ } u;
+};
+
/* PTM_GET_TPMESTABLISHED: get the establishment bit */
struct ptm_est {
union {
@@ -242,7 +252,8 @@ struct ptm_lockstorage {
} u;
};
-typedef uint64_t ptm_cap;
+typedef uint64_t ptm_cap; /* CUSE-only; use ptm_cap_n otherwise */
+typedef struct ptm_cap_n ptm_cap_n;
typedef struct ptm_est ptm_est;
typedef struct ptm_reset_est ptm_reset_est;
typedef struct ptm_loc ptm_loc;
diff --git a/backends/tpm/trace-events b/backends/tpm/trace-events
index cb5cfa6510..05e30533ce 100644
--- a/backends/tpm/trace-events
+++ b/backends/tpm/trace-events
@@ -16,7 +16,7 @@ tpm_util_show_buffer_content(const char *buf) "%s"
# tpm_emulator.c
tpm_emulator_set_locality(uint8_t locty) "setting locality to %d"
tpm_emulator_handle_request(void) "processing TPM command"
-tpm_emulator_probe_caps(uint64_t caps) "capabilities: 0x%"PRIx64
+tpm_emulator_probe_caps(uint32_t caps) "capabilities: 0x%x"
tpm_emulator_set_buffer_size(uint32_t buffersize, uint32_t minsize, uint32_t maxsize) "buffer size: %u, min: %u, max: %u"
tpm_emulator_startup_tpm_resume(bool is_resume, size_t buffersize) "is_resume: %d, buffer size: %zu"
tpm_emulator_get_tpm_established_flag(uint8_t flag) "got established flag: %d"
--
2.46.2
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 2/2] tpm_emulator: Read control channel response in 2 passes
2024-10-11 22:35 [PATCH 0/2] tpm: Resolve potential blocking forever issue Stefan Berger
2024-10-11 22:35 ` [PATCH 1/2] tpm: Use new ptm_cap_n structure for PTM_GET_CAPABILITY Stefan Berger
@ 2024-10-11 22:35 ` Stefan Berger
2024-10-14 13:27 ` Stefan Berger
1 sibling, 1 reply; 4+ messages in thread
From: Stefan Berger @ 2024-10-11 22:35 UTC (permalink / raw)
To: qemu-devel; +Cc: berrange, marcandre.lureau, Stefan Berger
Error responses from swtpm are always only 4 bytes long. Therefore, read
the entire response in 2 passes and stop if the first 4 bytes indicate an
error response with no subsequent bytes readable. Read the rest in a 2nd
pass, if needed. This avoids getting stuck while waiting for too many bytes
if only 4 bytes were sent due to an error message. 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 | 25 +++++++++++++++++++++++--
1 file changed, 23 insertions(+), 2 deletions(-)
diff --git a/backends/tpm/tpm_emulator.c b/backends/tpm/tpm_emulator.c
index b0e2fb3fc7..1b2e890668 100644
--- a/backends/tpm/tpm_emulator.c
+++ b/backends/tpm/tpm_emulator.c
@@ -129,6 +129,9 @@ static int tpm_emulator_ctrlcmd(TPMEmulator *tpm, unsigned long cmd, void *msg,
uint32_t cmd_no = cpu_to_be32(cmd);
ssize_t n = sizeof(uint32_t) + msg_len_in;
uint8_t *buf = NULL;
+ ptm_res res;
+ off_t o = 0;
+ int to_read;
WITH_QEMU_LOCK_GUARD(&tpm->mutex) {
buf = g_alloca(n);
@@ -140,11 +143,29 @@ 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);
+ /*
+ * Any response will be at least 4 bytes long. Error responses are only
+ * 4 bytes (=sizeof(ptm_res)), though. Therefore, read response in 2
+ * passes, returning when an error response is encountered.
+ */
+ to_read = sizeof(res);
+ while (msg_len_out > 0) {
+ n = qemu_chr_fe_read_all(dev, (uint8_t *)msg + o, to_read);
if (n <= 0) {
return -1;
}
+ msg_len_out -= to_read;
+ if (msg_len_out == 0) {
+ return 0;
+ }
+
+ memcpy(&res, msg, sizeof(res));
+ if (res) {
+ return 0;
+ }
+
+ o = to_read;
+ to_read = msg_len_out;
}
}
--
2.46.2
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH 2/2] tpm_emulator: Read control channel response in 2 passes
2024-10-11 22:35 ` [PATCH 2/2] tpm_emulator: Read control channel response in 2 passes Stefan Berger
@ 2024-10-14 13:27 ` Stefan Berger
0 siblings, 0 replies; 4+ messages in thread
From: Stefan Berger @ 2024-10-14 13:27 UTC (permalink / raw)
To: qemu-devel; +Cc: berrange, marcandre.lureau
On 10/11/24 6:35 PM, Stefan Berger wrote:
> Error responses from swtpm are always only 4 bytes long. Therefore, read
> the entire response in 2 passes and stop if the first 4 bytes indicate an
> error response with no subsequent bytes readable. Read the rest in a 2nd
> pass, if needed. This avoids getting stuck while waiting for too many bytes
> if only 4 bytes were sent due to an error message. 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 | 25 +++++++++++++++++++++++--
> 1 file changed, 23 insertions(+), 2 deletions(-)
>
> diff --git a/backends/tpm/tpm_emulator.c b/backends/tpm/tpm_emulator.c
> index b0e2fb3fc7..1b2e890668 100644
> --- a/backends/tpm/tpm_emulator.c
> +++ b/backends/tpm/tpm_emulator.c
> @@ -129,6 +129,9 @@ static int tpm_emulator_ctrlcmd(TPMEmulator *tpm, unsigned long cmd, void *msg,
> uint32_t cmd_no = cpu_to_be32(cmd);
> ssize_t n = sizeof(uint32_t) + msg_len_in;
> uint8_t *buf = NULL;
> + ptm_res res;
> + off_t o = 0;
> + int to_read;
>
> WITH_QEMU_LOCK_GUARD(&tpm->mutex) {
> buf = g_alloca(n);
> @@ -140,11 +143,29 @@ 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);
> + /*
> + * Any response will be at least 4 bytes long. Error responses are only
> + * 4 bytes (=sizeof(ptm_res)), though. Therefore, read response in 2
> + * passes, returning when an error response is encountered.
> + */
v2: We need one exception here and that's CMD_GET_STATEBLOB since it has
been sending a longer header since basically 'forever'.
> + to_read = sizeof(res);
> + while (msg_len_out > 0) {
> + n = qemu_chr_fe_read_all(dev, (uint8_t *)msg + o, to_read);
> if (n <= 0) {
> return -1;
> }
> + msg_len_out -= to_read;
> + if (msg_len_out == 0) {
> + return 0;
> + }
> +
> + memcpy(&res, msg, sizeof(res));
> + if (res) {
> + return 0;
> + }
> +
> + o = to_read;
> + to_read = msg_len_out;
> }
> }
>
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2024-10-14 13:27 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-10-11 22:35 [PATCH 0/2] tpm: Resolve potential blocking forever issue Stefan Berger
2024-10-11 22:35 ` [PATCH 1/2] tpm: Use new ptm_cap_n structure for PTM_GET_CAPABILITY Stefan Berger
2024-10-11 22:35 ` [PATCH 2/2] tpm_emulator: Read control channel response in 2 passes Stefan Berger
2024-10-14 13:27 ` Stefan Berger
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).