RDS: Let rds_message_alloc_sgs() return NULL
Andy Grover [Thu, 28 Oct 2010 15:40:59 +0000 (15:40 +0000)]
Even with the previous fix, we still are reading the iovecs once
to determine SGs needed, and then again later on. Preallocating
space for sg lists as part of rds_message seemed like a good idea
but it might be better to not do this. While working to redo that
code, this patch attempts to protect against userspace rewriting
the rds_iovec array between the first and second accesses.

The consequences of this would be either a too-small or too-large
sg list array. Too large is not an issue. This patch changes all
callers of message_alloc_sgs to handle running out of preallocated
sgs, and fail gracefully.

Signed-off-by: Andy Grover <andy.grover@oracle.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

net/rds/message.c
net/rds/rdma.c
net/rds/send.c

index a84545d..848cff4 100644 (file)
@@ -224,6 +224,9 @@ struct scatterlist *rds_message_alloc_sgs(struct rds_message *rm, int nents)
        WARN_ON(rm->m_used_sgs + nents > rm->m_total_sgs);
        WARN_ON(!nents);
 
+       if (rm->m_used_sgs + nents > rm->m_total_sgs)
+               return NULL;
+
        sg_ret = &sg_first[rm->m_used_sgs];
        sg_init_table(sg_ret, nents);
        rm->m_used_sgs += nents;
@@ -246,6 +249,8 @@ struct rds_message *rds_message_map_pages(unsigned long *page_addrs, unsigned in
        rm->m_inc.i_hdr.h_len = cpu_to_be32(total_len);
        rm->data.op_nents = ceil(total_len, PAGE_SIZE);
        rm->data.op_sg = rds_message_alloc_sgs(rm, num_sgs);
+       if (!rm->data.op_sg)
+               return ERR_PTR(-ENOMEM);
 
        for (i = 0; i < rm->data.op_nents; ++i) {
                sg_set_page(&rm->data.op_sg[i],
index caa4d98..8920f2a 100644 (file)
@@ -607,6 +607,10 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm,
        op->op_recverr = rs->rs_recverr;
        WARN_ON(!nr_pages);
        op->op_sg = rds_message_alloc_sgs(rm, nr_pages);
+       if (!op->op_sg) {
+               ret = -ENOMEM;
+               goto out;
+       }
 
        if (op->op_notify || op->op_recverr) {
                /* We allocate an uninitialized notifier here, because
@@ -807,6 +811,10 @@ int rds_cmsg_atomic(struct rds_sock *rs, struct rds_message *rm,
        rm->atomic.op_active = 1;
        rm->atomic.op_recverr = rs->rs_recverr;
        rm->atomic.op_sg = rds_message_alloc_sgs(rm, 1);
+       if (!rm->atomic.op_sg) {
+               ret = -ENOMEM;
+               goto err;
+       }
 
        /* verify 8 byte-aligned */
        if (args->local_addr & 0x7) {
index 0bc9db1..35b9c2e 100644 (file)
@@ -973,6 +973,10 @@ int rds_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
        /* Attach data to the rm */
        if (payload_len) {
                rm->data.op_sg = rds_message_alloc_sgs(rm, ceil(payload_len, PAGE_SIZE));
+               if (!rm->data.op_sg) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
                ret = rds_message_copy_from_user(rm, msg->msg_iov, payload_len);
                if (ret)
                        goto out;