diff options
author | lixiang <lixiang@codeaurora.org> | 2021-10-11 20:07:52 +0800 |
---|---|---|
committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2021-10-13 23:53:43 -0700 |
commit | 28b45f75d2ee6ef9a4023c1a7d4c38ff0d20e087 (patch) | |
tree | cbb2a32ecf0d0f1708008ab3fd07c186cb9db58d | |
parent | 885caec7690f28757aa491ad8ef17084e2f9b25d (diff) |
soc: qcom: hab: Add sanity check for payload_count
When handling memory import, payload_count is used for memory alloc
calculation. If the payload_count is too large, size will overflow
when creating page list.
Adding a sanity check for payload_count is necessary.
Change-Id: I6d60cea0c62bd29092852c55b766b77a94cb6e3b
Signed-off-by: lixiang <lixiang@codeaurora.org>
-rw-r--r-- | drivers/soc/qcom/hab/hab.h | 8 | ||||
-rw-r--r-- | drivers/soc/qcom/hab/hab_msg.c | 59 |
2 files changed, 63 insertions, 4 deletions
diff --git a/drivers/soc/qcom/hab/hab.h b/drivers/soc/qcom/hab/hab.h index cb7ed52050b2..bdc8f54b08b4 100644 --- a/drivers/soc/qcom/hab/hab.h +++ b/drivers/soc/qcom/hab/hab.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2019, 2021, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -98,6 +98,12 @@ enum hab_payload_type { /* make sure concascaded name is less than this value */ #define MAX_VMID_NAME_SIZE 30 +/* + * The maximum value of payload_count in struct export_desc + * Max u32_t size_bytes from hab_ioctl.h(0xFFFFFFFF) / page size(0x1000) + */ +#define MAX_EXP_PAYLOAD_COUNT 0xFFFFF + #define HABCFG_FILE_SIZE_MAX 256 #define HABCFG_MMID_AREA_MAX (MM_ID_MAX/100) diff --git a/drivers/soc/qcom/hab/hab_msg.c b/drivers/soc/qcom/hab/hab_msg.c index 9f92074665df..4494cfbf0e0b 100644 --- a/drivers/soc/qcom/hab/hab_msg.c +++ b/drivers/soc/qcom/hab/hab_msg.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2019, 2021, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -11,6 +11,7 @@ * */ #include "hab.h" +#include "hab_grantable.h" static int hab_rx_queue_empty(struct virtual_channel *vchan) { @@ -207,6 +208,8 @@ int hab_msg_recv(struct physical_channel *pchan, struct export_desc *exp_desc = NULL, exp_ack = {0}; struct timeval tv = {0}; unsigned long long rx_mpm_tv = 0; + size_t exp_desc_size_expected = 0; + struct compressed_pfns *pfn_table = NULL; /* get the local virtual channel if it isn't an open message */ if (payload_type != HAB_PAYLOAD_TYPE_INIT && @@ -295,8 +298,11 @@ int hab_msg_recv(struct physical_channel *pchan, break; case HAB_PAYLOAD_TYPE_EXPORT: - if (sizebytes > HAB_HEADER_SIZE_MASK) { - pr_err("%s exp size too large %zd header %zd\n", + exp_desc_size_expected = sizeof(struct export_desc) + + sizeof(struct compressed_pfns); + if (sizebytes > (size_t)HAB_HEADER_SIZE_MASK) || + sizebytes < exp_desc_size_expected) { + pr_err("%s exp size too large/small %zu header %zu\n", pchan->name, sizebytes, sizeof(*exp_desc)); break; } @@ -328,6 +334,53 @@ int hab_msg_recv(struct physical_channel *pchan, hab_export_enqueue(vchan, exp_desc); /* for local use */ hab_send_export_ack(vchan, pchan, &exp_ack); /* ack exporter */ + /* + * We should do all the checks here. + * But in order to improve performance, we put the + * checks related to exp->payload_count and + * pfn_table->region[i].size into function pages_list_create. + * So any potential usage of such data from the remote side + * after the checks here and before the checks in + * pages_list_create needs to add some more checks if necessary. + */ + pfn_table = (struct compressed_pfns *)exp_desc->payload; + if (pfn_table->nregions <= 0 || + (pfn_table->nregions > + SIZE_MAX / sizeof(struct region)) || + (SIZE_MAX - exp_desc_size_expected < + pfn_table->nregions * sizeof(struct region))) { + pr_err("%s nregions is too large or negative, nregions:%d!\n", + pchan->name, pfn_table->nregions); + kfree(exp_desc); + break; + } + + if (pfn_table->nregions > exp_desc->payload_count) { + pr_err("%s nregions %d greater than payload_count %d\n", + pchan->name, pfn_table->nregions, + exp_desc->payload_count); + kfree(exp_desc); + break; + } + + if (exp_desc->payload_count > MAX_EXP_PAYLOAD_COUNT) { + pr_err("payload_count out of range: %d size overflow\n", + exp_desc->payload_count); + kfree(exp_desc); + break; + } + + exp_desc_size_expected += + pfn_table->nregions * sizeof(struct region); + if (sizebytes != exp_desc_size_expected) { + pr_err("%s exp size not equal %zu expect %zu\n", + pchan->name, sizebytes, exp_desc_size_expected); + kfree(exp_desc); + break; + } + + hab_export_enqueue(vchan, exp_desc); + hab_send_export_ack(vchan, pchan, exp_desc); break; case HAB_PAYLOAD_TYPE_EXPORT_ACK: |