rpc: fix huge kmalloc's in gss-proxy
authorJ. Bruce Fields <bfields@redhat.com>
Tue, 20 Aug 2013 22:13:27 +0000 (18:13 -0400)
committerJ. Bruce Fields <bfields@redhat.com>
Fri, 6 Sep 2013 15:45:58 +0000 (11:45 -0400)
The reply to a gssproxy can include up to NGROUPS_MAX gid's, which will
take up more than a page.  We therefore need to allocate an array of
pages to hold the reply instead of trying to allocate a single huge
buffer.

Tested-by: Simo Sorce <simo@redhat.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
net/sunrpc/auth_gss/gss_rpc_upcall.c
net/sunrpc/auth_gss/gss_rpc_xdr.c
net/sunrpc/auth_gss/gss_rpc_xdr.h

index af7ffd4..be95af3 100644 (file)
@@ -213,6 +213,30 @@ static int gssp_call(struct net *net, struct rpc_message *msg)
        return status;
 }
 
+static void gssp_free_receive_pages(struct gssx_arg_accept_sec_context *arg)
+{
+       int i;
+
+       for (i = 0; i < arg->npages && arg->pages[i]; i++)
+               __free_page(arg->pages[i]);
+}
+
+static int gssp_alloc_receive_pages(struct gssx_arg_accept_sec_context *arg)
+{
+       int i;
+
+       arg->npages = DIV_ROUND_UP(NGROUPS_MAX * 4, PAGE_SIZE);
+       arg->pages = kzalloc(arg->npages * sizeof(struct page *), GFP_KERNEL);
+
+       for (i=0; i < arg->npages; i++) {
+               arg->pages[i] = alloc_page(GFP_KERNEL);
+               if (arg->pages[i] == NULL) {
+                       gssp_free_receive_pages(arg);
+                       return -ENOMEM;
+               }
+       }
+       return 0;
+}
 
 /*
  * Public functions
@@ -261,10 +285,16 @@ int gssp_accept_sec_context_upcall(struct net *net,
                arg.context_handle = &ctxh;
        res.output_token->len = GSSX_max_output_token_sz;
 
+       ret = gssp_alloc_receive_pages(&arg);
+       if (ret)
+               return ret;
+
        /* use nfs/ for targ_name ? */
 
        ret = gssp_call(net, &msg);
 
+       gssp_free_receive_pages(&arg);
+
        /* we need to fetch all data even in case of error so
         * that we can free special strctures is they have been allocated */
        data->major_status = res.status.major_status;
index 3c19c7d..f0f78c5 100644 (file)
@@ -780,6 +780,9 @@ void gssx_enc_accept_sec_context(struct rpc_rqst *req,
        /* arg->options */
        err = dummy_enc_opt_array(xdr, &arg->options);
 
+       xdr_inline_pages(&req->rq_rcv_buf,
+               PAGE_SIZE/2 /* pretty arbitrary */,
+               arg->pages, 0 /* page base */, arg->npages * PAGE_SIZE);
 done:
        if (err)
                dprintk("RPC:       gssx_enc_accept_sec_context: %d\n", err);
index 1c98b27..685a688 100644 (file)
@@ -147,6 +147,8 @@ struct gssx_arg_accept_sec_context {
        struct gssx_cb *input_cb;
        u32 ret_deleg_cred;
        struct gssx_option_array options;
+       struct page **pages;
+       unsigned int npages;
 };
 
 struct gssx_res_accept_sec_context {
@@ -240,7 +242,8 @@ int gssx_dec_accept_sec_context(struct rpc_rqst *rqstp,
                             2 * GSSX_max_princ_sz + \
                             8 + 8 + 4 + 4 + 4)
 #define GSSX_max_output_token_sz 1024
-#define GSSX_max_creds_sz (4 + 4 + 4 + NGROUPS_MAX * 4)
+/* grouplist not included; we allocate separate pages for that: */
+#define GSSX_max_creds_sz (4 + 4 + 4 /* + NGROUPS_MAX*4 */)
 #define GSSX_RES_accept_sec_context_sz (GSSX_default_status_sz + \
                                        GSSX_default_ctx_sz + \
                                        GSSX_max_output_token_sz + \