summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSarannya S <quic_sarannya@quicinc.com>2023-09-22 13:13:58 +0530
committerSarannya S <quic_sarannya@quicinc.com>2024-03-22 00:43:50 -0700
commitd764b49bbb724d678cec807309eb43a31917ca3a (patch)
tree741ff04f096528997f99bc14ddb4be8c75e2dc77
parent7462e3e61f80c615bca0b5d690273318e0504687 (diff)
soc: qcom: smem: Add boundary checks for partitions
Add condition check to make sure that the end address of private entry does not go out of partition. Change-Id: Ifb942c30ebeb8bccb891eebdcae4700b64b76920 Signed-off-by: Sarannya S <quic_sarannya@quicinc.com>
-rw-r--r--drivers/soc/qcom/smem.c59
1 files changed, 43 insertions, 16 deletions
diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c
index 4958b65ec935..e20657dba6ce 100644
--- a/drivers/soc/qcom/smem.c
+++ b/drivers/soc/qcom/smem.c
@@ -86,6 +86,17 @@
/* Max number of processors/hosts in a system */
#define SMEM_HOST_COUNT 9
+/* Entry range check
+ * ptr >= start : Checks if ptr is greater than the start of access region
+ * ptr + size >= ptr: Check for integer overflow (On 32bit system where ptr
+ * and size are 32bits, ptr + size can wrap around to be a small integer)
+ * ptr + size <= end: Checks if ptr+size is less than the end of access region
+ */
+#define IN_PARTITION_RANGE(ptr, size, start, end) \
+ (((void *)(ptr) >= (void *)(start)) && \
+ (((void *)(ptr) + (size)) >= (void *)(ptr)) && \
+ (((void *)(ptr) + (size)) <= (void *)(end)))
+
/**
* struct smem_proc_comm - proc_comm communication struct (legacy)
* @command: current command to be executed
@@ -303,6 +314,7 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem,
{
struct smem_partition_header *phdr;
struct smem_private_entry *hdr, *end;
+ struct smem_private_entry *next_hdr;
struct smem_partition_header *phdr;
size_t alloc_size;
void *cached;
@@ -315,10 +327,11 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem,
end = phdr_to_last_private_entry(phdr);
cached = phdr_to_first_cached_entry(phdr);
- if (WARN_ON((void *)end > p_end || (void *)cached > p_end))
+ if (WARN_ON(!IN_PARTITION_RANGE(end, 0, phdr, cached) ||
+ cached > p_end))
return -EINVAL;
- while (hdr < end) {
+ while ((hdr < end) && ((hdr + 1) < end)) {
if (hdr->canary != SMEM_PRIVATE_CANARY) {
dev_err(smem->dev,
"Found invalid canary in host %d:%d partition\n",
@@ -329,9 +342,15 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem,
if (le16_to_cpu(hdr->item) == item)
return -EEXIST;
- hdr = private_entry_next(hdr);
+ next_hdr = private_entry_next(hdr);
+
+ if (WARN_ON(next_hdr <= hdr))
+ return -EINVAL;
+
+ hdr = next_hdr;
}
- if (WARN_ON((void *)hdr > p_end))
+
+ if (WARN_ON((void *)hdr > (void *)end))
return -EINVAL;
/* Check that we don't grow into the cached region */
@@ -486,7 +505,9 @@ static void *qcom_smem_get_private(struct qcom_smem *smem,
{
struct smem_partition_header *phdr;
struct smem_private_entry *e, *end;
+ struct smem_private_entry *next_e;
void *item_ptr, *p_end;
+ size_t entry_size = 0;
u32 partition_size;
u32 padding_data;
u32 e_size;
@@ -501,7 +522,7 @@ static void *qcom_smem_get_private(struct qcom_smem *smem,
if (WARN_ON((void *)end > p_end))
return ERR_PTR(-EINVAL);
- while (e < end) {
+ while ((e < end) && ((e + 1) < end)) {
if (e->canary != SMEM_PRIVATE_CANARY) {
dev_err(smem->dev,
"Found invalid canary in host %d:%d partition\n",
@@ -510,25 +531,31 @@ static void *qcom_smem_get_private(struct qcom_smem *smem,
}
if (le16_to_cpu(e->item) == item) {
- if (size != NULL) {
- e_size = le32_to_cpu(e->size);
- padding_data = le16_to_cpu(e->padding_data);
+ e_size = le32_to_cpu(e->size);
+ padding_data = le16_to_cpu(e->padding_data);
- if (e_size < partition_size
- && padding_data < e_size)
- *size = e_size - padding_data;
- else
- return ERR_PTR(-EINVAL);
- }
+ if (e_size < partition_size && padding_data < e_size)
+ entry_size = e_size - padding_data;
+ else
+ return ERR_PTR(-EINVAL);
item_ptr = entry_to_item(e);
- if (WARN_ON(item_ptr > p_end))
+
+ if (WARN_ON(!IN_PARTITION_RANGE(item_ptr, entry_size,
+ e, end)))
return ERR_PTR(-EINVAL);
+ if (size != NULL)
+ *size = entry_size;
+
return item_ptr;
}
- e = private_entry_next(e);
+ next_e = private_entry_next(e);
+ if (WARN_ON(next_e <= e))
+ return ERR_PTR(-EINVAL);
+
+ e = next_e;
}
if (WARN_ON((void *)e > p_end))
return ERR_PTR(-EINVAL);