Re: [PATCH] scsi: ch: Do not keep references to data transfer element devices

From: Laurence Oberman

Date: Tue Sep 22 2026 - 16:55:47 EST


On Tue, 2026-09-22 at 15:46 +0000, Ahmed Abdelhaleem Ahmed via B4 Relay
wrote:
> From: Ahmed Abdelhaleem Ahmed <ahmedhal@xxxxxxxxx>
>
> ch_readconfig() looks up the scsi_device of every data transfer
> element
> whose SCSI id the changer reports in READ ELEMENT STATUS, and stores
> it
> in ch->dt[]. scsi_device_lookup() takes a reference, and nothing ever
> drops it: ch_destroy() frees the array with kfree(). ch->dt[] is read
> nowhere else - it only supplies the vendor, model and revision
> printed
> in the same loop.
>
> Once such a drive is removed, its scsi_device can never be released.
> It
> stays on the host's device list at its address, so a new device there
> is
> refused by anything that walks the list - target_core_pscsi reports
> "scsi_device_get() failed for H:C:T:L" - and the low-level driver's
> module can no longer be unloaded. Only a reboot recovers.
>
> It shows with any changer that reports its drives' ids; with the
> mhvtl
> virtual library (IBM 3573-TL personality), each create and remove of
> a
> library with four drives leaves four references behind, counted by
> the
> module's use count in lsmod. With ch not bound the count is
> unchanged,
> and with this patch applied it is unchanged too.
>
> Drop the reference as soon as the name has been printed, and remove
> the
> now unused dt[] array. That also removes the array leaked when
> ch_probe() fails after ch_readconfig().
>
> The same leak was reported with an RFC patch in 2022, which was not
> merged.
>
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Cc: stable@xxxxxxxxxxxxxxx
> Link:
> https://lore.kernel.org/linux-scsi/20220719075442.6215-1-yanghao_ht@xxxxxxx/
> Signed-off-by: Ahmed Abdelhaleem Ahmed <ahmedhal@xxxxxxxxx>
> ---
> Reproduced and tested on RHEL 9 (5.14.0-687.47.1.el9_8) with mhvtl,
> IBM 3573-TL personality, four ULT3580-HHA drives, library created and
> removed; mhvtl module use count leaked:
>
>   stock ch.ko:                          4
>   ch.ko with this fix:                  0
>   ch.ko with this fix, changer daemon
>   restarted twice while it existed:     0
>
> Build-tested on mkp/scsi.git for-next.
> ---
>  drivers/scsi/ch.c | 29 +++++++++--------------------
>  1 file changed, 9 insertions(+), 20 deletions(-)
>
> diff --git a/drivers/scsi/ch.c b/drivers/scsi/ch.c
> index 87e51e50a..b2c9fb71c 100644
> --- a/drivers/scsi/ch.c
> +++ b/drivers/scsi/ch.c
> @@ -112,7 +112,6 @@ typedef struct {
>   int                 minor;
>   char                name[8];
>   struct scsi_device  *device;
> - struct scsi_device  **dt;        /* ptrs to data transfer
> elements */
>   u_int               firsts[CH_TYPES];
>   u_int               counts[CH_TYPES];
>   u_int     voltags;
> @@ -354,15 +353,10 @@ ch_readconfig(scsi_changer *ch)
>   vendor_labels[i]);
>   }
>  
> - /* look up the devices of the data transfer elements */
> - ch->dt = kzalloc_objs(*ch->dt, ch->counts[CHET_DT]);
> -
> - if (!ch->dt) {
> - kfree(buffer);
> - return -ENOMEM;
> - }
> -
> + /* report the devices of the data transfer elements */
>   for (elem = 0; elem < ch->counts[CHET_DT]; elem++) {
> + struct scsi_device *sdev;
> +
>   id  = -1;
>   lun = 0;
>   if (elem < CH_DT_MAX  &&  -1 != dt_id[elem]) {
> @@ -378,10 +372,8 @@ ch_readconfig(scsi_changer *ch)
>   VPRINTK(KERN_INFO, "dt 0x%x: ",elem+ch-
> >firsts[CHET_DT]);
>   if (data[6] & 0x80) {
>   VPRINTK(KERN_CONT, "not this SCSI
> bus\n");
> - ch->dt[elem] = NULL;
>   } else if (0 == (data[6] & 0x30)) {
>   VPRINTK(KERN_CONT, "ID/LUN
> unknown\n");
> - ch->dt[elem] = NULL;
>   } else {
>   id  = ch->device->id;
>   lun = 0;
> @@ -391,18 +383,16 @@ ch_readconfig(scsi_changer *ch)
>   }
>   if (-1 != id) {
>   VPRINTK(KERN_CONT, "ID %i, LUN %i,
> ",id,lun);
> - ch->dt[elem] =
> - scsi_device_lookup(ch->device->host,
> -    ch->device-
> >channel,
> -    id,lun);
> - if (!ch->dt[elem]) {
> + sdev = scsi_device_lookup(ch->device->host,
> +   ch->device-
> >channel,
> +   id, lun);
> + if (!sdev) {
>   /* should not happen */
>   VPRINTK(KERN_CONT, "Huh? device not
> found!\n");
>   } else {
>   VPRINTK(KERN_CONT, "name: %8.8s
> %16.16s %4.4s\n",
> - ch->dt[elem]->vendor,
> - ch->dt[elem]->model,
> - ch->dt[elem]->rev);
> + sdev->vendor, sdev->model,
> sdev->rev);
> + scsi_device_put(sdev);
>   }
>   }
>   }
> @@ -566,7 +556,6 @@ static void ch_destroy(struct kref *ref)
>   scsi_changer *ch = container_of(ref, scsi_changer, ref);
>  
>   ch->device = NULL;
> - kfree(ch->dt);
>   kfree(ch);
>  }
>  
>
> ---
> base-commit: c3cff7fac01638ab58e85fe7df41a04fa25c5bae
> change-id: 20260922-ch-dt-leak-c2181b6dfda8
>
> Best regards,
> -- 
> Ahmed Abdelhaleem Ahmed <ahmedhal@xxxxxxxxx>
>
>
Looks good, not seeing any issues here.
Reviewed-by: Laurence Oberman <loberman@xxxxxxxxxx>