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

scsi: ch: fixup refcounting imbalance for SCSI devices

The SCSI device is required to be present during ch_probe() and ch_open().
But the SCSI device itself is only checked during ch_open(), so it's anyones
guess if it had been present during ch_probe(). And consequently we can't
reliably detach it during ch_release(), as ch_remove() might have been
called first. So initialize the changer device during ch_probe(), and take
a reference to the SCSI device during both ch_probe() and ch_open().

[mkp: fixed checkpatch warning]

Link: https://lore.kernel.org/r/20200213153207.123357-2-hare@suse.de
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>

authored by

Hannes Reinecke and committed by
Martin K. Petersen
66167283 1939295a

+18 -4
+18 -4
drivers/scsi/ch.c
··· 569 569 { 570 570 scsi_changer *ch = container_of(ref, scsi_changer, ref); 571 571 572 + ch->device = NULL; 572 573 kfree(ch->dt); 573 574 kfree(ch); 574 575 } ··· 595 594 spin_lock(&ch_index_lock); 596 595 ch = idr_find(&ch_index_idr, minor); 597 596 598 - if (NULL == ch || scsi_device_get(ch->device)) { 597 + if (ch == NULL || !kref_get_unless_zero(&ch->ref)) { 599 598 spin_unlock(&ch_index_lock); 600 599 mutex_unlock(&ch_mutex); 601 600 return -ENXIO; 602 601 } 603 - kref_get(&ch->ref); 604 602 spin_unlock(&ch_index_lock); 605 - 603 + if (scsi_device_get(ch->device)) { 604 + kref_put(&ch->ref, ch_destroy); 605 + mutex_unlock(&ch_mutex); 606 + return -ENXIO; 607 + } 606 608 file->private_data = ch; 607 609 mutex_unlock(&ch_mutex); 608 610 return 0; ··· 942 938 943 939 ch->minor = ret; 944 940 sprintf(ch->name,"ch%d",ch->minor); 941 + ret = scsi_device_get(sd); 942 + if (ret) { 943 + sdev_printk(KERN_WARNING, sd, "ch%d: failed to get device\n", 944 + ch->minor); 945 + goto remove_idr; 946 + } 945 947 946 948 class_dev = device_create(ch_sysfs_class, dev, 947 949 MKDEV(SCSI_CHANGER_MAJOR, ch->minor), ch, ··· 956 946 sdev_printk(KERN_WARNING, sd, "ch%d: device_create failed\n", 957 947 ch->minor); 958 948 ret = PTR_ERR(class_dev); 959 - goto remove_idr; 949 + goto put_device; 960 950 } 961 951 962 952 mutex_init(&ch->lock); ··· 974 964 return 0; 975 965 destroy_dev: 976 966 device_destroy(ch_sysfs_class, MKDEV(SCSI_CHANGER_MAJOR, ch->minor)); 967 + put_device: 968 + scsi_device_put(sd); 977 969 remove_idr: 978 970 idr_remove(&ch_index_idr, ch->minor); 979 971 free_ch: ··· 989 977 990 978 spin_lock(&ch_index_lock); 991 979 idr_remove(&ch_index_idr, ch->minor); 980 + dev_set_drvdata(dev, NULL); 992 981 spin_unlock(&ch_index_lock); 993 982 994 983 device_destroy(ch_sysfs_class, MKDEV(SCSI_CHANGER_MAJOR,ch->minor)); 984 + scsi_device_put(ch->device); 995 985 kref_put(&ch->ref, ch_destroy); 996 986 return 0; 997 987 }