Commit 3974ea19 authored by Sudeep Holla's avatar Sudeep Holla
Browse files

firmware: arm_ffa: Bound PARTITION_INFO_GET_REGS copies

The register-based PARTITION_INFO_GET path trusted the firmware-provided
indices when copying partition descriptors into the caller buffer.
Reject inconsistent counts or index progressions so the copy loop cannot
write past the allocated array.

Fixes: ba85c644 ("firmware: arm_ffa: Add support for FFA_PARTITION_INFO_GET_REGS")
Link: https://patch.msgid.link/20260428-ffa_fixes-v2-6-8595ae450034@kernel.org


(fixed cur_idx when exactly one descriptor in the first fragment)
Signed-off-by: default avatarSudeep Holla <sudeep.holla@kernel.org>
parent 6d3daa9b
Loading
Loading
Loading
Loading
+23 −6
Original line number Diff line number Diff line
@@ -323,6 +323,12 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
#define PART_INFO_ID_MASK	GENMASK(15, 0)
#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 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))))
@@ -330,15 +336,13 @@ static int
__ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
			      struct ffa_partition_info *buffer, int num_parts)
{
	u16 buf_sz, start_idx, cur_idx, count = 0, prev_idx = 0, tag = 0;
	u16 buf_sz, start_idx = 0, cur_idx, count = 0, tag = 0;
	struct ffa_partition_info *buf = buffer;
	ffa_value_t partition_info;

	do {
		__le64 *regs;
		int idx;

		start_idx = prev_idx ? prev_idx + 1 : 0;
		int idx, nr_desc, buf_idx;

		invoke_ffa_fn((ffa_value_t){
			      .a0 = FFA_PARTITION_INFO_GET_REGS,
@@ -354,15 +358,28 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
			count = PARTITION_COUNT(partition_info.a2);
		if (!buffer || !num_parts) /* count only */
			return count;
		if (count > num_parts)
			return -EINVAL;

		cur_idx = CURRENT_INDEX(partition_info.a2);
		if (cur_idx < start_idx || cur_idx >= count)
			return -EINVAL;

		nr_desc = cur_idx - start_idx + 1;
		if (nr_desc > FFA_PART_INFO_GET_REGS_MAX_DESC)
			return -EINVAL;

		buf_idx = buf - buffer;
		if (buf_idx + nr_desc > num_parts)
			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 < cur_idx - start_idx + 1; idx++, buf++) {
		for (idx = 0; idx < nr_desc; idx++, buf++) {
			union {
				uuid_t uuid;
				u64 regs[2];
@@ -380,7 +397,7 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
			uuid_copy(&buf->uuid, &uuid_regs.uuid);
			regs += 3;
		}
		prev_idx = cur_idx;
		start_idx = cur_idx + 1;

	} while (cur_idx < (count - 1));