]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
RDMA/nes: Don't allow userspace QPs to use STag zero
authorFaisal Latif <faisal.latif@intel.com>
Thu, 12 Mar 2009 21:34:59 +0000 (14:34 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 17 Mar 2009 00:52:57 +0000 (17:52 -0700)
commit c12e56ef6951f4fce1afe9ef6aab9243ea9a9b04 upstream.

STag zero is a special STag that allows consumers to access any bus
address without registering memory.  The nes driver unfortunately
allows STag zero to be used even with QPs created by unprivileged
userspace consumers, which means that any process with direct verbs
access to the nes device can read and write any memory accessible to
the underlying PCI device (usually any memory in the system).  Such
access is usually given for cluster software such as MPI to use, so
this is a local privilege escalation bug on most systems running this
driver.

The driver was using STag zero to receive the last streaming mode
data; to allow STag zero to be disabled for unprivileged QPs, the
driver now registers a special MR for this data.

Signed-off-by: Faisal Latif <faisal.latif@intel.com>
Signed-off-by: Roland Dreier <rolandd@cisco.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/infiniband/hw/nes/nes_cm.c
drivers/infiniband/hw/nes/nes_verbs.c
drivers/infiniband/hw/nes/nes_verbs.h

index 499d3cf83e1f28e2ee263ed3e92e2fdb18d1f59b..6f732ffa5b0bb5212dfc6548fe6200288dd30444 100644 (file)
@@ -2474,12 +2474,14 @@ static int nes_disconnect(struct nes_qp *nesqp, int abrupt)
        int ret = 0;
        struct nes_vnic *nesvnic;
        struct nes_device *nesdev;
+       struct nes_ib_device *nesibdev;
 
        nesvnic = to_nesvnic(nesqp->ibqp.device);
        if (!nesvnic)
                return -EINVAL;
 
        nesdev = nesvnic->nesdev;
+       nesibdev = nesvnic->nesibdev;
 
        nes_debug(NES_DBG_CM, "netdev refcnt = %u.\n",
                        atomic_read(&nesvnic->netdev->refcnt));
@@ -2491,6 +2493,8 @@ static int nes_disconnect(struct nes_qp *nesqp, int abrupt)
        } else {
                /* Need to free the Last Streaming Mode Message */
                if (nesqp->ietf_frame) {
+                       if (nesqp->lsmm_mr)
+                               nesibdev->ibdev.dereg_mr(nesqp->lsmm_mr);
                        pci_free_consistent(nesdev->pcidev,
                                        nesqp->private_data_len+sizeof(struct ietf_mpa_frame),
                                        nesqp->ietf_frame, nesqp->ietf_frame_pbase);
@@ -2524,6 +2528,10 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
        struct iw_cm_event cm_event;
        struct nes_hw_qp_wqe *wqe;
        struct nes_v4_quad nes_quad;
+       struct nes_ib_device *nesibdev;
+       struct ib_mr *ibmr = NULL;
+       struct ib_phys_buf ibphysbuf;
+       struct nes_pd *nespd;
        u32 crc_value;
        int ret;
 
@@ -2584,6 +2592,26 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
        if (cm_id->remote_addr.sin_addr.s_addr !=
                        cm_id->local_addr.sin_addr.s_addr) {
                u64temp = (unsigned long)nesqp;
+               nesibdev = nesvnic->nesibdev;
+               nespd = nesqp->nespd;
+               ibphysbuf.addr = nesqp->ietf_frame_pbase;
+               ibphysbuf.size = conn_param->private_data_len +
+                                       sizeof(struct ietf_mpa_frame);
+               ibmr = nesibdev->ibdev.reg_phys_mr((struct ib_pd *)nespd,
+                                               &ibphysbuf, 1,
+                                               IB_ACCESS_LOCAL_WRITE,
+                                               (u64 *)&nesqp->ietf_frame);
+               if (!ibmr) {
+                       nes_debug(NES_DBG_CM, "Unable to register memory region"
+                                       "for lSMM for cm_node = %p \n",
+                                       cm_node);
+                       return -ENOMEM;
+               }
+
+               ibmr->pd = &nespd->ibpd;
+               ibmr->device = nespd->ibpd.device;
+               nesqp->lsmm_mr = ibmr;
+
                u64temp |= NES_SW_CONTEXT_ALIGN>>1;
                set_wqe_64bit_value(wqe->wqe_words,
                        NES_IWARP_SQ_WQE_COMP_CTX_LOW_IDX,
@@ -2594,14 +2622,13 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
                wqe->wqe_words[NES_IWARP_SQ_WQE_TOTAL_PAYLOAD_IDX] =
                        cpu_to_le32(conn_param->private_data_len +
                        sizeof(struct ietf_mpa_frame));
-               wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_LOW_IDX] =
-                       cpu_to_le32((u32)nesqp->ietf_frame_pbase);
-               wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_HIGH_IDX] =
-                       cpu_to_le32((u32)((u64)nesqp->ietf_frame_pbase >> 32));
+               set_wqe_64bit_value(wqe->wqe_words,
+                                       NES_IWARP_SQ_WQE_FRAG0_LOW_IDX,
+                                       (u64)nesqp->ietf_frame);
                wqe->wqe_words[NES_IWARP_SQ_WQE_LENGTH0_IDX] =
                        cpu_to_le32(conn_param->private_data_len +
                        sizeof(struct ietf_mpa_frame));
-               wqe->wqe_words[NES_IWARP_SQ_WQE_STAG0_IDX] = 0;
+               wqe->wqe_words[NES_IWARP_SQ_WQE_STAG0_IDX] = ibmr->lkey;
 
                nesqp->nesqp_context->ird_ord_sizes |=
                        cpu_to_le32(NES_QPCONTEXT_ORDIRD_LSMM_PRESENT |
index d79942e849799341e8f676f2436dacd6be2bd793..7c794516958078da20118bfe86c2541617b14160 100644 (file)
@@ -1338,8 +1338,10 @@ static struct ib_qp *nes_create_qp(struct ib_pd *ibpd,
                                        NES_QPCONTEXT_MISC_RQ_SIZE_SHIFT);
                        nesqp->nesqp_context->misc |= cpu_to_le32((u32)nesqp->hwqp.sq_encoded_size <<
                                        NES_QPCONTEXT_MISC_SQ_SIZE_SHIFT);
+                       if (!udata) {
                                nesqp->nesqp_context->misc |= cpu_to_le32(NES_QPCONTEXT_MISC_PRIV_EN);
                                nesqp->nesqp_context->misc |= cpu_to_le32(NES_QPCONTEXT_MISC_FAST_REGISTER_EN);
+                       }
                        nesqp->nesqp_context->cqs = cpu_to_le32(nesqp->nesscq->hw_cq.cq_number +
                                        ((u32)nesqp->nesrcq->hw_cq.cq_number << 16));
                        u64temp = (u64)nesqp->hwqp.sq_pbase;
index 6c6b4da5184f9aee7bdb4600a69d887162cbf873..ae0ca9bc83bd9aae7a8fd83dd4c9165939af051e 100644 (file)
@@ -134,6 +134,7 @@ struct nes_qp {
        struct ietf_mpa_frame *ietf_frame;
        dma_addr_t            ietf_frame_pbase;
        wait_queue_head_t     state_waitq;
+       struct ib_mr          *lsmm_mr;
        unsigned long         socket;
        struct nes_hw_qp      hwqp;
        struct work_struct    work;