From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CD1E423E320 for ; Wed, 17 Jun 2026 12:45:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781700349; cv=none; b=ADW9vkjc53i3QMZTwzRBRjmITJfLIJcRfB0/s/L0wZPMORehFWKAvtocuTrs9VvKvJ66JmncRL/LUnWK49f2pgbYQ7zwJK+Gj1KEyYHIwRWjTyrDgHfDj/r1lNTA/qkJ6bguBp1SY4Ux+sA3d7rVwDhAStbES+VOGf4bXn5o1jI= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781700349; c=relaxed/simple; bh=EYe2U5kb+DI1PVTkLIyCk/XXadoTiQIIClruG2jTE3Y=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=nJTjlkj0GPDLk3aJRT2uM3uC2F3ghxMyYCXLBtqO/pz9rzoEP61x24YhXwEzuedq86+rJ1ZWfEVqrDl+a5FbkxKeiegOgk6GT7BZluOPRm83f5OeOH0QP/qWuwmJaMjmhSV68OKS8EZMVh7xSc8ClMaDidGLy9ke+oXlGEUb9HI= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=AL9OL6BU; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="AL9OL6BU" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E3AF01F00A3A; Wed, 17 Jun 2026 12:45:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781700348; bh=icKfoAOd1q8p4FlZ96R9x14MG55Ag/Zn5WtF0D8ddAY=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=AL9OL6BUU4547wfDU7yF6BpnmBuHTa6c2HeBUa5ecoFcKTcxD+51Dog8//IEsrYPy SNE2qefgVoHLj02GIuKJLG0ED05PjMCGlpItTat849JU/xY+He3n22rd64gwOZrNVN QVArwtiRjj+qcxIhdUkI/wGmO04fgEyKRIKKRP5P6A7at0xvGhQHL4cN+pYt9It7Gs EgPxvUUy7FkxNw5OsvmHKwHdeYf/DtaYm7eFfKPPEIVTPXEYvWwwB3wnD06Ul+K60z cU30MFZ6yrTvbsCL2K5fP96tJ58kw4XhxYPOqzPMgFQH1NXucPFKAVQYvmthuaqmTM 1Dj0fgf58F6UA== From: Sasha Levin To: stable@vger.kernel.org Cc: Tudor Ambarus , Krzysztof Kozlowski , Sasha Levin Subject: [PATCH 7.0.y] firmware: samsung: acpm: Fix infinite loop on sequence number exhaustion Date: Wed, 17 Jun 2026 08:45:46 -0400 Message-ID: <20260617124546.3855844-1-sashal@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <2026061542-daintily-knickers-a496@gregkh> References: <2026061542-daintily-knickers-a496@gregkh> Precedence: bulk X-Mailing-List: stable@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: Tudor Ambarus [ Upstream commit 7fe40c32a33905302341797b5d12c541729dd08d ] Sashiko identified a possible infinite loop [1]. ACPM IPC sequence numbers are tracked via a 64-bit bitmap. Previously, acpm_prepare_xfer() used a do...while loop to search for a free sequence number. If all 63 available sequence numbers are leaked due to transient hardware timeouts or mailbox failures, the bitmap becomes full. The next call to acpm_prepare_xfer() would enter an infinite loop. Fix this by utilizing the kernel's optimized bitmap search functions (find_next_zero_bit / find_first_zero_bit). If the pool is completely exhausted, log the failure and return -EBUSY to allow the kernel to fail gracefully instead of hanging. Furthermore, drop the allocation loop entirely. Because acpm_prepare_xfer() is strictly called under the 'tx_lock' mutex, sequence number allocations are perfectly serialized. If find_next_zero_bit() locates a free bit, a single test_and_set_bit_lock() is mathematically guaranteed to succeed. To enforce this locking invariant, wrap the allocation in a WARN_ON_ONCE. If the atomic set fails, it indicates the driver's mutex serialization is fundamentally broken. The warning generates a stack trace for debugging, while returning -EIO immediately aborts the transfer to prevent silent payload corruption. Cc: stable@vger.kernel.org Fixes: a88927b534ba ("firmware: add Exynos ACPM protocol driver") Closes: https://sashiko.dev/#/patchset/20260420-acpm-tmu-v3-0-3dc8e93f0b26%40linaro.org [1] Signed-off-by: Tudor Ambarus Link: https://patch.msgid.link/20260505-acpm-fixes-sashiko-reports-v5-7-43b5ee7f1674@linaro.org Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sasha Levin --- drivers/firmware/samsung/exynos-acpm.c | 43 +++++++++++++++++++------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/drivers/firmware/samsung/exynos-acpm.c b/drivers/firmware/samsung/exynos-acpm.c index 987b59778ffc4a..075aadb10d23a3 100644 --- a/drivers/firmware/samsung/exynos-acpm.c +++ b/drivers/firmware/samsung/exynos-acpm.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -364,29 +365,47 @@ static int acpm_wait_for_queue_slots(struct acpm_chan *achan, u32 next_tx_front) * TX queue. * @achan: ACPM channel info. * @xfer: reference to the transfer being prepared. + * + * Return: 0 on success, -errno otherwise. */ -static void acpm_prepare_xfer(struct acpm_chan *achan, - const struct acpm_xfer *xfer) +static int acpm_prepare_xfer(struct acpm_chan *achan, + const struct acpm_xfer *xfer) { struct acpm_rx_data *rx_data; u32 *txd = (u32 *)xfer->txd; + unsigned long size = ACPM_SEQNUM_MAX - 1; + unsigned long bit = achan->seqnum; + + bit = find_next_zero_bit(achan->bitmap_seqnum, size, bit); + if (bit >= size) { + bit = find_first_zero_bit(achan->bitmap_seqnum, size); + if (bit >= size) { + dev_err_ratelimited(achan->acpm->dev, + "ACPM sequence number pool exhausted\n"); + return -EBUSY; + } + } - /* Prevent chan->seqnum from being re-used */ - do { - if (++achan->seqnum == ACPM_SEQNUM_MAX) - achan->seqnum = 1; - } while (test_bit(achan->seqnum - 1, achan->bitmap_seqnum)); + /* + * Execute the atomic set to formally claim the bit and establish + * LKMM Acquire semantics against the RX thread's clear_bit_unlock(). + * A loop is unnecessary because allocations are strictly serialized + * by tx_lock. + */ + if (WARN_ON_ONCE(test_and_set_bit_lock(bit, achan->bitmap_seqnum))) + return -EIO; + /* Flag the index based on seqnum. (seqnum: 1~63, bitmap: 0~62) */ + achan->seqnum = bit + 1; txd[0] |= FIELD_PREP(ACPM_PROTOCOL_SEQNUM, achan->seqnum); /* Clear data for upcoming responses */ - rx_data = &achan->rx_data[achan->seqnum - 1]; + rx_data = &achan->rx_data[bit]; memset(rx_data->cmd, 0, sizeof(*rx_data->cmd) * rx_data->n_cmd); if (xfer->rxd) rx_data->response = true; - /* Flag the index based on seqnum. (seqnum: 1~63, bitmap: 0~62) */ - set_bit(achan->seqnum - 1, achan->bitmap_seqnum); + return 0; } /** @@ -444,7 +463,9 @@ int acpm_do_xfer(struct acpm_handle *handle, const struct acpm_xfer *xfer) if (ret) return ret; - acpm_prepare_xfer(achan, xfer); + ret = acpm_prepare_xfer(achan, xfer); + if (ret) + return ret; /* Write TX command. */ __iowrite32_copy(achan->tx.base + achan->mlen * tx_front, -- 2.53.0