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

[SCSI] libiscsi_tcp: fix max_r2t manipulation

Problem description from Xi Wang:
A large max_r2t could lead to integer overflow in subsequent call to
iscsi_tcp_r2tpool_alloc(), allocating a smaller buffer than expected
and leading to out-of-bounds write.

Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>

authored by

Mike Christie and committed by
James Bottomley
1304be5f df1c7bab

+24 -26
+2 -11
drivers/scsi/cxgbi/libcxgbi.c
··· 2148 2148 enum iscsi_param param, char *buf, int buflen) 2149 2149 { 2150 2150 struct iscsi_conn *conn = cls_conn->dd_data; 2151 - struct iscsi_session *session = conn->session; 2152 2151 struct iscsi_tcp_conn *tcp_conn = conn->dd_data; 2153 2152 struct cxgbi_conn *cconn = tcp_conn->dd_data; 2154 2153 struct cxgbi_sock *csk = cconn->cep->csk; 2155 - int value, err = 0; 2154 + int err; 2156 2155 2157 2156 log_debug(1 << CXGBI_DBG_ISCSI, 2158 2157 "cls_conn 0x%p, param %d, buf(%d) %s.\n", ··· 2173 2174 conn->datadgst_en, 0); 2174 2175 break; 2175 2176 case ISCSI_PARAM_MAX_R2T: 2176 - sscanf(buf, "%d", &value); 2177 - if (value <= 0 || !is_power_of_2(value)) 2178 - return -EINVAL; 2179 - if (session->max_r2t == value) 2180 - break; 2181 - iscsi_tcp_r2tpool_free(session); 2182 - err = iscsi_set_param(cls_conn, param, buf, buflen); 2183 - if (!err && iscsi_tcp_r2tpool_alloc(session)) 2184 - return -ENOMEM; 2177 + return iscsi_tcp_set_max_r2t(conn, buf); 2185 2178 case ISCSI_PARAM_MAX_RECV_DLENGTH: 2186 2179 err = iscsi_set_param(cls_conn, param, buf, buflen); 2187 2180 if (!err)
+1 -12
drivers/scsi/iscsi_tcp.c
··· 684 684 int buflen) 685 685 { 686 686 struct iscsi_conn *conn = cls_conn->dd_data; 687 - struct iscsi_session *session = conn->session; 688 687 struct iscsi_tcp_conn *tcp_conn = conn->dd_data; 689 688 struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; 690 - int value; 691 689 692 690 switch(param) { 693 691 case ISCSI_PARAM_HDRDGST_EN: ··· 697 699 sock_no_sendpage : tcp_sw_conn->sock->ops->sendpage; 698 700 break; 699 701 case ISCSI_PARAM_MAX_R2T: 700 - sscanf(buf, "%d", &value); 701 - if (value <= 0 || !is_power_of_2(value)) 702 - return -EINVAL; 703 - if (session->max_r2t == value) 704 - break; 705 - iscsi_tcp_r2tpool_free(session); 706 - iscsi_set_param(cls_conn, param, buf, buflen); 707 - if (iscsi_tcp_r2tpool_alloc(session)) 708 - return -ENOMEM; 709 - break; 702 + return iscsi_tcp_set_max_r2t(conn, buf); 710 703 default: 711 704 return iscsi_set_param(cls_conn, param, buf, buflen); 712 705 }
+1 -1
drivers/scsi/libiscsi.c
··· 3201 3201 sscanf(buf, "%d", &session->initial_r2t_en); 3202 3202 break; 3203 3203 case ISCSI_PARAM_MAX_R2T: 3204 - sscanf(buf, "%d", &session->max_r2t); 3204 + sscanf(buf, "%hu", &session->max_r2t); 3205 3205 break; 3206 3206 case ISCSI_PARAM_IMM_DATA_EN: 3207 3207 sscanf(buf, "%d", &session->imm_data_en);
+18
drivers/scsi/libiscsi_tcp.c
··· 1170 1170 } 1171 1171 EXPORT_SYMBOL_GPL(iscsi_tcp_r2tpool_free); 1172 1172 1173 + int iscsi_tcp_set_max_r2t(struct iscsi_conn *conn, char *buf) 1174 + { 1175 + struct iscsi_session *session = conn->session; 1176 + unsigned short r2ts = 0; 1177 + 1178 + sscanf(buf, "%hu", &r2ts); 1179 + if (session->max_r2t == r2ts) 1180 + return 0; 1181 + 1182 + if (!r2ts || !is_power_of_2(r2ts)) 1183 + return -EINVAL; 1184 + 1185 + session->max_r2t = r2ts; 1186 + iscsi_tcp_r2tpool_free(session); 1187 + return iscsi_tcp_r2tpool_alloc(session); 1188 + } 1189 + EXPORT_SYMBOL_GPL(iscsi_tcp_set_max_r2t); 1190 + 1173 1191 void iscsi_tcp_conn_get_stats(struct iscsi_cls_conn *cls_conn, 1174 1192 struct iscsi_stats *stats) 1175 1193 {
+1 -1
include/scsi/libiscsi.h
··· 268 268 int lu_reset_timeout; 269 269 int tgt_reset_timeout; 270 270 int initial_r2t_en; 271 - unsigned max_r2t; 271 + unsigned short max_r2t; 272 272 int imm_data_en; 273 273 unsigned first_burst; 274 274 unsigned max_burst;
+1 -1
include/scsi/libiscsi_tcp.h
··· 128 128 /* misc helpers */ 129 129 extern int iscsi_tcp_r2tpool_alloc(struct iscsi_session *session); 130 130 extern void iscsi_tcp_r2tpool_free(struct iscsi_session *session); 131 - 131 + extern int iscsi_tcp_set_max_r2t(struct iscsi_conn *conn, char *buf); 132 132 extern void iscsi_tcp_conn_get_stats(struct iscsi_cls_conn *cls_conn, 133 133 struct iscsi_stats *stats); 134 134 #endif /* LIBISCSI_TCP_H */