From f8cdef9809a107509e4e115752484a1302fe52f3 Mon Sep 17 00:00:00 2001 From: Shengjie Li Date: Tue, 11 Jun 2024 09:56:05 +0800 Subject: [PATCH] cvm_tsi: Fix security issue for Confidential cVM TSI virtcca inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IA45I1 -------------------------------- Fix security issue for Confidential cVM TSI: 1. Init TSI driver when config cvm_guest. 2. Modify the token structure to reduce the variables that are exposed to the user state. 3. Add a lock for obtaining the remote attestation report. Fixes: 31071f4b2a0b ("cvm_tsi: add cvm tsi interface") Signed-off-by: Shengjie Li --- arch/arm64/include/asm/cvm_smc.h | 22 +++---- arch/arm64/include/uapi/asm/cvm_tsi.h | 11 ++-- arch/arm64/kernel/cvm_tsi.c | 89 ++++++++++++++------------- 3 files changed, 64 insertions(+), 58 deletions(-) diff --git a/arch/arm64/include/asm/cvm_smc.h b/arch/arm64/include/asm/cvm_smc.h index c4d56333e38b..91688bc16963 100644 --- a/arch/arm64/include/asm/cvm_smc.h +++ b/arch/arm64/include/asm/cvm_smc.h @@ -124,30 +124,30 @@ static inline unsigned long tsi_measurement_read(struct cvm_measurement *cvm_mea return res.a0; } -static inline unsigned long tsi_attestation_token_init(struct cvm_attestation_cmd *attest_cmd) +static inline unsigned long tsi_attestation_token_init(unsigned char *challenge) { struct arm_smccc_res res; - unsigned char *challenge; + unsigned char *buf; - challenge = kmalloc(CHALLENGE_SIZE, GFP_KERNEL); - if (!challenge) + buf = kmalloc(CHALLENGE_SIZE, GFP_KERNEL); + if (!buf) return -ENOMEM; - memcpy(challenge, attest_cmd->challenge, CHALLENGE_SIZE); + memcpy(buf, challenge, CHALLENGE_SIZE); - arm_smccc_1_1_smc(SMC_TSI_ATTESTATION_TOKEN_INIT, virt_to_phys(challenge), &res); - kfree(challenge); + arm_smccc_1_1_smc(SMC_TSI_ATTESTATION_TOKEN_INIT, virt_to_phys(buf), &res); + kfree(buf); return res.a0; } -static inline unsigned long tsi_attestation_token_continue(struct cvm_attestation_cmd *attest_cmd) +static inline unsigned long tsi_attestation_token_continue(struct cvm_token_granule *token_granule) { struct arm_smccc_res res; - arm_smccc_1_1_smc(SMC_TSI_ATTESTATION_TOKEN_CONTINUE, virt_to_phys(attest_cmd->granule_ipa), - attest_cmd->offset, attest_cmd->size, &res); + arm_smccc_1_1_smc(SMC_TSI_ATTESTATION_TOKEN_CONTINUE, virt_to_phys(token_granule->ipa), + token_granule->offset, token_granule->size, &res); - attest_cmd->num_wr_bytes = res.a1; + token_granule->num_wr_bytes = res.a1; return res.a0; } diff --git a/arch/arm64/include/uapi/asm/cvm_tsi.h b/arch/arm64/include/uapi/asm/cvm_tsi.h index 43e17a0da047..1ed4386db2a1 100644 --- a/arch/arm64/include/uapi/asm/cvm_tsi.h +++ b/arch/arm64/include/uapi/asm/cvm_tsi.h @@ -55,10 +55,13 @@ struct cvm_measurement_extend { struct cvm_attestation_cmd { unsigned char challenge[CHALLENGE_SIZE]; /* input: challenge value */ - unsigned long token_size; /* return: challenge value */ - void *granule_head; - void *granule_ipa; /* IPA of the Granule to which the token will be written */ - unsigned long granule_count; + unsigned long token_size; /* return: token size */ +}; + +struct cvm_token_granule { + void *head; + void *ipa; /* IPA of the Granule to which the token will be written */ + unsigned long count; unsigned long offset; /* Offset within Granule to start of buffer in bytes */ unsigned long size; /* Size of buffer in bytes */ unsigned long num_wr_bytes; /* Number of bytes written to buffer */ diff --git a/arch/arm64/kernel/cvm_tsi.c b/arch/arm64/kernel/cvm_tsi.c index b48e5b17f486..252fe5352a8b 100644 --- a/arch/arm64/kernel/cvm_tsi.c +++ b/arch/arm64/kernel/cvm_tsi.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -16,20 +17,19 @@ struct attestation_token { static struct attestation_token token; +static DEFINE_SPINLOCK(lock); + static long tmm_tsi_ioctl(struct file *file, unsigned int cmd, unsigned long arg); -static int tmm_tsi_release(struct inode *inode, struct file *file); static ssize_t tmm_token_read(struct file *file, char __user *user_buffer, size_t size, loff_t *offset); static int tmm_get_tsi_version(struct cvm_tsi_version __user *arg); -static int tmm_get_attestation_token(struct cvm_attestation_cmd __user *arg, - struct attestation_token *attest_token); +static int tmm_get_attestation_token(struct cvm_attestation_cmd __user *arg); static int tmm_get_device_cert(struct cca_device_cert __user *arg); static const struct file_operations tmm_tsi_fops = { .owner = THIS_MODULE, .read = tmm_token_read, - .release = tmm_tsi_release, .unlocked_ioctl = tmm_tsi_ioctl }; @@ -44,6 +44,9 @@ static int __init tmm_tsi_init(void) unsigned long ver; int ret; + if (!is_cvm_world()) + return -EIO; + ver = tsi_get_version(); if (ver == SMCCC_RET_NOT_SUPPORTED) { pr_err("tmm_tsi: SMC return not supported!\n"); @@ -56,6 +59,11 @@ static int __init tmm_tsi_init(void) return ret; } + /* Allocate a large memory */ + token.buf = kmalloc(GRANULE_SIZE * MAX_TOKEN_GRANULE_PAGE, GFP_KERNEL); + if (!token.buf) + return -ENOMEM; + pr_warn("tmm_tsi: module loaded (version %lu.%lu).\n", TSI_ABI_VERSION_GET_MAJOR(ver), TSI_ABI_VERSION_GET_MINOR(ver)); @@ -65,8 +73,10 @@ static int __init tmm_tsi_init(void) static void __exit tmm_tsi_exit(void) { - if (token.buf != NULL) + if (token.buf != NULL) { + memset(token.buf, 0, GRANULE_SIZE * MAX_TOKEN_GRANULE_PAGE); kfree(token.buf); + } misc_deregister(&ioctl_dev); pr_warn("tmm_tsi: module unloaded.\n"); } @@ -80,7 +90,7 @@ static long tmm_tsi_ioctl(struct file *file, unsigned int cmd, unsigned long arg ret = tmm_get_tsi_version((struct cvm_tsi_version *)arg); break; case TMM_GET_ATTESTATION_TOKEN: - ret = tmm_get_attestation_token((struct cvm_attestation_cmd *)arg, &token); + ret = tmm_get_attestation_token((struct cvm_attestation_cmd *)arg); break; case TMM_GET_DEVICE_CERT: ret = tmm_get_device_cert((struct cca_device_cert *)arg); @@ -99,28 +109,25 @@ static ssize_t tmm_token_read(struct file *file, char __user *user_buffer, int ret; int to_copy; - if (*offset >= token.size) + spin_lock(&lock); + if (*offset >= token.size) { + spin_unlock(&lock); return 0; + } to_copy = min((int)size, (int)(token.size - *offset)); ret = copy_to_user(user_buffer, token.buf + *offset, to_copy); if (ret) { pr_err("tmm_tsi: copy token to user failed (%d)!\n", ret); + spin_unlock(&lock); return -1; } *offset += to_copy; + spin_unlock(&lock); return to_copy; } -static int tmm_tsi_release(struct inode *inode, struct file *file) -{ - if (token.buf != NULL) { - memset(token.buf, 0, GRANULE_SIZE * MAX_TOKEN_GRANULE_PAGE); - kfree(token.buf); - } - return 0; -} static int tmm_get_tsi_version(struct cvm_tsi_version __user *arg) { @@ -141,65 +148,61 @@ static int tmm_get_tsi_version(struct cvm_tsi_version __user *arg) return 0; } -static int tmm_get_attestation_token(struct cvm_attestation_cmd __user *arg, - struct attestation_token *attest_token) +static int tmm_get_attestation_token(struct cvm_attestation_cmd __user *arg) { unsigned long ret; - struct cvm_attestation_cmd cmd = {0}; + struct cvm_token_granule token_granule = {0}; + unsigned char challenge[CHALLENGE_SIZE]; - ret = copy_from_user(&(cmd.challenge), &(arg->challenge), sizeof(cmd.challenge)); + ret = copy_from_user(challenge, &(arg->challenge), CHALLENGE_SIZE); if (ret) { pr_err("tmm_tsi: copy data from user failed (%lu)!\n", ret); return -EFAULT; } - /* Allocate a large memory */ - attest_token->buf = kmalloc(GRANULE_SIZE * MAX_TOKEN_GRANULE_PAGE, GFP_KERNEL); - if (!attest_token->buf) - return -ENOMEM; - cmd.granule_head = attest_token->buf; - cmd.granule_ipa = cmd.granule_head; + spin_lock(&lock); + token_granule.head = token.buf; + token_granule.ipa = token_granule.head; - /* preempt_disable(); */ - - ret = tsi_attestation_token_init(&cmd); + ret = tsi_attestation_token_init(challenge); if (ret) { pr_err("tmm_tsi: tsi call tsi_attestation_token_init failed (%lu)!\n", ret); + spin_unlock(&lock); return -EIO; } do { /* Retrieve one Granule of data per loop iteration */ - cmd.granule_ipa = cmd.granule_head + - (unsigned long)(cmd.granule_count * GRANULE_SIZE); - cmd.offset = 0; + token_granule.ipa = token_granule.head + + (unsigned long)(token_granule.count * GRANULE_SIZE); + token_granule.offset = 0; do { /* Retrieve sub-Granule chunk of data per loop iteration */ - cmd.size = GRANULE_SIZE - cmd.offset; - ret = tsi_attestation_token_continue(&cmd); - cmd.offset += cmd.num_wr_bytes; - } while (ret == TSI_INCOMPLETE && cmd.offset < GRANULE_SIZE); + token_granule.size = GRANULE_SIZE - token_granule.offset; + ret = tsi_attestation_token_continue(&token_granule); + token_granule.offset += token_granule.num_wr_bytes; + } while (ret == TSI_INCOMPLETE && token_granule.offset < GRANULE_SIZE); - cmd.granule_count += 1; - if (cmd.granule_count >= MAX_TOKEN_GRANULE_PAGE && ret == TSI_INCOMPLETE) { + token_granule.count += 1; + if (token_granule.count >= MAX_TOKEN_GRANULE_PAGE && ret == TSI_INCOMPLETE) { pr_err("tmm_tsi: macro MAX_TOKEN_GRANULE_PAGE (%d) is too small!\n", MAX_TOKEN_GRANULE_PAGE); + spin_unlock(&lock); return -ENOMEM; } } while (ret == TSI_INCOMPLETE); - /* preempt_enable(); */ - /* Send to user space the total size of the token */ - cmd.granule_count = cmd.granule_count - 1; - cmd.token_size = (unsigned long)(GRANULE_SIZE * cmd.granule_count) + cmd.offset; - attest_token->size = cmd.token_size; + token_granule.count = token_granule.count - 1; + token.size = (unsigned long)(GRANULE_SIZE * token_granule.count) + token_granule.offset; - ret = copy_to_user(&(arg->token_size), &(cmd.token_size), sizeof(cmd.token_size)); + ret = copy_to_user(&(arg->token_size), &(token.size), sizeof(token.size)); if (ret) { pr_err("tmm_tsi: copy data to user failed (%lu)!\n", ret); + spin_unlock(&lock); return -EFAULT; } + spin_unlock(&lock); return 0; } -- Gitee