[bug report] scsi: lpfc: Prevent NDLP reference count underflow in dev_loss_tmo callback

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello Justin Tee,

Commit 4281f44ea8bf ("scsi: lpfc: Prevent NDLP reference count
underflow in dev_loss_tmo callback") from Oct 31, 2024 (linux-next),
leads to the following Smatch static checker warning:

drivers/scsi/lpfc/lpfc_hbadisc.c:220 lpfc_dev_loss_tmo_callbk() error: dereferencing freed memory 'ndlp' (line 211)

// presumably these are fine
drivers/scsi/lpfc/lpfc_els.c:1021 lpfc_cmpl_els_flogi() error: dereferencing freed memory 'ndlp' (line 1019)
drivers/scsi/lpfc/lpfc_els.c:1021 lpfc_cmpl_els_flogi() error: dereferencing freed memory 'ndlp' (line 1019)
drivers/scsi/lpfc/lpfc_els.c:4993 lpfc_els_retry() error: we previously assumed 'ndlp' could be null (see line 4970)
drivers/scsi/lpfc/lpfc_els.c:5278 lpfc_mbx_cmpl_dflt_rpi() error: dereferencing freed memory 'ndlp' (line 5277)
drivers/scsi/lpfc/lpfc_els.c:8634 lpfc_els_rsp_rls_acc() error: dereferencing freed memory 'ndlp' (line 8602)
drivers/scsi/lpfc/lpfc_nvme.c:2654 lpfc_nvme_unregister_port() error: dereferencing freed memory 'ndlp' (line 2642)
drivers/scsi/lpfc/lpfc_nportdisc.c:1875 lpfc_rcv_logo_reglogin_issue() error: dereferencing freed memory 'ndlp' (line 1867)

// this one has a comment saying it's fine
drivers/scsi/lpfc/lpfc_hbadisc.c:4335 lpfc_mbx_cmpl_ns_reg_login() error: dereferencing freed memory 'ndlp' (line 4327)

drivers/scsi/lpfc/lpfc_hbadisc.c
    156 void
    157 lpfc_dev_loss_tmo_callbk(struct fc_rport *rport)
    158 {
    159         struct lpfc_nodelist *ndlp;
    160         struct lpfc_vport *vport;
    161         struct lpfc_hba   *phba;
    162         struct lpfc_work_evt *evtp;
    163         unsigned long iflags;
    164         bool nvme_reg = false;
    165 
    166         ndlp = ((struct lpfc_rport_data *)rport->dd_data)->pnode;
    167         if (!ndlp)
    168                 return;
    169 
    170         vport = ndlp->vport;
    171         phba  = vport->phba;
    172 
    173         lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT,
    174                 "rport devlosscb: sid:x%x did:x%x flg:x%lx",
    175                 ndlp->nlp_sid, ndlp->nlp_DID, ndlp->nlp_flag);
    176 
    177         lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NODE,
    178                          "3181 dev_loss_callbk x%06x, rport x%px flg x%lx "
    179                          "load_flag x%lx refcnt %u state %d xpt x%x\n",
    180                          ndlp->nlp_DID, ndlp->rport, ndlp->nlp_flag,
    181                          vport->load_flag, kref_read(&ndlp->kref),
    182                          ndlp->nlp_state, ndlp->fc4_xpt_flags);
    183 
    184         /* Don't schedule a worker thread event if the vport is going down. */
    185         if (test_bit(FC_UNLOADING, &vport->load_flag) ||
    186             !test_bit(HBA_SETUP, &phba->hba_flag)) {
    187 
    188                 spin_lock_irqsave(&ndlp->lock, iflags);
    189                 ndlp->rport = NULL;
    190 
    191                 if (ndlp->fc4_xpt_flags & NVME_XPT_REGD)
    192                         nvme_reg = true;
    193 
    194                 /* The scsi_transport is done with the rport so lpfc cannot
    195                  * call to unregister.
    196                  */
    197                 if (ndlp->fc4_xpt_flags & SCSI_XPT_REGD) {
    198                         ndlp->fc4_xpt_flags &= ~SCSI_XPT_REGD;
    199 
    200                         /* If NLP_XPT_REGD was cleared in lpfc_nlp_unreg_node,
    201                          * unregister calls were made to the scsi and nvme
    202                          * transports and refcnt was already decremented. Clear
    203                          * the NLP_XPT_REGD flag only if the NVME Rport is
    204                          * confirmed unregistered.
    205                          */
    206                         if (!nvme_reg && ndlp->fc4_xpt_flags & NLP_XPT_REGD) {
    207                                 ndlp->fc4_xpt_flags &= ~NLP_XPT_REGD;
    208                                 spin_unlock_irqrestore(&ndlp->lock, iflags);
    209                                 lpfc_nlp_put(ndlp); /* may free ndlp */

If nvme_reg is false then sometimes we free ndlp.  I would normally
ignore this warning because obviously the code is doing something
subtle, but there is a comment which says that it does free ndlp.

    210                         } else {
    211                                 spin_unlock_irqrestore(&ndlp->lock, iflags);
    212                         }
    213                 } else {
    214                         spin_unlock_irqrestore(&ndlp->lock, iflags);
    215                 }
    216 
    217                 /* Only 1 thread can drop the initial node reference.  If
    218                  * another thread has set NLP_DROPPED, this thread is done.
    219                  */
--> 220                 if (nvme_reg || test_bit(NLP_DROPPED, &ndlp->nlp_flag))
                                                               ^^^^
Here we know nvme_reg is false but we're still dereferencing "ndlp".

    221                         return;
    222 
    223                 set_bit(NLP_DROPPED, &ndlp->nlp_flag);
                                              ^^^^
Here nvme_reg is false again but this is even more dereferencing.

    224                 lpfc_nlp_put(ndlp);
                                     ^^^^
And here.

    225                 return;
    226         }
    227 

regards,
dan carpenter




[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]

  Powered by Linux