]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/infiniband/hw/nes/nes_cm.c
RDMA/nes: Fix crash in nes_accept()
[karo-tx-linux.git] / drivers / infiniband / hw / nes / nes_cm.c
index 73473db1986361f2a390a30b09c942f74e62ca96..ec04786b6069c7eb1f4b68b17a6a8c500aa4b3be 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006 - 2009 Intel-NE, Inc.  All rights reserved.
+ * Copyright (c) 2006 - 2009 Intel Corporation.  All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -251,6 +251,33 @@ static int parse_mpa(struct nes_cm_node *cm_node, u8 *buffer, u32 *type,
 
        mpa_frame = (struct ietf_mpa_frame *)buffer;
        cm_node->mpa_frame_size = ntohs(mpa_frame->priv_data_len);
+       /* make sure mpa private data len is less than 512 bytes */
+       if (cm_node->mpa_frame_size > IETF_MAX_PRIV_DATA_LEN) {
+               nes_debug(NES_DBG_CM, "The received Length of Private"
+                       " Data field exceeds 512 octets\n");
+               return -EINVAL;
+       }
+       /*
+        * make sure MPA receiver interoperate with the
+        * received MPA version and MPA key information
+        *
+        */
+       if (mpa_frame->rev != mpa_version) {
+               nes_debug(NES_DBG_CM, "The received mpa version"
+                               " can not be interoperated\n");
+               return -EINVAL;
+       }
+       if (cm_node->state != NES_CM_STATE_MPAREQ_SENT) {
+               if (memcmp(mpa_frame->key, IEFT_MPA_KEY_REQ, IETF_MPA_KEY_SIZE)) {
+                       nes_debug(NES_DBG_CM, "Unexpected MPA Key received \n");
+                       return -EINVAL;
+               }
+       } else {
+               if (memcmp(mpa_frame->key, IEFT_MPA_KEY_REP, IETF_MPA_KEY_SIZE)) {
+                       nes_debug(NES_DBG_CM, "Unexpected MPA Key received \n");
+                       return -EINVAL;
+               }
+       }
 
        if (cm_node->mpa_frame_size + sizeof(struct ietf_mpa_frame) != len) {
                nes_debug(NES_DBG_CM, "The received ietf buffer was not right"
@@ -486,6 +513,8 @@ static void nes_retrans_expired(struct nes_cm_node *cm_node)
                send_reset(cm_node, NULL);
                break;
        default:
+               add_ref_cm_node(cm_node);
+               send_reset(cm_node, NULL);
                create_event(cm_node, NES_CM_EVENT_ABORTED);
        }
 }
@@ -949,6 +978,7 @@ static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
                                reset_entry);
                {
                        struct nes_cm_node *loopback = cm_node->loopbackpartner;
+                       enum nes_cm_node_state old_state;
                        if (NES_CM_STATE_FIN_WAIT1 <= cm_node->state) {
                                rem_ref_cm_node(cm_node->cm_core, cm_node);
                        } else {
@@ -960,11 +990,12 @@ static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
                                                         NES_CM_STATE_CLOSED;
                                                WARN_ON(1);
                                        } else {
-                                               cm_node->state =
-                                                       NES_CM_STATE_CLOSED;
-                                               rem_ref_cm_node(
-                                                       cm_node->cm_core,
-                                                       cm_node);
+                                               old_state = cm_node->state;
+                                               cm_node->state = NES_CM_STATE_LISTENER_DESTROYED;
+                                               if (old_state != NES_CM_STATE_MPAREQ_RCVD)
+                                                       rem_ref_cm_node(
+                                                               cm_node->cm_core,
+                                                               cm_node);
                                        }
                                } else {
                                        struct nes_cm_event event;
@@ -980,6 +1011,7 @@ static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
                                                         loopback->loc_port;
                                        event.cm_info.cm_id = loopback->cm_id;
                                        cm_event_connect_error(&event);
+                                       cm_node->state = NES_CM_STATE_LISTENER_DESTROYED;
                                        loopback->state = NES_CM_STATE_CLOSED;
 
                                        event.cm_node = cm_node;
@@ -1974,7 +2006,7 @@ static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core,
        if (!cm_node)
                return NULL;
        mpa_frame = &cm_node->mpa_frame;
-       strcpy(mpa_frame->key, IEFT_MPA_KEY_REQ);
+       memcpy(mpa_frame->key, IEFT_MPA_KEY_REQ, IETF_MPA_KEY_SIZE);
        mpa_frame->flags = IETF_MPA_FLAGS_CRC;
        mpa_frame->rev =  IETF_MPA_VERSION;
        mpa_frame->priv_data_len = htons(private_data_len);
@@ -2102,30 +2134,39 @@ static int mini_cm_reject(struct nes_cm_core *cm_core,
                        cm_node->state = NES_CM_STATE_CLOSED;
                        rem_ref_cm_node(cm_core, cm_node);
                } else {
-                       ret = send_mpa_reject(cm_node);
-                       if (ret) {
-                               cm_node->state = NES_CM_STATE_CLOSED;
-                               err = send_reset(cm_node, NULL);
-                               if (err)
-                                       WARN_ON(1);
-                       } else
-                               cm_id->add_ref(cm_id);
+                       if (cm_node->state == NES_CM_STATE_LISTENER_DESTROYED) {
+                               rem_ref_cm_node(cm_core, cm_node);
+                       } else {
+                               ret = send_mpa_reject(cm_node);
+                               if (ret) {
+                                       cm_node->state = NES_CM_STATE_CLOSED;
+                                       err = send_reset(cm_node, NULL);
+                                       if (err)
+                                               WARN_ON(1);
+                               } else
+                                       cm_id->add_ref(cm_id);
+                       }
                }
        } else {
                cm_node->cm_id = NULL;
-               event.cm_node = loopback;
-               event.cm_info.rem_addr = loopback->rem_addr;
-               event.cm_info.loc_addr = loopback->loc_addr;
-               event.cm_info.rem_port = loopback->rem_port;
-               event.cm_info.loc_port = loopback->loc_port;
-               event.cm_info.cm_id = loopback->cm_id;
-               cm_event_mpa_reject(&event);
-               rem_ref_cm_node(cm_core, cm_node);
-               loopback->state = NES_CM_STATE_CLOSING;
+               if (cm_node->state == NES_CM_STATE_LISTENER_DESTROYED) {
+                       rem_ref_cm_node(cm_core, cm_node);
+                       rem_ref_cm_node(cm_core, loopback);
+               } else {
+                       event.cm_node = loopback;
+                       event.cm_info.rem_addr = loopback->rem_addr;
+                       event.cm_info.loc_addr = loopback->loc_addr;
+                       event.cm_info.rem_port = loopback->rem_port;
+                       event.cm_info.loc_port = loopback->loc_port;
+                       event.cm_info.cm_id = loopback->cm_id;
+                       cm_event_mpa_reject(&event);
+                       rem_ref_cm_node(cm_core, cm_node);
+                       loopback->state = NES_CM_STATE_CLOSING;
 
-               cm_id = loopback->cm_id;
-               rem_ref_cm_node(cm_core, loopback);
-               cm_id->rem_ref(cm_id);
+                       cm_id = loopback->cm_id;
+                       rem_ref_cm_node(cm_core, loopback);
+                       cm_id->rem_ref(cm_id);
+               }
        }
 
        return ret;
@@ -2169,6 +2210,7 @@ static int mini_cm_close(struct nes_cm_core *cm_core, struct nes_cm_node *cm_nod
        case NES_CM_STATE_UNKNOWN:
        case NES_CM_STATE_INITED:
        case NES_CM_STATE_CLOSED:
+       case NES_CM_STATE_LISTENER_DESTROYED:
                ret = rem_ref_cm_node(cm_core, cm_node);
                break;
        case NES_CM_STATE_TSA:
@@ -2687,8 +2729,6 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
        struct nes_pd *nespd;
        u64 tagged_offset;
 
-
-
        ibqp = nes_get_qp(cm_id->device, conn_param->qpn);
        if (!ibqp)
                return -EINVAL;
@@ -2704,6 +2744,13 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
                "%s\n", cm_node, nesvnic, nesvnic->netdev,
                nesvnic->netdev->name);
 
+       if (NES_CM_STATE_LISTENER_DESTROYED == cm_node->state) {
+               if (cm_node->loopbackpartner)
+                       rem_ref_cm_node(cm_node->cm_core, cm_node->loopbackpartner);
+               rem_ref_cm_node(cm_node->cm_core, cm_node);
+               return -EINVAL;
+       }
+
        /* associate the node with the QP */
        nesqp->cm_node = (void *)cm_node;
        cm_node->nesqp = nesqp;
@@ -2929,7 +2976,7 @@ int nes_reject(struct iw_cm_id *cm_id, const void *pdata, u8 pdata_len)
        if (cm_node->mpa_frame_size > MAX_CM_BUFFER)
                return -EINVAL;
 
-       strcpy(&cm_node->mpa_frame.key[0], IEFT_MPA_KEY_REP);
+       memcpy(&cm_node->mpa_frame.key[0], IEFT_MPA_KEY_REP, IETF_MPA_KEY_SIZE);
        if (loopback) {
                memcpy(&loopback->mpa_frame.priv_data, pdata, pdata_len);
                loopback->mpa_frame.priv_data_len = pdata_len;
@@ -2974,6 +3021,9 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
        if (!nesdev)
                return -EINVAL;
 
+       if (!(cm_id->local_addr.sin_port) || !(cm_id->remote_addr.sin_port))
+               return -EINVAL;
+
        nes_debug(NES_DBG_CM, "QP%u, current IP = 0x%08X, Destination IP = "
                "0x%08X:0x%04X, local = 0x%08X:0x%04X.\n", nesqp->hwqp.qp_id,
                ntohl(nesvnic->local_ipaddr),
@@ -3346,7 +3396,7 @@ static void cm_event_connect_error(struct nes_cm_event *event)
        nesqp->cm_id = NULL;
        cm_id->provider_data = NULL;
        cm_event.event = IW_CM_EVENT_CONNECT_REPLY;
-       cm_event.status = IW_CM_EVENT_STATUS_REJECTED;
+       cm_event.status = -ECONNRESET;
        cm_event.provider_data = cm_id->provider_data;
        cm_event.local_addr = cm_id->local_addr;
        cm_event.remote_addr = cm_id->remote_addr;