Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mfd: qcom-pm8xxx: Fix potential deadlock on &chip->pm_irq_lock

As &chip->pm_irq_lock is acquired by pm8xxx_irq_handler() under irq
context, other process context code should disable irq before acquiring
the lock.

Since .irq_set_type and .irq_get_irqchip_state callbacks are generally
executed from process context without irq disabled by default, the same
lock acquision should disable irq.

Possible deadlock scenario
pm8xxx_irq_set_type()
-> pm8xxx_config_irq()
-> spin_lock(&chip->pm_irq_lock)
<irq interrupt>
-> pm8xxx_irq_handler()
-> pm8xxx_irq_master_handler()
-> pm8xxx_irq_block_handler()
-> pm8xxx_read_block_irq()
-> spin_lock(&chip->pm_irq_lock) (deadlock here)

This flaw was found using an experimental static analysis tool we are
developing for irq-related deadlock.

Fix the potential deadlock by spin_lock_irqsave().

Signed-off-by: Chengfeng Ye <dg573847474@gmail.com>
Reviewed-by: Bjorn Andersson <quic_bjorande@quicinc.com>
Link: https://lore.kernel.org/r/20230720071330.50382-1-dg573847474@gmail.com
Signed-off-by: Lee Jones <lee@kernel.org>

authored by

Chengfeng Ye and committed by
Lee Jones
f1a63db6 506fbc6b

+6 -4
+6 -4
drivers/mfd/qcom-pm8xxx.c
··· 103 103 pm8xxx_config_irq(struct pm_irq_chip *chip, unsigned int bp, unsigned int cp) 104 104 { 105 105 int rc; 106 + unsigned long flags; 106 107 107 - spin_lock(&chip->pm_irq_lock); 108 + spin_lock_irqsave(&chip->pm_irq_lock, flags); 108 109 rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, bp); 109 110 if (rc) { 110 111 pr_err("Failed Selecting Block %d rc=%d\n", bp, rc); ··· 117 116 if (rc) 118 117 pr_err("Failed Configuring IRQ rc=%d\n", rc); 119 118 bail: 120 - spin_unlock(&chip->pm_irq_lock); 119 + spin_unlock_irqrestore(&chip->pm_irq_lock, flags); 121 120 return rc; 122 121 } 123 122 ··· 322 321 struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); 323 322 unsigned int pmirq = irqd_to_hwirq(d); 324 323 unsigned int bits; 324 + unsigned long flags; 325 325 int irq_bit; 326 326 u8 block; 327 327 int rc; ··· 333 331 block = pmirq / 8; 334 332 irq_bit = pmirq % 8; 335 333 336 - spin_lock(&chip->pm_irq_lock); 334 + spin_lock_irqsave(&chip->pm_irq_lock, flags); 337 335 rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, block); 338 336 if (rc) { 339 337 pr_err("Failed Selecting Block %d rc=%d\n", block, rc); ··· 348 346 349 347 *state = !!(bits & BIT(irq_bit)); 350 348 bail: 351 - spin_unlock(&chip->pm_irq_lock); 349 + spin_unlock_irqrestore(&chip->pm_irq_lock, flags); 352 350 353 351 return rc; 354 352 }