Skip to content

Commit

Permalink
RDMA/nes: Don't allow userspace QPs to use STag zero
Browse files Browse the repository at this point in the history
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.

Cc: <stable@kernel.org>
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>
  • Loading branch information
fsl-latif authored and torvalds committed Mar 12, 2009
1 parent 7ef0d73 commit c12e56e
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 5 deletions.
39 changes: 34 additions & 5 deletions drivers/infiniband/hw/nes/nes_cm.c
Expand Up @@ -2490,12 +2490,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));
Expand All @@ -2507,6 +2509,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);
Expand Down Expand Up @@ -2543,6 +2547,12 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
u32 crc_value;
int ret;
int passive_state;
struct nes_ib_device *nesibdev;
struct ib_mr *ibmr = NULL;
struct ib_phys_buf ibphysbuf;
struct nes_pd *nespd;



ibqp = nes_get_qp(cm_id->device, conn_param->qpn);
if (!ibqp)
Expand Down Expand Up @@ -2601,6 +2611,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,
Expand All @@ -2611,14 +2641,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 |
Expand Down
2 changes: 2 additions & 0 deletions drivers/infiniband/hw/nes/nes_verbs.c
Expand Up @@ -1360,8 +1360,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;
Expand Down
1 change: 1 addition & 0 deletions drivers/infiniband/hw/nes/nes_verbs.h
Expand Up @@ -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;
Expand Down

0 comments on commit c12e56e

Please sign in to comment.