[SCSI] libiscsi_tcp: fix max_r2t manipulation
Mike Christie [Fri, 27 Jan 2012 03:13:10 +0000 (21:13 -0600)]
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>

drivers/scsi/cxgbi/libcxgbi.c
drivers/scsi/iscsi_tcp.c
drivers/scsi/libiscsi.c
drivers/scsi/libiscsi_tcp.c
include/scsi/libiscsi.h
include/scsi/libiscsi_tcp.h

index d3ff9cd..e6e6aa9 100644 (file)
@@ -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)
index db47158..453a740 100644 (file)
@@ -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);
        }
index 00592e3..8582d7c 100644 (file)
@@ -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);
index 5715a3d..c4996b0 100644 (file)
@@ -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)
 {
index 2e42e9a..6e33386 100644 (file)
@@ -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;
index ac0cc1d..215469a 100644 (file)
@@ -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 */