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

[SCSI] libfc: fix memory corruption caused by double frees and bad error handling

I was running into several different panics under stress, which I traced down
to a few different possible slab corruption issues in error handling paths.
I have not yet looked into why these exchange sends fail, but with these
fixes my test system is much more stable under stress than before.

fc_elsct_send() could fail and either leave the passed in frame intact
(failure in fc_ct/els_fill) or the frame could have been freed if the
failure was is fc_exch_seq_send(). The caller had no way of knowing, and
there was a potential double free in the error handling in fc_fcp_rec().

Make fc_elsct_send() always free the frame before returning, and remove the
fc_frame_free() call in fc_fcp_rec().

While fc_exch_seq_send() did always consume the frame, there were double free
bugs in the error handling of fc_fcp_cmd_send() and fc_fcp_srr() as well.

Numerous calls to error handling routines (fc_disc_error(),
fc_lport_error(), fc_rport_error_retry() ) were passing in a frame pointer that
had already been freed in the case of an error. I have changed the call
sites to pass in a NULL pointer, but there may be more appropriate error
codes to use.

Question: Why do these error routines take a frame pointer anyway? I
understand passing in a pointer encoded error to the response handlers, but
the error routines take no action on a valid pointer and should never be
called that way.

Signed-off-by: Chris Leech <christopher.leech@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>

authored by

Chris Leech and committed by
James Bottomley
8f550f93 b7a727f1

+15 -16
+1 -1
drivers/scsi/libfc/fc_disc.c
··· 371 371 disc, lport->e_d_tov)) 372 372 return; 373 373 err: 374 - fc_disc_error(disc, fp); 374 + fc_disc_error(disc, NULL); 375 375 } 376 376 377 377 /**
+3 -1
drivers/scsi/libfc/fc_elsct.c
··· 53 53 did = FC_FID_DIR_SERV; 54 54 } 55 55 56 - if (rc) 56 + if (rc) { 57 + fc_frame_free(fp); 57 58 return NULL; 59 + } 58 60 59 61 fc_fill_fc_hdr(fp, r_ctl, did, fc_host_port_id(lport->host), fh_type, 60 62 FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+2 -5
drivers/scsi/libfc/fc_fcp.c
··· 1051 1051 1052 1052 seq = lp->tt.exch_seq_send(lp, fp, resp, fc_fcp_pkt_destroy, fsp, 0); 1053 1053 if (!seq) { 1054 - fc_frame_free(fp); 1055 1054 rc = -1; 1056 1055 goto unlock; 1057 1056 } ··· 1315 1316 fc_fcp_pkt_hold(fsp); /* hold while REC outstanding */ 1316 1317 return; 1317 1318 } 1318 - fc_frame_free(fp); 1319 1319 retry: 1320 1320 if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY) 1321 1321 fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV); ··· 1562 1564 1563 1565 seq = lp->tt.exch_seq_send(lp, fp, fc_fcp_srr_resp, NULL, 1564 1566 fsp, jiffies_to_msecs(FC_SCSI_REC_TOV)); 1565 - if (!seq) { 1566 - fc_frame_free(fp); 1567 + if (!seq) 1567 1568 goto retry; 1568 - } 1569 + 1569 1570 fsp->recov_seq = seq; 1570 1571 fsp->xfer_len = offset; 1571 1572 fsp->xfer_contig_end = offset;
+4 -4
drivers/scsi/libfc/fc_lport.c
··· 1115 1115 1116 1116 if (!lport->tt.elsct_send(lport, FC_FID_FCTRL, fp, ELS_SCR, 1117 1117 fc_lport_scr_resp, lport, lport->e_d_tov)) 1118 - fc_lport_error(lport, fp); 1118 + fc_lport_error(lport, NULL); 1119 1119 } 1120 1120 1121 1121 /** ··· 1186 1186 if (!lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, FC_NS_RPN_ID, 1187 1187 fc_lport_rpn_id_resp, 1188 1188 lport, lport->e_d_tov)) 1189 - fc_lport_error(lport, fp); 1189 + fc_lport_error(lport, NULL); 1190 1190 } 1191 1191 1192 1192 static struct fc_rport_operations fc_lport_rport_ops = { ··· 1340 1340 1341 1341 if (!lport->tt.elsct_send(lport, FC_FID_FLOGI, fp, ELS_LOGO, 1342 1342 fc_lport_logo_resp, lport, lport->e_d_tov)) 1343 - fc_lport_error(lport, fp); 1343 + fc_lport_error(lport, NULL); 1344 1344 } 1345 1345 1346 1346 /** ··· 1456 1456 1457 1457 if (!lport->tt.elsct_send(lport, FC_FID_FLOGI, fp, ELS_FLOGI, 1458 1458 fc_lport_flogi_resp, lport, lport->e_d_tov)) 1459 - fc_lport_error(lport, fp); 1459 + fc_lport_error(lport, NULL); 1460 1460 } 1461 1461 1462 1462 /* Configure a fc_lport */
+5 -5
drivers/scsi/libfc/fc_rport.c
··· 632 632 633 633 if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_PLOGI, 634 634 fc_rport_plogi_resp, rdata, lport->e_d_tov)) 635 - fc_rport_error_retry(rdata, fp); 635 + fc_rport_error_retry(rdata, NULL); 636 636 else 637 637 kref_get(&rdata->kref); 638 638 } ··· 793 793 794 794 if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_PRLI, 795 795 fc_rport_prli_resp, rdata, lport->e_d_tov)) 796 - fc_rport_error_retry(rdata, fp); 796 + fc_rport_error_retry(rdata, NULL); 797 797 else 798 798 kref_get(&rdata->kref); 799 799 } ··· 889 889 890 890 if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_RTV, 891 891 fc_rport_rtv_resp, rdata, lport->e_d_tov)) 892 - fc_rport_error_retry(rdata, fp); 892 + fc_rport_error_retry(rdata, NULL); 893 893 else 894 894 kref_get(&rdata->kref); 895 895 } ··· 919 919 920 920 if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_LOGO, 921 921 fc_rport_logo_resp, rdata, lport->e_d_tov)) 922 - fc_rport_error_retry(rdata, fp); 922 + fc_rport_error_retry(rdata, NULL); 923 923 else 924 924 kref_get(&rdata->kref); 925 925 } ··· 1006 1006 } 1007 1007 if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_ADISC, 1008 1008 fc_rport_adisc_resp, rdata, lport->e_d_tov)) 1009 - fc_rport_error_retry(rdata, fp); 1009 + fc_rport_error_retry(rdata, NULL); 1010 1010 else 1011 1011 kref_get(&rdata->kref); 1012 1012 }