summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlixiang <lixiang@codeaurora.org>2021-10-11 20:07:52 +0800
committerGerrit - the friendly Code Review server <code-review@localhost>2021-10-13 23:53:43 -0700
commit28b45f75d2ee6ef9a4023c1a7d4c38ff0d20e087 (patch)
treecbb2a32ecf0d0f1708008ab3fd07c186cb9db58d
parent885caec7690f28757aa491ad8ef17084e2f9b25d (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.h8
-rw-r--r--drivers/soc/qcom/hab/hab_msg.c59
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: