]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
firmware: arm_ffa: Honor partition info descriptor size
authorJamie Nguyen <jamien@nvidia.com>
Mon, 18 May 2026 20:31:16 +0000 (13:31 -0700)
committerSudeep Holla <sudeep.holla@kernel.org>
Tue, 19 May 2026 18:04:28 +0000 (19:04 +0100)
FFA_PARTITION_INFO_GET_REGS reports the size of each partition
information descriptor in x2[63:48]. However, __ffa_partition_info_get_regs()
walks the returned register payload with a hardcoded 24-byte stride
(regs += 3), even though the size is already read into buf_sz.

That works for the FF-A v1.1/v1.2 24-byte descriptor layout, where each
descriptor consumes three registers. Newer FF-A revisions can extend the
descriptor while keeping the existing fields at the front. For example, a
48-byte descriptor consumes six registers, so advancing by only three
registers desynchronises the parser and can make it read subsequent entries
from the middle of a descriptor.

Use the advertised descriptor size to derive the register stride. Validate
that the size is register-aligned, large enough for the fields parsed by the
driver, and that the requested number of descriptors fits in the returned
x3..x17 register window. The driver still copies only the fields it
understands, but now skips over any trailing descriptor fields correctly.

Fixes: ba85c644ac8d ("firmware: arm_ffa: Add support for FFA_PARTITION_INFO_GET_REGS")
Suggested-by: Sudeep Holla <sudeep.holla@kernel.org>
Signed-off-by: Jamie Nguyen <jamien@nvidia.com>
Link: https://patch.msgid.link/20260518203116.42624-1-jamien@nvidia.com
(sudeep.holla: Minor rewordng of the commit message and subject)
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
drivers/firmware/arm_ffa/driver.c

index fb97b01c282aae6072fbca3963733f36781f1ac7..54984e1b97413eb9fea10f75f7d49a070331c578 100644 (file)
@@ -329,11 +329,9 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
 #define PART_INFO_EXEC_CXT_MASK        GENMASK(31, 16)
 #define PART_INFO_PROPS_MASK   GENMASK(63, 32)
 #define FFA_PART_INFO_GET_REGS_FIRST_REG       3
-#define FFA_PART_INFO_GET_REGS_REGS_PER_DESC   3
-#define FFA_PART_INFO_GET_REGS_MAX_DESC \
-       (((sizeof(ffa_value_t) / sizeof_field(ffa_value_t, a0)) - \
-         FFA_PART_INFO_GET_REGS_FIRST_REG) / \
-        FFA_PART_INFO_GET_REGS_REGS_PER_DESC)
+#define FFA_PART_INFO_GET_REGS_MIN_REGS_PER_DESC       3
+#define FFA_PART_INFO_GET_REGS_NUM_REGS \
+       (sizeof(ffa_value_t) / sizeof_field(ffa_value_t, a0))
 #define PART_INFO_ID(x)                ((u16)(FIELD_GET(PART_INFO_ID_MASK, (x))))
 #define PART_INFO_EXEC_CXT(x)  ((u16)(FIELD_GET(PART_INFO_EXEC_CXT_MASK, (x))))
 #define PART_INFO_PROPERTIES(x)        ((u32)(FIELD_GET(PART_INFO_PROPS_MASK, (x))))
@@ -347,7 +345,7 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
 
        do {
                __le64 *regs;
-               int idx, nr_desc, buf_idx;
+               int idx, nr_desc, buf_idx, regs_per_desc, max_desc;
 
                invoke_ffa_fn((ffa_value_t){
                              .a0 = FFA_PARTITION_INFO_GET_REGS,
@@ -370,8 +368,18 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
                if (cur_idx < start_idx || cur_idx >= count)
                        return -EINVAL;
 
+               buf_sz = PARTITION_INFO_SZ(partition_info.a2);
+               if (buf_sz % sizeof(*regs))
+                       return -EINVAL;
+
+               regs_per_desc = buf_sz / sizeof(*regs);
+               if (regs_per_desc < FFA_PART_INFO_GET_REGS_MIN_REGS_PER_DESC)
+                       return -EINVAL;
+
                nr_desc = cur_idx - start_idx + 1;
-               if (nr_desc > FFA_PART_INFO_GET_REGS_MAX_DESC)
+               max_desc = (FFA_PART_INFO_GET_REGS_NUM_REGS -
+                           FFA_PART_INFO_GET_REGS_FIRST_REG) / regs_per_desc;
+               if (nr_desc > max_desc)
                        return -EINVAL;
 
                buf_idx = buf - buffer;
@@ -379,9 +387,6 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
                        return -EINVAL;
 
                tag = UUID_INFO_TAG(partition_info.a2);
-               buf_sz = PARTITION_INFO_SZ(partition_info.a2);
-               if (buf_sz > sizeof(*buffer))
-                       buf_sz = sizeof(*buffer);
 
                regs = (void *)&partition_info.a3;
                for (idx = 0; idx < nr_desc; idx++, buf++) {
@@ -400,7 +405,7 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
                        buf->exec_ctxt = PART_INFO_EXEC_CXT(val);
                        buf->properties = PART_INFO_PROPERTIES(val);
                        uuid_copy(&buf->uuid, &uuid_regs.uuid);
-                       regs += 3;
+                       regs += regs_per_desc;
                }
                start_idx = cur_idx + 1;