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

w1_therm reference count family data

A temperature conversion can take 750 ms and when possible the
w1_therm slave driver drops the bus_mutex to allow other bus
operations, but that includes operations such as a periodic slave
search, which can remove this slave when it is no longer detected.
If that happens the sl->family_data will be freed and set to NULL
causing w1_slave_show to crash when it wakes up.

Signed-off-by: David Fries <David@Fries.net>
Reported-By: Thorsten Bschorr <thorsten@bschorr.de>
Tested-by: Thorsten Bschorr <thorsten@bschorr.de>
Acked-by: Evgeniy Polyakov <zbr@ioremap.net>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

David Fries and committed by
Greg Kroah-Hartman
f7134eea c3098356

+47 -15
+47 -15
drivers/w1/slaves/w1_therm.c
··· 59 59 static int w1_strong_pullup = 1; 60 60 module_param_named(strong_pullup, w1_strong_pullup, int, 0); 61 61 62 + struct w1_therm_family_data { 63 + uint8_t rom[9]; 64 + atomic_t refcnt; 65 + }; 66 + 67 + /* return the address of the refcnt in the family data */ 68 + #define THERM_REFCNT(family_data) \ 69 + (&((struct w1_therm_family_data*)family_data)->refcnt) 70 + 62 71 static int w1_therm_add_slave(struct w1_slave *sl) 63 72 { 64 - sl->family_data = kzalloc(9, GFP_KERNEL); 73 + sl->family_data = kzalloc(sizeof(struct w1_therm_family_data), 74 + GFP_KERNEL); 65 75 if (!sl->family_data) 66 76 return -ENOMEM; 77 + atomic_set(THERM_REFCNT(sl->family_data), 1); 67 78 return 0; 68 79 } 69 80 70 81 static void w1_therm_remove_slave(struct w1_slave *sl) 71 82 { 83 + int refcnt = atomic_sub_return(1, THERM_REFCNT(sl->family_data)); 84 + while(refcnt) { 85 + msleep(1000); 86 + refcnt = atomic_read(THERM_REFCNT(sl->family_data)); 87 + } 72 88 kfree(sl->family_data); 73 89 sl->family_data = NULL; 74 90 } ··· 210 194 struct w1_slave *sl = dev_to_w1_slave(device); 211 195 struct w1_master *dev = sl->master; 212 196 u8 rom[9], crc, verdict, external_power; 213 - int i, max_trying = 10; 197 + int i, ret, max_trying = 10; 214 198 ssize_t c = PAGE_SIZE; 199 + u8 *family_data = sl->family_data; 215 200 216 - i = mutex_lock_interruptible(&dev->bus_mutex); 217 - if (i != 0) 218 - return i; 201 + ret = mutex_lock_interruptible(&dev->bus_mutex); 202 + if (ret != 0) 203 + goto post_unlock; 219 204 205 + if(!sl->family_data) 206 + { 207 + ret = -ENODEV; 208 + goto pre_unlock; 209 + } 210 + 211 + /* prevent the slave from going away in sleep */ 212 + atomic_inc(THERM_REFCNT(family_data)); 220 213 memset(rom, 0, sizeof(rom)); 221 214 222 215 while (max_trying--) { ··· 255 230 mutex_unlock(&dev->bus_mutex); 256 231 257 232 sleep_rem = msleep_interruptible(tm); 258 - if (sleep_rem != 0) 259 - return -EINTR; 233 + if (sleep_rem != 0) { 234 + ret = -EINTR; 235 + goto post_unlock; 236 + } 260 237 261 - i = mutex_lock_interruptible(&dev->bus_mutex); 262 - if (i != 0) 263 - return i; 238 + ret = mutex_lock_interruptible(&dev->bus_mutex); 239 + if (ret != 0) 240 + goto post_unlock; 264 241 } else if (!w1_strong_pullup) { 265 242 sleep_rem = msleep_interruptible(tm); 266 243 if (sleep_rem != 0) { 267 - mutex_unlock(&dev->bus_mutex); 268 - return -EINTR; 244 + ret = -EINTR; 245 + goto pre_unlock; 269 246 } 270 247 } 271 248 ··· 296 269 c -= snprintf(buf + PAGE_SIZE - c, c, ": crc=%02x %s\n", 297 270 crc, (verdict) ? "YES" : "NO"); 298 271 if (verdict) 299 - memcpy(sl->family_data, rom, sizeof(rom)); 272 + memcpy(family_data, rom, sizeof(rom)); 300 273 else 301 274 dev_warn(device, "Read failed CRC check\n"); 302 275 303 276 for (i = 0; i < 9; ++i) 304 277 c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", 305 - ((u8 *)sl->family_data)[i]); 278 + ((u8 *)family_data)[i]); 306 279 307 280 c -= snprintf(buf + PAGE_SIZE - c, c, "t=%d\n", 308 281 w1_convert_temp(rom, sl->family->fid)); 282 + ret = PAGE_SIZE - c; 283 + 284 + pre_unlock: 309 285 mutex_unlock(&dev->bus_mutex); 310 286 311 - return PAGE_SIZE - c; 287 + post_unlock: 288 + atomic_dec(THERM_REFCNT(family_data)); 289 + return ret; 312 290 } 313 291 314 292 static int __init w1_therm_init(void)