i2c: imx: Fix slave registration race and error handling

In i2c_imx_reg_slave(), the slave pointer was assigned before
pm_runtime_resume_and_get().  If pm_runtime_resume_and_get() failed,
the error path returned without clearing i2c_imx->slave, leaving it
non-NULL and causing all subsequent registration attempts to fail
with -EBUSY.

Additionally, because this driver uses a shared IRQ, the interrupt
handler i2c_imx_isr() can execute concurrently and, after acquiring
slave_lock, dereference i2c_imx->slave.  The previous fix attempt
added a lockless i2c_imx->slave = NULL on the error path, but that
could race with the ISR under the lock and still cause a NULL pointer
dereference.

Fix both issues by deferring the assignment of i2c_imx->slave and
i2c_imx->last_slave_event to after a successful resume, and by
performing the assignment inside the slave_lock critical section.
This guarantees that the slave pointer is never left stale on the
error path and is always valid when observed by the interrupt handler.

Fixes: f7414cd692 ("i2c: imx: support slave mode for imx I2C driver")
Signed-off-by: Liem <liem16213@gmail.com>
Cc: <stable@vger.kernel.org> # v5.11+
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Acked-by: Carlos Song <carlos.song@nxp.com>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
Link: https://lore.kernel.org/r/20260629023829.152651-2-liem16213@gmail.com
This commit is contained in:
Liem 2026-06-29 10:38:28 +08:00 committed by Andi Shyti
parent 98f2e9e6d6
commit d64ec362c3
No known key found for this signature in database
GPG Key ID: DA78056626D32D6E

View File

@ -930,9 +930,6 @@ static int i2c_imx_reg_slave(struct i2c_client *client)
if (i2c_imx->slave)
return -EBUSY;
i2c_imx->slave = client;
i2c_imx->last_slave_event = I2C_SLAVE_STOP;
/* Resume */
ret = pm_runtime_resume_and_get(i2c_imx->adapter.dev.parent);
if (ret < 0) {
@ -940,6 +937,11 @@ static int i2c_imx_reg_slave(struct i2c_client *client)
return ret;
}
scoped_guard(spinlock_irqsave, &i2c_imx->slave_lock) {
i2c_imx->slave = client;
i2c_imx->last_slave_event = I2C_SLAVE_STOP;
}
i2c_imx_slave_init(i2c_imx);
return 0;