From f53459c00871a7c3db5b69b49d840ca9d497a695 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sun, 22 Oct 2017 01:03:44 +0300 Subject: w1: keep balance of mutex locks and refcnts w1_therm_eeprom() and w1_DS18B20_precision() decrement THERM_REFCNT on error paths, while they did not increment it yet. read_therm() unlocks bus mutex on some error paths, while it is not acquired. The patch makes sure all the functions keep the balance in usage of the mutex and the THERM_REFCNT. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: Greg Kroah-Hartman --- drivers/w1/slaves/w1_therm.c | 59 +++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 28 deletions(-) (limited to 'drivers/w1') diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index 259525c3382a..3c350dfbcd0b 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -268,17 +268,18 @@ static inline int w1_therm_eeprom(struct device *device) int ret, max_trying = 10; u8 *family_data = sl->family_data; - ret = mutex_lock_interruptible(&dev->bus_mutex); - if (ret != 0) - goto post_unlock; - if (!sl->family_data) { ret = -ENODEV; - goto pre_unlock; + goto error; } /* prevent the slave from going away in sleep */ atomic_inc(THERM_REFCNT(family_data)); + + ret = mutex_lock_interruptible(&dev->bus_mutex); + if (ret != 0) + goto dec_refcnt; + memset(rom, 0, sizeof(rom)); while (max_trying--) { @@ -306,17 +307,17 @@ static inline int w1_therm_eeprom(struct device *device) sleep_rem = msleep_interruptible(tm); if (sleep_rem != 0) { ret = -EINTR; - goto post_unlock; + goto dec_refcnt; } ret = mutex_lock_interruptible(&dev->bus_mutex); if (ret != 0) - goto post_unlock; + goto dec_refcnt; } else if (!w1_strong_pullup) { sleep_rem = msleep_interruptible(tm); if (sleep_rem != 0) { ret = -EINTR; - goto pre_unlock; + goto mt_unlock; } } @@ -324,11 +325,11 @@ static inline int w1_therm_eeprom(struct device *device) } } -pre_unlock: +mt_unlock: mutex_unlock(&dev->bus_mutex); - -post_unlock: +dec_refcnt: atomic_dec(THERM_REFCNT(family_data)); +error: return ret; } @@ -350,20 +351,22 @@ static inline int w1_DS18B20_precision(struct device *device, int val) if (val > 12 || val < 9) { pr_warn("Unsupported precision\n"); - return -1; + ret = -EINVAL; + goto error; } - ret = mutex_lock_interruptible(&dev->bus_mutex); - if (ret != 0) - goto post_unlock; - if (!sl->family_data) { ret = -ENODEV; - goto pre_unlock; + goto error; } /* prevent the slave from going away in sleep */ atomic_inc(THERM_REFCNT(family_data)); + + ret = mutex_lock_interruptible(&dev->bus_mutex); + if (ret != 0) + goto dec_refcnt; + memset(rom, 0, sizeof(rom)); /* translate precision to bitmask (see datasheet page 9) */ @@ -411,11 +414,10 @@ static inline int w1_DS18B20_precision(struct device *device, int val) } } -pre_unlock: mutex_unlock(&dev->bus_mutex); - -post_unlock: +dec_refcnt: atomic_dec(THERM_REFCNT(family_data)); +error: return ret; } @@ -490,17 +492,18 @@ static ssize_t read_therm(struct device *device, int ret, max_trying = 10; u8 *family_data = sl->family_data; - ret = mutex_lock_interruptible(&dev->bus_mutex); - if (ret != 0) - goto error; - if (!family_data) { ret = -ENODEV; - goto mt_unlock; + goto error; } /* prevent the slave from going away in sleep */ atomic_inc(THERM_REFCNT(family_data)); + + ret = mutex_lock_interruptible(&dev->bus_mutex); + if (ret != 0) + goto dec_refcnt; + memset(info->rom, 0, sizeof(info->rom)); while (max_trying--) { @@ -542,7 +545,7 @@ static ssize_t read_therm(struct device *device, sleep_rem = msleep_interruptible(tm); if (sleep_rem != 0) { ret = -EINTR; - goto dec_refcnt; + goto mt_unlock; } } @@ -567,10 +570,10 @@ static ssize_t read_therm(struct device *device, break; } -dec_refcnt: - atomic_dec(THERM_REFCNT(family_data)); mt_unlock: mutex_unlock(&dev->bus_mutex); +dec_refcnt: + atomic_dec(THERM_REFCNT(family_data)); error: return ret; } -- cgit