From: sashiko-bot@kernel.org
To: "Link Mauve" <linkmauve@linkmauve.fr>
Cc: neil.armstrong@linaro.org, Frank.Li@kernel.org, ojeda@kernel.org,
linux-amlogic@lists.infradead.org, imx@lists.linux.dev
Subject: Re: [PATCH v2 2/2] nvmem: make all reg_write callbacks take const void *
Date: Wed, 15 Jul 2026 20:21:01 +0000 [thread overview]
Message-ID: <20260715202101.9E1CD1F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260715195520.25410-3-linkmauve@linkmauve.fr>
Thank you for your contribution! Sashiko AI review found 10 potential issue(s) to consider:
New issues:
- [Critical] The nvmem core incorrectly evaluates write permissions, causing all drivers providing only `.reg_write_const` to be unconditionally treated as read-only.
Pre-existing issues:
- [Critical] The Spreadtrum eFuse driver incorrectly calculates memory offsets and size constraints, leading to out-of-bounds MMIO writes, memory corruption, and kernel stack information leaks.
- [High] The bcm-ocotp driver fails to disable programming mode on the error path.
- [High] The stm32-bsec-optee-ta driver leaks TEE sessions when unaligned writes are requested.
- [High] The imx-ocotp-scu driver has a Time-of-Check to Time-of-Use (TOCTOU) race condition when writing to ECC regions.
- [High] The meson-sm firmware driver exposes a lockless shared memory race when interacting with meson-efuse.
- [High] The lpc18xx-eeprom driver lacks serialization, causing a race condition on the EEPROM power state that can crash the system.
- [High] The qfprom driver modifies global clock rates and regulator voltages without serialization.
- [High] The lan9662-otpc driver modifies hardware state and registers without locking, leading to race conditions and fuse corruption.
- [High] The bcm-ocotp driver modifies command and address registers without locking, leading to OTP fuse corruption.
--
--- Patch [2]: [PATCH v2 2/2] nvmem: make all reg_write callbacks take const void * ---
commit 757b2f38ab10e7e5415fbd52779fa12f901c3ac4
Author: Link Mauve <linkmauve@linkmauve.fr>
Subject: nvmem: make all reg_write callbacks take const void *
The previous commit switched from a pointer to mutable data to a pointer
to immutable data, so let’s fix all users of this API.
> diff --git a/drivers/nvmem/apple-spmi-nvmem.c b/drivers/nvmem/apple-spmi-nvmem.c
> --- a/drivers/nvmem/apple-spmi-nvmem.c
> +++ b/drivers/nvmem/apple-spmi-nvmem.c
> @@ -29,7 +29,7 @@ static int apple_spmi_nvmem_probe(struct spmi_device *sdev)
> .stride = 1,
> .size = 0xffff,
> .reg_read = (void *)regmap_bulk_read,
> - .reg_write = (void *)regmap_bulk_write,
> + .reg_write_const = (void *)regmap_bulk_write,
[Severity: Critical]
By migrating to .reg_write_const and leaving .reg_write as NULL, does this
inadvertently make all these devices read-only?
Looking at the nvmem core in drivers/nvmem/core.c:
nvmem_register() {
...
nvmem->read_only = device_property_present(config->dev, "read-only") ||
config->read_only || !nvmem->reg_write ||
!nvmem->reg_write_const;
...
}
bin_attr_nvmem_write() {
...
if (!nvmem->reg_write || !nvmem->reg_write_const || nvmem->read_only)
return -EPERM;
...
}
Because the core checks !nvmem->reg_write || !nvmem->reg_write_const,
setting reg_write to NULL causes the core to strip write permissions and
return -EPERM for all sysfs writes.
> diff --git a/drivers/nvmem/bcm-ocotp.c b/drivers/nvmem/bcm-ocotp.c
> --- a/drivers/nvmem/bcm-ocotp.c
> +++ b/drivers/nvmem/bcm-ocotp.c
> [ ... ]
> @@ -179,11 +179,11 @@ static int bcm_otpc_read(void *context, unsigned int offset, void *val,
> return 0;
> }
>
> -static int bcm_otpc_write(void *context, unsigned int offset, void *val,
> +static int bcm_otpc_write(void *context, unsigned int offset, const void *val,
> size_t bytes)
> {
> struct otpc_priv *priv = context;
> - u32 *buf = val;
> + const u32 *buf = val;
[Severity: High]
This isn't a bug introduced by this patch, but is it possible for concurrent
reads and writes to corrupt hardware state?
In bcm_otpc_write():
for (bytes_written = 0; bytes_written < bytes;) {
set_command(priv->base, OTPC_CMD_PROGRAM);
set_cpu_address(priv->base, address++);
And similarly in bcm_otpc_read():
for (bytes_read = 0; bytes_read < bytes;) {
set_command(priv->base, OTPC_CMD_READ);
set_cpu_address(priv->base, address++);
Without serialization, concurrent sysfs operations could cause the command and
address registers to be overwritten mid-operation by another thread,
potentially redirecting writes to unintended fuse locations.
[Severity: High]
This isn't a bug introduced by this patch, but does the error path in
bcm_otpc_write() permanently leave the hardware in programming mode?
ret = enable_ocotp_program(priv->base);
if (ret)
return -EIO;
for (bytes_written = 0; bytes_written < bytes;) {
...
ret = poll_cpu_status(priv->base, OTPC_STAT_CMD_DONE);
reset_start_bit(priv->base);
if (ret) {
dev_err(priv->dev, "otp write error: 0x%x", ret);
return -EIO;
}
}
If poll_cpu_status() returns an error, the function returns -EIO directly and
skips the disable_ocotp_program(priv->base) call at the end of the function,
potentially leaving programming voltages applied.
> diff --git a/drivers/nvmem/imx-ocotp-scu.c b/drivers/nvmem/imx-ocotp-scu.c
> --- a/drivers/nvmem/imx-ocotp-scu.c
> +++ b/drivers/nvmem/imx-ocotp-scu.c
> @@ -178,11 +178,11 @@ static int imx_scu_ocotp_read(void *context, unsigned int offset,
> }
>
> static int imx_scu_ocotp_write(void *context, unsigned int offset,
> - void *val, size_t bytes)
> + const void *val, size_t bytes)
> {
> struct ocotp_priv *priv = context;
> struct arm_smccc_res res;
> - u32 *buf = val;
> + const u32 *buf = val;
[Severity: High]
This isn't a bug introduced by this patch, but is there a race condition when
writing to ECC regions in imx_scu_ocotp_write()?
if (in_ecc(context, index)) {
pr_warn("ECC region, only program once\n");
mutex_lock(&scu_ocotp_mutex);
ret = imx_sc_misc_otp_fuse_read(priv->nvmem_ipc, index, &tmp);
mutex_unlock(&scu_ocotp_mutex);
if (ret)
return ret;
if (tmp) {
pr_warn("ECC region, already has value: %x\n", tmp);
return -EIO;
}
}
mutex_lock(&scu_ocotp_mutex);
arm_smccc_smc(IMX_SIP_OTP_WRITE, index, *buf, 0, 0, 0, 0, 0, &res);
mutex_unlock(&scu_ocotp_mutex);
Because the lock is released after checking if the ECC region is programmed and
then re-acquired for the SMC write, two concurrent threads could both pass the
initial check and perform a double-write to the write-once ECC region.
> diff --git a/drivers/nvmem/lan9662-otpc.c b/drivers/nvmem/lan9662-otpc.c
> --- a/drivers/nvmem/lan9662-otpc.c
> +++ b/drivers/nvmem/lan9662-otpc.c
> @@ -140,10 +140,10 @@ static int lan9662_otp_read(void *context, unsigned int offset,
> }
>
> static int lan9662_otp_write(void *context, unsigned int offset,
> - void *_val, size_t bytes)
> + const void *_val, size_t bytes)
> {
> struct lan9662_otp *otp = context;
> - u8 *val = _val;
> + const u8 *val = _val;
[Severity: High]
This isn't a bug introduced by this patch, but can concurrent accesses to
lan9662_otp_write() and lan9662_otp_read() corrupt OTP operations?
for (i = 0; i < bytes; i++) {
if (val[i]) {
rc = lan9662_otp_read_byte(otp, offset + i, &data);
if (rc < 0)
break;
...
rc = lan9662_otp_write_byte(otp, offset + i, newdata);
Since these hardware register accesses and state changes (such as powering up
and polling) are performed without holding a local lock, concurrent sysfs
operations might overwrite each other's commands in flight, potentially
corrupting OTP data.
> diff --git a/drivers/nvmem/lpc18xx_eeprom.c b/drivers/nvmem/lpc18xx_eeprom.c
> --- a/drivers/nvmem/lpc18xx_eeprom.c
> +++ b/drivers/nvmem/lpc18xx_eeprom.c
> @@ -88,7 +88,7 @@ static int lpc18xx_eeprom_busywait_until_prog(struct lpc18xx_eeprom_dev *eeprom)
> }
>
> static int lpc18xx_eeprom_gather_write(void *context, unsigned int reg,
> - void *val, size_t bytes)
> + const void *val, size_t bytes)
> {
> struct lpc18xx_eeprom_dev *eeprom = context;
> unsigned int offset = reg;
> @@ -110,7 +110,7 @@ static int lpc18xx_eeprom_gather_write(void *context, unsigned int reg,
> usleep_range(100, 200);
>
> while (bytes) {
> - writel(*(u32 *)val, eeprom->mem_base + offset);
> + writel(*(const u32 *)val, eeprom->mem_base + offset);
[Severity: High]
This isn't a bug introduced by this patch, but does the lack of a lock around
EEPROM power management in lpc18xx_eeprom_gather_write() and
lpc18xx_eeprom_read() risk a system crash?
lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_PWRDWN,
LPC18XX_EEPROM_PWRDWN_NO);
usleep_range(100, 200);
...
writel(*(const u32 *)val, eeprom->mem_base + offset);
lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_PWRDWN,
LPC18XX_EEPROM_PWRDWN_YES);
If a read and write occur concurrently, one thread might power down the EEPROM
while the other is still inside its memory access loop. Accessing powered-down
MMIO regions typically causes a synchronous data abort.
> diff --git a/drivers/nvmem/meson-efuse.c b/drivers/nvmem/meson-efuse.c
> --- a/drivers/nvmem/meson-efuse.c
> +++ b/drivers/nvmem/meson-efuse.c
> @@ -27,12 +27,12 @@ static int meson_efuse_read(void *context, unsigned int offset,
> }
>
> static int meson_efuse_write(void *context, unsigned int offset,
> - void *val, size_t bytes)
> + const void *val, size_t bytes)
> {
> struct meson_sm_firmware *fw = context;
> int ret;
>
> - ret = meson_sm_call_write(fw, (u8 *)val, bytes, SM_EFUSE_WRITE, offset,
> + ret = meson_sm_call_write(fw, (void *)val, bytes, SM_EFUSE_WRITE, offset,
[Severity: High]
This isn't a bug introduced by this patch, but does meson_sm_call_write()
expose a lockless shared memory race?
In drivers/firmware/meson/meson_sm.c:
memcpy(fw->sm_shmem_in_base, buffer, size);
if (meson_sm_call(fw, cmd_index, &written, arg0, arg1, arg2, arg3, arg4) < 0)
return -EINVAL;
Since fw->sm_shmem_in_base is a global shared memory buffer and there is no
locking, concurrent writes could overwrite each other's payloads before the SMC
call executes, potentially writing corrupted or arbitrary data to the
write-once eFuses.
> diff --git a/drivers/nvmem/qfprom.c b/drivers/nvmem/qfprom.c
> --- a/drivers/nvmem/qfprom.c
> +++ b/drivers/nvmem/qfprom.c
> @@ -253,13 +253,13 @@ static int qfprom_enable_fuse_blowing(const struct qfprom_priv *priv,
> *
> * Return: 0 or -err.
> */
> -static int qfprom_reg_write(void *context, unsigned int reg, void *_val,
> +static int qfprom_reg_write(void *context, unsigned int reg, const void *_val,
> size_t bytes)
> {
> struct qfprom_priv *priv = context;
> struct qfprom_touched_values old;
> int words = bytes / 4;
> - u32 *value = _val;
> + const u32 *value = _val;
[Severity: High]
This isn't a bug introduced by this patch, but can concurrent calls to
qfprom_reg_write() leave the system clock and regulators permanently unbalanced?
ret = qfprom_enable_fuse_blowing(priv, &old);
...
qfprom_disable_fuse_blowing(priv, &old);
Inside qfprom_enable_fuse_blowing():
old->clk_rate = clk_get_rate(priv->secclk);
ret = clk_set_rate(priv->secclk, priv->soc_data->qfprom_blow_set_freq);
Because the old state is saved to a local stack variable without any mutex
serialization, a concurrent write could save the already-elevated clock rate
and regulator voltage as the "old" state. Upon completion, the hardware would
be restored to this elevated state rather than the original one.
> diff --git a/drivers/nvmem/sprd-efuse.c b/drivers/nvmem/sprd-efuse.c
> --- a/drivers/nvmem/sprd-efuse.c
> +++ b/drivers/nvmem/sprd-efuse.c
> @@ -192,7 +192,7 @@ static void sprd_efuse_set_prog_en(struct sprd_efuse *efuse, bool en)
> }
>
> static int sprd_efuse_raw_prog(struct sprd_efuse *efuse, u32 blk, bool doub,
> - bool lock, u32 *data)
> + bool lock, const u32 *data)
> {
> u32 status;
> int ret = 0;
> [ ... ]
> @@ -321,7 +321,7 @@ static int sprd_efuse_read(void *context, u32 offset, void *val, size_t bytes)
> return ret;
> }
>
> -static int sprd_efuse_write(void *context, u32 offset, void *val, size_t bytes)
> +static int sprd_efuse_write(void *context, u32 offset, const void *val, size_t bytes)
> {
> struct sprd_efuse *efuse = context;
> bool blk_double = efuse->data->blk_double;
[Severity: Critical]
This isn't a bug introduced by this patch, but does the offset handling in
sprd_efuse_write() and sprd_efuse_read() lead to out-of-bounds access?
In sprd_efuse_write(), the raw byte offset is passed directly as the block
index to sprd_efuse_raw_prog():
ret = sprd_efuse_raw_prog(efuse, offset, blk_double, lock, val);
Then, in sprd_efuse_raw_prog():
writel(*data, efuse->base + SPRD_EFUSE_MEM(blk));
The macro SPRD_EFUSE_MEM(blk) shifts the offset by 2 (multiplying by 4).
Because word_size = 1 and stride = 1, userspace can request arbitrary offsets,
causing this to write far outside the intended eFuse MMIO region.
Additionally, in sprd_efuse_read():
u32 data;
...
ret = sprd_efuse_raw_read(efuse, index, &data, blk_double);
if (!ret) {
data >>= blk_offset;
memcpy(val, &data, bytes);
}
If userspace requests a read larger than 4 bytes, memcpy() will read past the
local stack variable 'data' and copy uninitialized adjacent kernel stack memory
to userspace.
> diff --git a/drivers/nvmem/stm32-bsec-optee-ta.c b/drivers/nvmem/stm32-bsec-optee-ta.c
> --- a/drivers/nvmem/stm32-bsec-optee-ta.c
> +++ b/drivers/nvmem/stm32-bsec-optee-ta.c
> @@ -203,7 +203,7 @@ int stm32_bsec_optee_ta_read(struct tee_context *ctx, unsigned int offset,
>
> /* stm32_bsec_optee_ta_write() - nvmem write access using PTA client driver */
> int stm32_bsec_optee_ta_write(struct tee_context *ctx, unsigned int lower,
> - unsigned int offset, void *buf, size_t bytes)
> + unsigned int offset, const void *buf, size_t bytes)
> { struct tee_shm *shm;
[Severity: High]
This isn't a bug introduced by this patch, but does an unaligned write request
leak TEE sessions?
ret = stm32_bsec_ta_open_session(ctx, &session_id);
if (ret)
return ret;
/* Allow only writing complete 32-bits aligned words */
if ((bytes % 4) || (offset % 4))
return -EINVAL;
If the offset or bytes are not 32-bit aligned, the function returns -EINVAL
directly, bypassing the call to stm32_bsec_ta_close_session(ctx, session_id).
Because the nvmem core allows unaligned writes by default, this could be
triggered repeatedly to exhaust all available TEE sessions.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260715195520.25410-1-linkmauve@linkmauve.fr?part=2
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
WARNING: multiple messages have this Message-ID (diff)
From: sashiko-bot@kernel.org
To: "Link Mauve" <linkmauve@linkmauve.fr>
Cc: neil.armstrong@linaro.org, Frank.Li@kernel.org, ojeda@kernel.org,
linux-amlogic@lists.infradead.org, imx@lists.linux.dev
Subject: Re: [PATCH v2 2/2] nvmem: make all reg_write callbacks take const void *
Date: Wed, 15 Jul 2026 20:21:01 +0000 [thread overview]
Message-ID: <20260715202101.9E1CD1F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260715195520.25410-3-linkmauve@linkmauve.fr>
Thank you for your contribution! Sashiko AI review found 10 potential issue(s) to consider:
New issues:
- [Critical] The nvmem core incorrectly evaluates write permissions, causing all drivers providing only `.reg_write_const` to be unconditionally treated as read-only.
Pre-existing issues:
- [Critical] The Spreadtrum eFuse driver incorrectly calculates memory offsets and size constraints, leading to out-of-bounds MMIO writes, memory corruption, and kernel stack information leaks.
- [High] The bcm-ocotp driver fails to disable programming mode on the error path.
- [High] The stm32-bsec-optee-ta driver leaks TEE sessions when unaligned writes are requested.
- [High] The imx-ocotp-scu driver has a Time-of-Check to Time-of-Use (TOCTOU) race condition when writing to ECC regions.
- [High] The meson-sm firmware driver exposes a lockless shared memory race when interacting with meson-efuse.
- [High] The lpc18xx-eeprom driver lacks serialization, causing a race condition on the EEPROM power state that can crash the system.
- [High] The qfprom driver modifies global clock rates and regulator voltages without serialization.
- [High] The lan9662-otpc driver modifies hardware state and registers without locking, leading to race conditions and fuse corruption.
- [High] The bcm-ocotp driver modifies command and address registers without locking, leading to OTP fuse corruption.
--
--- Patch [2]: [PATCH v2 2/2] nvmem: make all reg_write callbacks take const void * ---
commit 757b2f38ab10e7e5415fbd52779fa12f901c3ac4
Author: Link Mauve <linkmauve@linkmauve.fr>
Subject: nvmem: make all reg_write callbacks take const void *
The previous commit switched from a pointer to mutable data to a pointer
to immutable data, so let’s fix all users of this API.
> diff --git a/drivers/nvmem/apple-spmi-nvmem.c b/drivers/nvmem/apple-spmi-nvmem.c
> --- a/drivers/nvmem/apple-spmi-nvmem.c
> +++ b/drivers/nvmem/apple-spmi-nvmem.c
> @@ -29,7 +29,7 @@ static int apple_spmi_nvmem_probe(struct spmi_device *sdev)
> .stride = 1,
> .size = 0xffff,
> .reg_read = (void *)regmap_bulk_read,
> - .reg_write = (void *)regmap_bulk_write,
> + .reg_write_const = (void *)regmap_bulk_write,
[Severity: Critical]
By migrating to .reg_write_const and leaving .reg_write as NULL, does this
inadvertently make all these devices read-only?
Looking at the nvmem core in drivers/nvmem/core.c:
nvmem_register() {
...
nvmem->read_only = device_property_present(config->dev, "read-only") ||
config->read_only || !nvmem->reg_write ||
!nvmem->reg_write_const;
...
}
bin_attr_nvmem_write() {
...
if (!nvmem->reg_write || !nvmem->reg_write_const || nvmem->read_only)
return -EPERM;
...
}
Because the core checks !nvmem->reg_write || !nvmem->reg_write_const,
setting reg_write to NULL causes the core to strip write permissions and
return -EPERM for all sysfs writes.
> diff --git a/drivers/nvmem/bcm-ocotp.c b/drivers/nvmem/bcm-ocotp.c
> --- a/drivers/nvmem/bcm-ocotp.c
> +++ b/drivers/nvmem/bcm-ocotp.c
> [ ... ]
> @@ -179,11 +179,11 @@ static int bcm_otpc_read(void *context, unsigned int offset, void *val,
> return 0;
> }
>
> -static int bcm_otpc_write(void *context, unsigned int offset, void *val,
> +static int bcm_otpc_write(void *context, unsigned int offset, const void *val,
> size_t bytes)
> {
> struct otpc_priv *priv = context;
> - u32 *buf = val;
> + const u32 *buf = val;
[Severity: High]
This isn't a bug introduced by this patch, but is it possible for concurrent
reads and writes to corrupt hardware state?
In bcm_otpc_write():
for (bytes_written = 0; bytes_written < bytes;) {
set_command(priv->base, OTPC_CMD_PROGRAM);
set_cpu_address(priv->base, address++);
And similarly in bcm_otpc_read():
for (bytes_read = 0; bytes_read < bytes;) {
set_command(priv->base, OTPC_CMD_READ);
set_cpu_address(priv->base, address++);
Without serialization, concurrent sysfs operations could cause the command and
address registers to be overwritten mid-operation by another thread,
potentially redirecting writes to unintended fuse locations.
[Severity: High]
This isn't a bug introduced by this patch, but does the error path in
bcm_otpc_write() permanently leave the hardware in programming mode?
ret = enable_ocotp_program(priv->base);
if (ret)
return -EIO;
for (bytes_written = 0; bytes_written < bytes;) {
...
ret = poll_cpu_status(priv->base, OTPC_STAT_CMD_DONE);
reset_start_bit(priv->base);
if (ret) {
dev_err(priv->dev, "otp write error: 0x%x", ret);
return -EIO;
}
}
If poll_cpu_status() returns an error, the function returns -EIO directly and
skips the disable_ocotp_program(priv->base) call at the end of the function,
potentially leaving programming voltages applied.
> diff --git a/drivers/nvmem/imx-ocotp-scu.c b/drivers/nvmem/imx-ocotp-scu.c
> --- a/drivers/nvmem/imx-ocotp-scu.c
> +++ b/drivers/nvmem/imx-ocotp-scu.c
> @@ -178,11 +178,11 @@ static int imx_scu_ocotp_read(void *context, unsigned int offset,
> }
>
> static int imx_scu_ocotp_write(void *context, unsigned int offset,
> - void *val, size_t bytes)
> + const void *val, size_t bytes)
> {
> struct ocotp_priv *priv = context;
> struct arm_smccc_res res;
> - u32 *buf = val;
> + const u32 *buf = val;
[Severity: High]
This isn't a bug introduced by this patch, but is there a race condition when
writing to ECC regions in imx_scu_ocotp_write()?
if (in_ecc(context, index)) {
pr_warn("ECC region, only program once\n");
mutex_lock(&scu_ocotp_mutex);
ret = imx_sc_misc_otp_fuse_read(priv->nvmem_ipc, index, &tmp);
mutex_unlock(&scu_ocotp_mutex);
if (ret)
return ret;
if (tmp) {
pr_warn("ECC region, already has value: %x\n", tmp);
return -EIO;
}
}
mutex_lock(&scu_ocotp_mutex);
arm_smccc_smc(IMX_SIP_OTP_WRITE, index, *buf, 0, 0, 0, 0, 0, &res);
mutex_unlock(&scu_ocotp_mutex);
Because the lock is released after checking if the ECC region is programmed and
then re-acquired for the SMC write, two concurrent threads could both pass the
initial check and perform a double-write to the write-once ECC region.
> diff --git a/drivers/nvmem/lan9662-otpc.c b/drivers/nvmem/lan9662-otpc.c
> --- a/drivers/nvmem/lan9662-otpc.c
> +++ b/drivers/nvmem/lan9662-otpc.c
> @@ -140,10 +140,10 @@ static int lan9662_otp_read(void *context, unsigned int offset,
> }
>
> static int lan9662_otp_write(void *context, unsigned int offset,
> - void *_val, size_t bytes)
> + const void *_val, size_t bytes)
> {
> struct lan9662_otp *otp = context;
> - u8 *val = _val;
> + const u8 *val = _val;
[Severity: High]
This isn't a bug introduced by this patch, but can concurrent accesses to
lan9662_otp_write() and lan9662_otp_read() corrupt OTP operations?
for (i = 0; i < bytes; i++) {
if (val[i]) {
rc = lan9662_otp_read_byte(otp, offset + i, &data);
if (rc < 0)
break;
...
rc = lan9662_otp_write_byte(otp, offset + i, newdata);
Since these hardware register accesses and state changes (such as powering up
and polling) are performed without holding a local lock, concurrent sysfs
operations might overwrite each other's commands in flight, potentially
corrupting OTP data.
> diff --git a/drivers/nvmem/lpc18xx_eeprom.c b/drivers/nvmem/lpc18xx_eeprom.c
> --- a/drivers/nvmem/lpc18xx_eeprom.c
> +++ b/drivers/nvmem/lpc18xx_eeprom.c
> @@ -88,7 +88,7 @@ static int lpc18xx_eeprom_busywait_until_prog(struct lpc18xx_eeprom_dev *eeprom)
> }
>
> static int lpc18xx_eeprom_gather_write(void *context, unsigned int reg,
> - void *val, size_t bytes)
> + const void *val, size_t bytes)
> {
> struct lpc18xx_eeprom_dev *eeprom = context;
> unsigned int offset = reg;
> @@ -110,7 +110,7 @@ static int lpc18xx_eeprom_gather_write(void *context, unsigned int reg,
> usleep_range(100, 200);
>
> while (bytes) {
> - writel(*(u32 *)val, eeprom->mem_base + offset);
> + writel(*(const u32 *)val, eeprom->mem_base + offset);
[Severity: High]
This isn't a bug introduced by this patch, but does the lack of a lock around
EEPROM power management in lpc18xx_eeprom_gather_write() and
lpc18xx_eeprom_read() risk a system crash?
lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_PWRDWN,
LPC18XX_EEPROM_PWRDWN_NO);
usleep_range(100, 200);
...
writel(*(const u32 *)val, eeprom->mem_base + offset);
lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_PWRDWN,
LPC18XX_EEPROM_PWRDWN_YES);
If a read and write occur concurrently, one thread might power down the EEPROM
while the other is still inside its memory access loop. Accessing powered-down
MMIO regions typically causes a synchronous data abort.
> diff --git a/drivers/nvmem/meson-efuse.c b/drivers/nvmem/meson-efuse.c
> --- a/drivers/nvmem/meson-efuse.c
> +++ b/drivers/nvmem/meson-efuse.c
> @@ -27,12 +27,12 @@ static int meson_efuse_read(void *context, unsigned int offset,
> }
>
> static int meson_efuse_write(void *context, unsigned int offset,
> - void *val, size_t bytes)
> + const void *val, size_t bytes)
> {
> struct meson_sm_firmware *fw = context;
> int ret;
>
> - ret = meson_sm_call_write(fw, (u8 *)val, bytes, SM_EFUSE_WRITE, offset,
> + ret = meson_sm_call_write(fw, (void *)val, bytes, SM_EFUSE_WRITE, offset,
[Severity: High]
This isn't a bug introduced by this patch, but does meson_sm_call_write()
expose a lockless shared memory race?
In drivers/firmware/meson/meson_sm.c:
memcpy(fw->sm_shmem_in_base, buffer, size);
if (meson_sm_call(fw, cmd_index, &written, arg0, arg1, arg2, arg3, arg4) < 0)
return -EINVAL;
Since fw->sm_shmem_in_base is a global shared memory buffer and there is no
locking, concurrent writes could overwrite each other's payloads before the SMC
call executes, potentially writing corrupted or arbitrary data to the
write-once eFuses.
> diff --git a/drivers/nvmem/qfprom.c b/drivers/nvmem/qfprom.c
> --- a/drivers/nvmem/qfprom.c
> +++ b/drivers/nvmem/qfprom.c
> @@ -253,13 +253,13 @@ static int qfprom_enable_fuse_blowing(const struct qfprom_priv *priv,
> *
> * Return: 0 or -err.
> */
> -static int qfprom_reg_write(void *context, unsigned int reg, void *_val,
> +static int qfprom_reg_write(void *context, unsigned int reg, const void *_val,
> size_t bytes)
> {
> struct qfprom_priv *priv = context;
> struct qfprom_touched_values old;
> int words = bytes / 4;
> - u32 *value = _val;
> + const u32 *value = _val;
[Severity: High]
This isn't a bug introduced by this patch, but can concurrent calls to
qfprom_reg_write() leave the system clock and regulators permanently unbalanced?
ret = qfprom_enable_fuse_blowing(priv, &old);
...
qfprom_disable_fuse_blowing(priv, &old);
Inside qfprom_enable_fuse_blowing():
old->clk_rate = clk_get_rate(priv->secclk);
ret = clk_set_rate(priv->secclk, priv->soc_data->qfprom_blow_set_freq);
Because the old state is saved to a local stack variable without any mutex
serialization, a concurrent write could save the already-elevated clock rate
and regulator voltage as the "old" state. Upon completion, the hardware would
be restored to this elevated state rather than the original one.
> diff --git a/drivers/nvmem/sprd-efuse.c b/drivers/nvmem/sprd-efuse.c
> --- a/drivers/nvmem/sprd-efuse.c
> +++ b/drivers/nvmem/sprd-efuse.c
> @@ -192,7 +192,7 @@ static void sprd_efuse_set_prog_en(struct sprd_efuse *efuse, bool en)
> }
>
> static int sprd_efuse_raw_prog(struct sprd_efuse *efuse, u32 blk, bool doub,
> - bool lock, u32 *data)
> + bool lock, const u32 *data)
> {
> u32 status;
> int ret = 0;
> [ ... ]
> @@ -321,7 +321,7 @@ static int sprd_efuse_read(void *context, u32 offset, void *val, size_t bytes)
> return ret;
> }
>
> -static int sprd_efuse_write(void *context, u32 offset, void *val, size_t bytes)
> +static int sprd_efuse_write(void *context, u32 offset, const void *val, size_t bytes)
> {
> struct sprd_efuse *efuse = context;
> bool blk_double = efuse->data->blk_double;
[Severity: Critical]
This isn't a bug introduced by this patch, but does the offset handling in
sprd_efuse_write() and sprd_efuse_read() lead to out-of-bounds access?
In sprd_efuse_write(), the raw byte offset is passed directly as the block
index to sprd_efuse_raw_prog():
ret = sprd_efuse_raw_prog(efuse, offset, blk_double, lock, val);
Then, in sprd_efuse_raw_prog():
writel(*data, efuse->base + SPRD_EFUSE_MEM(blk));
The macro SPRD_EFUSE_MEM(blk) shifts the offset by 2 (multiplying by 4).
Because word_size = 1 and stride = 1, userspace can request arbitrary offsets,
causing this to write far outside the intended eFuse MMIO region.
Additionally, in sprd_efuse_read():
u32 data;
...
ret = sprd_efuse_raw_read(efuse, index, &data, blk_double);
if (!ret) {
data >>= blk_offset;
memcpy(val, &data, bytes);
}
If userspace requests a read larger than 4 bytes, memcpy() will read past the
local stack variable 'data' and copy uninitialized adjacent kernel stack memory
to userspace.
> diff --git a/drivers/nvmem/stm32-bsec-optee-ta.c b/drivers/nvmem/stm32-bsec-optee-ta.c
> --- a/drivers/nvmem/stm32-bsec-optee-ta.c
> +++ b/drivers/nvmem/stm32-bsec-optee-ta.c
> @@ -203,7 +203,7 @@ int stm32_bsec_optee_ta_read(struct tee_context *ctx, unsigned int offset,
>
> /* stm32_bsec_optee_ta_write() - nvmem write access using PTA client driver */
> int stm32_bsec_optee_ta_write(struct tee_context *ctx, unsigned int lower,
> - unsigned int offset, void *buf, size_t bytes)
> + unsigned int offset, const void *buf, size_t bytes)
> { struct tee_shm *shm;
[Severity: High]
This isn't a bug introduced by this patch, but does an unaligned write request
leak TEE sessions?
ret = stm32_bsec_ta_open_session(ctx, &session_id);
if (ret)
return ret;
/* Allow only writing complete 32-bits aligned words */
if ((bytes % 4) || (offset % 4))
return -EINVAL;
If the offset or bytes are not 32-bit aligned, the function returns -EINVAL
directly, bypassing the call to stm32_bsec_ta_close_session(ctx, session_id).
Because the nvmem core allows unaligned writes by default, this could be
triggered repeatedly to exhaust all available TEE sessions.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260715195520.25410-1-linkmauve@linkmauve.fr?part=2
next prev parent reply other threads:[~2026-07-15 20:21 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-15 19:55 [PATCH v2 0/2] nvmem: fix a const-unsoundness in reg_write Link Mauve
2026-07-15 19:55 ` Link Mauve
2026-07-15 19:55 ` [PATCH v2 1/2] nvmem: core: deprecate reg_write callback and add reg_write_const Link Mauve
2026-07-15 19:55 ` Link Mauve
2026-07-15 20:04 ` sashiko-bot
2026-07-15 20:04 ` sashiko-bot
2026-07-15 19:55 ` [PATCH v2 2/2] nvmem: make all reg_write callbacks take const void * Link Mauve
2026-07-15 19:55 ` Link Mauve
2026-07-15 20:21 ` sashiko-bot [this message]
2026-07-15 20:21 ` sashiko-bot
2026-07-16 9:03 ` [PATCH v2 0/2] nvmem: fix a const-unsoundness in reg_write Andy Shevchenko
2026-07-16 9:03 ` Andy Shevchenko
2026-07-16 11:07 ` Link Mauve
2026-07-16 11:07 ` Link Mauve
2026-07-16 17:48 ` Andy Shevchenko
2026-07-16 17:48 ` Andy Shevchenko
2026-07-16 11:29 ` Link Mauve
2026-07-16 11:29 ` Link Mauve
2026-07-16 17:50 ` Andy Shevchenko
2026-07-16 17:50 ` Andy Shevchenko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260715202101.9E1CD1F000E9@smtp.kernel.org \
--to=sashiko-bot@kernel.org \
--cc=Frank.Li@kernel.org \
--cc=imx@lists.linux.dev \
--cc=linkmauve@linkmauve.fr \
--cc=linux-amlogic@lists.infradead.org \
--cc=neil.armstrong@linaro.org \
--cc=ojeda@kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.