Rather then cacheing whole pages, use kmalloc to potentially
cache a smaller size.

Change-Id: I9fb749dc2bdac506d1bc6f2259fbbdeeec87b298
Signed-off-by: William Roberts <[email protected]>
---
 fs/proc/base.c          |   93 +++++++++++++++++++++++++++++++++++++++--------
 include/linux/proc_fs.h |    5 ++-
 kernel/auditsc.c        |   43 ++++++++++++++--------
 3 files changed, 109 insertions(+), 32 deletions(-)

diff --git a/fs/proc/base.c b/fs/proc/base.c
index 25b73d3..a0751ed 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -209,35 +209,76 @@ struct mm_struct *mm_for_maps(struct task_struct *task)
        return mm_access(task, PTRACE_MODE_READ);
 }
 
-int proc_pid_cmdline(struct task_struct *task, char *buffer)
+/*
+ * Returns the length of the VM area containing the tasks cmdline info.
+ * 0 indicates success
+ */
+int proc_pid_cmdline_length(struct task_struct *task, unsigned int *len)
+{
+       int res = -1;
+       struct mm_struct *mm;
+
+       if (!task || !len)
+               return 0;
+
+       mm = get_task_mm(task);
+       if (!mm)
+               goto out;
+       if (!mm->arg_end)
+               goto out_mm;    /* Shh! No looking before we're done */
+
+       *len = mm->arg_end - mm->arg_start;
+       res = 0;
+out_mm:
+       mmput(mm);
+out:
+       return res;
+}
+
+/* Copy's the tasks cmdline data into buf, truncating at buflen */
+int proc_pid_copy_cmdline_to_buf(struct task_struct *task, char *buf,
+                                unsigned int buflen)
 {
        int res = 0;
        unsigned int len;
-       struct mm_struct *mm = get_task_mm(task);
+       struct mm_struct *mm;
+
+       if (!buflen || !buf)
+               return 0;
+
+       mm = get_task_mm(task);
        if (!mm)
                goto out;
        if (!mm->arg_end)
                goto out_mm;    /* Shh! No looking before we're done */
 
-       len = mm->arg_end - mm->arg_start;
- 
-       if (len > PAGE_SIZE)
-               len = PAGE_SIZE;
- 
-       res = access_process_vm(task, mm->arg_start, buffer, len, 0);
+       res = access_process_vm(task, mm->arg_start, buf, buflen, 0);
+       if (res <= 0)
+               goto out_mm;
+
+       /* Truncate to res if buflen is longer */
+       if (res > buflen)
+               res = buflen;
 
-       // If the nul at the end of args has been overwritten, then
-       // assume application is using setproctitle(3).
-       if (res > 0 && buffer[res-1] != '\0' && len < PAGE_SIZE) {
-               len = strnlen(buffer, res);
+       /*
+        * If the nul at the end of args has been overwritten, then
+        * assume application is using setproctitle(3).
+        */
+       if (buf[res-1] != '\0') {
+               /* Nul between start and end of vm space?
+                  If so, then truncate size down */
+               len = strnlen(buf, res);
                if (len < res) {
                    res = len;
                } else {
+                       /* No nul, truncate to buflen if too big to fit */
                        len = mm->env_end - mm->env_start;
-                       if (len > PAGE_SIZE - res)
-                               len = PAGE_SIZE - res;
-                       res += access_process_vm(task, mm->env_start, 
buffer+res, len, 0);
-                       res = strnlen(buffer, res);
+                       if (len > buflen - res)
+                               len = buflen - res;
+                       /* Copy in any remaining data */
+                       res += access_process_vm(task, mm->env_start, buf+res,
+                                                len, 0);
+                       res = strnlen(buf, res);
                }
        }
 out_mm:
@@ -246,6 +287,26 @@ out:
        return res;
 }
 
+static int proc_pid_cmdline(struct task_struct *task, char *buffer)
+{
+       unsigned int len;
+       int res = proc_pid_cmdline_length(task, &len);
+       if (res)
+               return 0;
+
+       /* The caller of this allocates a page */
+       if (len > PAGE_SIZE)
+               len = PAGE_SIZE;
+
+       res = proc_pid_copy_cmdline_to_buf(task, buffer, len);
+       /*
+        * Ensure NULL terminated! Application could
+        * could be using setproctitle(3).
+        */
+       buffer[res-1] = '\0';
+       return res;
+}
+
 static int proc_pid_auxv(struct task_struct *task, char *buffer)
 {
        struct mm_struct *mm = mm_for_maps(task);
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index d85ac14..f76deb3 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -118,7 +118,10 @@ struct pid_namespace;
 
 extern int pid_ns_prepare_proc(struct pid_namespace *ns);
 extern void pid_ns_release_proc(struct pid_namespace *ns);
-extern int proc_pid_cmdline(struct task_struct *task, char *buffer);
+
+extern int proc_pid_cmdline_length(struct task_struct *task, unsigned int 
*len);
+extern int proc_pid_copy_cmdline_to_buf(struct task_struct *task, char *buf,
+                                       unsigned int buflen);
 
 /*
  * proc_tty.c
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 27f8224..34a6c1d 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -1049,7 +1049,7 @@ static inline void audit_cmdline_free(struct 
audit_context *ctx)
 {
        if (!ctx->cmdline)
                return;
-       free_page((unsigned long)ctx->cmdline);
+       kfree(ctx->cmdline);
        ctx->cmdline = NULL;
 }
 
@@ -1164,30 +1164,43 @@ error_path:
 
 EXPORT_SYMBOL(audit_log_task_context);
 
-static char *audit_cmdline_get_page(struct audit_buffer *ab,
+static char *audit_cmdline_get(struct audit_buffer *ab,
                                  struct task_struct *tsk)
 {
        int len;
-       unsigned long page;
+       int res;
+       char *buf;
 
-       /* Get the process cmdline */
-       page = __get_free_page(GFP_TEMPORARY);
-       if (!page) {
+       res = proc_pid_cmdline_length(tsk, &len);
+       if (res != 0 || len == 0)
                return NULL;
-       }
-       len = proc_pid_cmdline(tsk, (char *)page);
-       if (len <= 0) {
-               free_page(page);
+
+       if (len > PATH_MAX)
+               len = PATH_MAX;
+
+       buf = kmalloc(len, GFP_KERNEL);
+       if (!buf)
+               return NULL;
+
+       res = proc_pid_copy_cmdline_to_buf(tsk, buf, len);
+       if (res <= 0) {
+               kfree(buf);
                return NULL;
        }
+
+       /*
+        * res is guarenteed not to be longer than
+        * the buf as it was truncated to len
+        * in proc_pid_copy_cmdline_to_buf()
+        */
+       len = res;
+
        /*
         * Ensure NULL terminated! Application could
         * could be using setproctitle(3).
         */
-       ((char *)page)[len-1] = '\0';
-
-       /* XXX: Re-alloc to something smaller then a page here? */
-       return (char *)page;
+       buf[len-1] = '\0';
+       return buf;
 }
 
 static void audit_log_cmdline(struct audit_buffer *ab, struct task_struct *tsk,
@@ -1203,7 +1216,7 @@ static void audit_log_cmdline(struct audit_buffer *ab, 
struct task_struct *tsk,
                goto out;
        }
        /* Not cached yet */
-       context->cmdline = audit_cmdline_get_page(ab, tsk);
+       context->cmdline = audit_cmdline_get(ab, tsk);
        if (!context->cmdline)
                goto out;
        msg = context->cmdline;
-- 
1.7.9.5

--
Linux-audit mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/linux-audit

Reply via email to