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
