[PATCH] hwrng: cctrng - Lock read and writer of circular buffer

From: Herbert Xu

Date: Mon Oct 26 2026 - 01:56:12 EST


On Thu, Oct 15, 2026 at 12:39:34PM +0200, Thorsten Blum wrote:
> cctrng_read() copies random data in two steps when a read wraps around
> the end of the circular buffer.
>
> Use the correct destination offset for the second copy instead of
> overwriting the start of the destination buffer.
>
> Fixes: a583ed310bb6 ("hwrng: cctrng - introduce Arm CryptoCell driver")
> Cc: stable@xxxxxxxxxxxxxxx
> Signed-off-by: Thorsten Blum <blum@xxxxxxxxxx>
> ---
> drivers/char/hw_random/cctrng.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)

Sashiko complained about the lack of memory barries in the circular
buffer code. This is taking a spinlock on the read-path, so it might
as well do it for both reading and writing.

I've also changed it to a mutex and added support for the wait flag.

---8<---
The circular buffer handling in cctrng is buggy as it doesn't use
any synchronisation mechanism between the reader and the writer.

As this isn't performance-critical, simply change it to use a mutex
that covers both reading and writing.

While adding an inclusion for linux/err.h and linux/mutex.h, also
sort the headers alphabetically to avoid potential duplicates.

Fixes: a583ed310bb6 ("hwrng: cctrng - introduce Arm CryptoCell driver")
Signed-off-by: Herbert Xu <herbert@xxxxxxxxxxxxxxxxxxx>

diff --git a/drivers/char/hw_random/cctrng.c b/drivers/char/hw_random/cctrng.c
index 03401ee59a8e..ef49e0195ee9 100644
--- a/drivers/char/hw_random/cctrng.c
+++ b/drivers/char/hw_random/cctrng.c
@@ -1,21 +1,23 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2019-2020 ARM Limited or its affiliates. */

-#include <linux/kernel.h>
-#include <linux/module.h>
+#include <linux/bitfield.h>
+#include <linux/circ_buf.h>
#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/err.h>
+#include <linux/fips.h>
+#include <linux/kernel.h>
#include <linux/hw_random.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/irqreturn.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
-#include <linux/interrupt.h>
-#include <linux/irqreturn.h>
#include <linux/workqueue.h>
-#include <linux/circ_buf.h>
-#include <linux/completion.h>
-#include <linux/of.h>
-#include <linux/bitfield.h>
-#include <linux/fips.h>

#include "cctrng.h"

@@ -68,8 +70,8 @@ struct cctrng_drvdata {
/* pending_hw - 1 when HW is pending, 0 when it is idle */
atomic_t pending_hw;

- /* protects against multiple concurrent consumers of data_buf */
- spinlock_t read_lock;
+ /* protects data_buf */
+ struct mutex lock;
};


@@ -213,11 +215,8 @@ static int cctrng_read(struct hwrng *rng, void *data, size_t max, bool wait)
size_t size;
size_t left;

- if (!spin_trylock(&drvdata->read_lock)) {
- /* concurrent consumers from data_buf cannot be served */
- dev_dbg_ratelimited(dev, "unable to hold lock\n");
- return 0;
- }
+ if (mutex_lock_interruptible(&drvdata->lock))
+ return -ERESTARTSYS;

/* copy till end of data buffer (without wrap back) */
cnt_w = CIRC_CNT_TO_END(drvdata->circ.head,
@@ -237,7 +236,7 @@ static int cctrng_read(struct hwrng *rng, void *data, size_t max, bool wait)
circ_idx_inc(&drvdata->circ.tail, size);
}

- spin_unlock(&drvdata->read_lock);
+ mutex_unlock(&drvdata->lock);

if (circ_buf_space(drvdata) >= CC_TRNG_EHR_IN_WORDS) {
if (atomic_cmpxchg(&drvdata->pending_hw, 0, 1) == 0) {
@@ -347,6 +346,7 @@ static void cc_trng_compwork_handler(struct work_struct *w)
}

/* read EHR data from registers */
+ mutex_lock(&drvdata->lock);
for (i = 0; i < CC_TRNG_EHR_IN_WORDS; i++) {
/* calc word ptr in data_buf */
u32 *buf = (u32 *)drvdata->circ.buf;
@@ -358,6 +358,7 @@ static void cc_trng_compwork_handler(struct work_struct *w)
* returned, restart the entropy collection.
*/
if (buf[drvdata->circ.head] == 0) {
+ mutex_unlock(&drvdata->lock);
dev_dbg(dev, "Got 0 value in EHR. active_rosc %u\n",
drvdata->active_rosc);
goto next_rosc;
@@ -365,6 +366,7 @@ static void cc_trng_compwork_handler(struct work_struct *w)

circ_idx_inc(&drvdata->circ.head, 1<<2);
}
+ mutex_unlock(&drvdata->lock);

atomic_set(&drvdata->pending_hw, 0);

@@ -499,7 +501,7 @@ static int cctrng_probe(struct platform_device *pdev)

INIT_WORK(&drvdata->compwork, cc_trng_compwork_handler);
INIT_WORK(&drvdata->startwork, cc_trng_startwork_handler);
- spin_lock_init(&drvdata->read_lock);
+ mutex_init(&drvdata->lock);

/* register the driver isr function */
rc = devm_request_irq(dev, irq, cc_isr, IRQF_SHARED, "cctrng", drvdata);
--
Email: Herbert Xu <herbert@xxxxxxxxxxxxxxxxxxx>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt