[PATCH v4 2/4] soc: qcom: smem: validate private entry list walks
From: Albert Esteve
Date: Thu Oct 22 2026 - 05:52:48 EST
From: Sarannya S <sarannya.s@xxxxxxxxxxxxxxxx>
When walking private entries, use the uncached/cached free offsets as
the list bounds, require room for a header before dereferencing, and
reject a next pointer that does not advance.
Skip the cached list when the cached free offset is at the partition
end.
Signed-off-by: Sarannya S <sarannya.s@xxxxxxxxxxxxxxxx>
Signed-off-by: Pranav Mahesh Phansalkar <pranav.phansalkar@xxxxxxxxxxxxxxxx>
Signed-off-by: Sudeepgoud Patil <sudeep.patil@xxxxxxxxxxxxxxxx>
Signed-off-by: Albert Esteve <aesteve@xxxxxxxxxx>
---
drivers/soc/qcom/smem.c | 52 ++++++++++++++++++++++++++++++++++++-------------
1 file changed, 38 insertions(+), 14 deletions(-)
diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c
index 36a4a10f3aec..a6732832e2c5 100644
--- a/drivers/soc/qcom/smem.c
+++ b/drivers/soc/qcom/smem.c
@@ -422,6 +422,7 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem,
size_t size)
{
struct smem_private_entry *hdr, *end;
+ struct smem_private_entry *next_hdr;
struct smem_partition_header *phdr;
size_t alloc_size;
void *cached;
@@ -434,19 +435,25 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem,
end = phdr_to_last_uncached_entry(phdr);
cached = phdr_to_last_cached_entry(phdr);
- if (WARN_ON((void *)end > p_end || cached > p_end))
+ if (WARN_ON(!IN_PARTITION_RANGE(end, 0, phdr, cached) ||
+ cached > p_end))
return -EINVAL;
- while (hdr < end) {
+ while ((hdr + 1) <= end) {
if (hdr->canary != SMEM_PRIVATE_CANARY)
goto bad_canary;
if (le16_to_cpu(hdr->item) == item)
return -EEXIST;
- hdr = uncached_entry_next(hdr);
+ next_hdr = uncached_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 */
@@ -605,7 +612,8 @@ static void *qcom_smem_get_private(struct qcom_smem *smem,
unsigned int item,
size_t *size)
{
- struct smem_private_entry *e, *end;
+ struct smem_private_entry *e, *uncached_end, *cached_end;
+ struct smem_private_entry *next_e;
struct smem_partition_header *phdr;
void *item_ptr, *p_end;
u32 padding_data;
@@ -615,9 +623,14 @@ static void *qcom_smem_get_private(struct qcom_smem *smem,
p_end = (void *)phdr + part->size;
e = phdr_to_first_uncached_entry(phdr);
- end = phdr_to_last_uncached_entry(phdr);
+ uncached_end = phdr_to_last_uncached_entry(phdr);
+ cached_end = phdr_to_last_cached_entry(phdr);
+
+ if (WARN_ON(!IN_PARTITION_RANGE(uncached_end, 0, phdr, cached_end) ||
+ (void *)cached_end > p_end))
+ return ERR_PTR(-EINVAL);
- while (e < end) {
+ while ((e + 1) <= uncached_end) {
if (e->canary != SMEM_PRIVATE_CANARY)
goto invalid_canary;
@@ -639,21 +652,28 @@ static void *qcom_smem_get_private(struct qcom_smem *smem,
return item_ptr;
}
- e = uncached_entry_next(e);
+ next_e = uncached_entry_next(e);
+ if (WARN_ON(next_e <= e))
+ return ERR_PTR(-EINVAL);
+
+ e = next_e;
}
- if (WARN_ON((void *)e > p_end))
+ if (WARN_ON((void *)e > (void *)uncached_end))
return ERR_PTR(-EINVAL);
/* Item was not found in the uncached list, search the cached list */
+ if (cached_end == p_end)
+ return ERR_PTR(-ENOENT);
+
e = phdr_to_first_cached_entry(phdr, part->cacheline);
- end = phdr_to_last_cached_entry(phdr);
- if (WARN_ON((void *)e < (void *)phdr || (void *)end > p_end))
+ if (WARN_ON(!IN_PARTITION_RANGE(cached_end, 0, uncached_end, p_end) ||
+ !IN_PARTITION_RANGE(e, sizeof(*e), cached_end, p_end)))
return ERR_PTR(-EINVAL);
- while (e > end) {
+ while (e > cached_end) {
if (e->canary != SMEM_PRIVATE_CANARY)
goto invalid_canary;
@@ -675,10 +695,14 @@ static void *qcom_smem_get_private(struct qcom_smem *smem,
return item_ptr;
}
- e = cached_entry_next(e, part->cacheline);
+ next_e = cached_entry_next(e, part->cacheline);
+ if (WARN_ON(next_e >= e))
+ return ERR_PTR(-EINVAL);
+
+ e = next_e;
}
- if (WARN_ON((void *)e < (void *)phdr))
+ if (WARN_ON((void *)e < (void *)cached_end))
return ERR_PTR(-EINVAL);
return ERR_PTR(-ENOENT);
--
2.55.0