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

Configure Feed

Select the types of activity you want to include in your feed.

USB: chaoskey: fail open after removal

chaoskey_open() takes the lock only to increase the
counter of openings. That means that the mutual exclusion
with chaoskey_disconnect() cannot prevent an increase
of the counter and chaoskey_open() returning a success.

If that race is hit, chaoskey_disconnect() will happily
free all resources associated with the device after
it has dropped the lock, as it has read the counter
as zero.

To prevent this race chaoskey_open() has to check
the presence of the device under the lock.
However, the current per device lock cannot be used,
because it is a part of the data structure to be
freed. Hence an additional global mutex is needed.
The issue is as old as the driver.

Signed-off-by: Oliver Neukum <oneukum@suse.com>
Reported-by: syzbot+422188bce66e76020e55@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=422188bce66e76020e55
Fixes: 66e3e591891da ("usb: Add driver for Altus Metrum ChaosKey device (v2)")
Rule: add
Link: https://lore.kernel.org/stable/20241002132201.552578-1-oneukum%40suse.com
Link: https://lore.kernel.org/r/20241002132201.552578-1-oneukum@suse.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Oliver Neukum and committed by
Greg Kroah-Hartman
422dc0a4 e0aa9614

+24 -11
+24 -11
drivers/usb/misc/chaoskey.c
··· 27 27 static int chaoskey_rng_read(struct hwrng *rng, void *data, 28 28 size_t max, bool wait); 29 29 30 + static DEFINE_MUTEX(chaoskey_list_lock); 31 + 30 32 #define usb_dbg(usb_if, format, arg...) \ 31 33 dev_dbg(&(usb_if)->dev, format, ## arg) 32 34 ··· 232 230 if (dev->hwrng_registered) 233 231 hwrng_unregister(&dev->hwrng); 234 232 233 + mutex_lock(&chaoskey_list_lock); 235 234 usb_deregister_dev(interface, &chaoskey_class); 236 235 237 236 usb_set_intfdata(interface, NULL); ··· 247 244 } else 248 245 mutex_unlock(&dev->lock); 249 246 247 + mutex_unlock(&chaoskey_list_lock); 250 248 usb_dbg(interface, "disconnect done"); 251 249 } 252 250 ··· 255 251 { 256 252 struct chaoskey *dev; 257 253 struct usb_interface *interface; 254 + int rv = 0; 258 255 259 256 /* get the interface from minor number and driver information */ 260 257 interface = usb_find_interface(&chaoskey_driver, iminor(inode)); ··· 271 266 } 272 267 273 268 file->private_data = dev; 269 + mutex_lock(&chaoskey_list_lock); 274 270 mutex_lock(&dev->lock); 275 - ++dev->open; 271 + if (dev->present) 272 + ++dev->open; 273 + else 274 + rv = -ENODEV; 276 275 mutex_unlock(&dev->lock); 276 + mutex_unlock(&chaoskey_list_lock); 277 277 278 - usb_dbg(interface, "open success"); 279 - return 0; 278 + return rv; 280 279 } 281 280 282 281 static int chaoskey_release(struct inode *inode, struct file *file) 283 282 { 284 283 struct chaoskey *dev = file->private_data; 285 284 struct usb_interface *interface; 285 + int rv = 0; 286 286 287 287 if (dev == NULL) 288 288 return -ENODEV; ··· 296 286 297 287 usb_dbg(interface, "release"); 298 288 289 + mutex_lock(&chaoskey_list_lock); 299 290 mutex_lock(&dev->lock); 300 291 301 292 usb_dbg(interface, "open count at release is %d", dev->open); 302 293 303 294 if (dev->open <= 0) { 304 295 usb_dbg(interface, "invalid open count (%d)", dev->open); 305 - mutex_unlock(&dev->lock); 306 - return -ENODEV; 296 + rv = -ENODEV; 297 + goto bail; 307 298 } 308 299 309 300 --dev->open; ··· 313 302 if (dev->open == 0) { 314 303 mutex_unlock(&dev->lock); 315 304 chaoskey_free(dev); 316 - } else 317 - mutex_unlock(&dev->lock); 318 - } else 319 - mutex_unlock(&dev->lock); 320 - 305 + goto destruction; 306 + } 307 + } 308 + bail: 309 + mutex_unlock(&dev->lock); 310 + destruction: 311 + mutex_lock(&chaoskey_list_lock); 321 312 usb_dbg(interface, "release success"); 322 - return 0; 313 + return rv; 323 314 } 324 315 325 316 static void chaos_read_callback(struct urb *urb)