From 1304be5fe0efb42b7ec6a50dd8e1a9bce2adae17 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Thu, 26 Jan 2012 21:13:10 -0600 Subject: [PATCH] [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 Signed-off-by: James Bottomley --- drivers/scsi/cxgbi/libcxgbi.c | 13 ++----------- drivers/scsi/iscsi_tcp.c | 13 +------------ drivers/scsi/libiscsi.c | 2 +- drivers/scsi/libiscsi_tcp.c | 18 ++++++++++++++++++ include/scsi/libiscsi.h | 2 +- include/scsi/libiscsi_tcp.h | 2 +- 6 files changed, 24 insertions(+), 26 deletions(-) diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c index d3ff9cd40234..e6e6aa9289b8 100644 --- a/drivers/scsi/cxgbi/libcxgbi.c +++ b/drivers/scsi/cxgbi/libcxgbi.c @@ -2148,11 +2148,10 @@ int cxgbi_set_conn_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param, char *buf, int buflen) { struct iscsi_conn *conn = cls_conn->dd_data; - struct iscsi_session *session = conn->session; struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct cxgbi_conn *cconn = tcp_conn->dd_data; struct cxgbi_sock *csk = cconn->cep->csk; - int value, err = 0; + int err; log_debug(1 << CXGBI_DBG_ISCSI, "cls_conn 0x%p, param %d, buf(%d) %s.\n", @@ -2174,15 +2173,7 @@ int cxgbi_set_conn_param(struct iscsi_cls_conn *cls_conn, conn->datadgst_en, 0); break; case ISCSI_PARAM_MAX_R2T: - sscanf(buf, "%d", &value); - if (value <= 0 || !is_power_of_2(value)) - return -EINVAL; - if (session->max_r2t == value) - break; - iscsi_tcp_r2tpool_free(session); - err = iscsi_set_param(cls_conn, param, buf, buflen); - if (!err && iscsi_tcp_r2tpool_alloc(session)) - return -ENOMEM; + return iscsi_tcp_set_max_r2t(conn, buf); case ISCSI_PARAM_MAX_RECV_DLENGTH: err = iscsi_set_param(cls_conn, param, buf, buflen); if (!err) diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index db47158e0dde..453a740fa68e 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -684,10 +684,8 @@ static int iscsi_sw_tcp_conn_set_param(struct iscsi_cls_conn *cls_conn, int buflen) { struct iscsi_conn *conn = cls_conn->dd_data; - struct iscsi_session *session = conn->session; struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; - int value; switch(param) { case ISCSI_PARAM_HDRDGST_EN: @@ -699,16 +697,7 @@ static int iscsi_sw_tcp_conn_set_param(struct iscsi_cls_conn *cls_conn, sock_no_sendpage : tcp_sw_conn->sock->ops->sendpage; break; case ISCSI_PARAM_MAX_R2T: - sscanf(buf, "%d", &value); - if (value <= 0 || !is_power_of_2(value)) - return -EINVAL; - if (session->max_r2t == value) - break; - iscsi_tcp_r2tpool_free(session); - iscsi_set_param(cls_conn, param, buf, buflen); - if (iscsi_tcp_r2tpool_alloc(session)) - return -ENOMEM; - break; + return iscsi_tcp_set_max_r2t(conn, buf); default: return iscsi_set_param(cls_conn, param, buf, buflen); } diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 00592e3bb375..8582d7c25732 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -3201,7 +3201,7 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn, sscanf(buf, "%d", &session->initial_r2t_en); break; case ISCSI_PARAM_MAX_R2T: - sscanf(buf, "%d", &session->max_r2t); + sscanf(buf, "%hu", &session->max_r2t); break; case ISCSI_PARAM_IMM_DATA_EN: sscanf(buf, "%d", &session->imm_data_en); diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c index 5715a3d0a3d3..c4996b081999 100644 --- a/drivers/scsi/libiscsi_tcp.c +++ b/drivers/scsi/libiscsi_tcp.c @@ -1170,6 +1170,24 @@ void iscsi_tcp_r2tpool_free(struct iscsi_session *session) } EXPORT_SYMBOL_GPL(iscsi_tcp_r2tpool_free); +int iscsi_tcp_set_max_r2t(struct iscsi_conn *conn, char *buf) +{ + struct iscsi_session *session = conn->session; + unsigned short r2ts = 0; + + sscanf(buf, "%hu", &r2ts); + if (session->max_r2t == r2ts) + return 0; + + if (!r2ts || !is_power_of_2(r2ts)) + return -EINVAL; + + session->max_r2t = r2ts; + iscsi_tcp_r2tpool_free(session); + return iscsi_tcp_r2tpool_alloc(session); +} +EXPORT_SYMBOL_GPL(iscsi_tcp_set_max_r2t); + void iscsi_tcp_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats) { diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h index 2e42e9a0e0b6..6e33386a3898 100644 --- a/include/scsi/libiscsi.h +++ b/include/scsi/libiscsi.h @@ -268,7 +268,7 @@ struct iscsi_session { int lu_reset_timeout; int tgt_reset_timeout; int initial_r2t_en; - unsigned max_r2t; + unsigned short max_r2t; int imm_data_en; unsigned first_burst; unsigned max_burst; diff --git a/include/scsi/libiscsi_tcp.h b/include/scsi/libiscsi_tcp.h index ac0cc1d925ef..215469a9b801 100644 --- a/include/scsi/libiscsi_tcp.h +++ b/include/scsi/libiscsi_tcp.h @@ -128,7 +128,7 @@ extern void iscsi_tcp_conn_teardown(struct iscsi_cls_conn *cls_conn); /* misc helpers */ extern int iscsi_tcp_r2tpool_alloc(struct iscsi_session *session); extern void iscsi_tcp_r2tpool_free(struct iscsi_session *session); - +extern int iscsi_tcp_set_max_r2t(struct iscsi_conn *conn, char *buf); extern void iscsi_tcp_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats); #endif /* LIBISCSI_TCP_H */ -- 2.20.1