diff --git a/fs/proc/base.c b/fs/proc/base.c index 837469a..5ac6ece 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -255,32 +255,47 @@ static int proc_pid_cmdline(struct task_struct *task, char * buffer) int res = 0; unsigned int len; struct mm_struct *mm = get_task_mm(task); + unsigned seq; + if (!mm) goto out; + + /* The process was not constructed yet? */ if (!mm->arg_end) - goto out_mm; /* Shh! No looking before we're done */ + goto out_mm; - 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); - - // 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 (len < res) { - res = len; - } else { - 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); + do { + seq = read_seqbegin(&mm->arg_lock); + + 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); + + if (mm->arg_end != mm->env_start) + /* PR_SET_PROCTITLE_AREA used */ res = strnlen(buffer, res); + else if (res > 0 && buffer[res-1] != '\0' && len < PAGE_SIZE) { + /* + * If the nul at the end of args has been overwritten, + * then assume application is using sendmail's + * SPT_REUSEARGV style argv override. + */ + len = strnlen(buffer, res); + if (len < res) { + res = len; + } else { + 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); + } } - } + } while (read_seqretry(&mm->arg_lock, seq)); + out_mm: mmput(mm); out: diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 84a524a..279d620 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -236,6 +237,7 @@ struct mm_struct { unsigned long stack_vm, reserved_vm, def_flags, nr_ptes; unsigned long start_code, end_code, start_data, end_data; unsigned long start_brk, brk, start_stack; + seqlock_t arg_lock; unsigned long arg_start, arg_end, env_start, env_end; unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */ diff --git a/include/linux/prctl.h b/include/linux/prctl.h index 9311505..e80a11b 100644 --- a/include/linux/prctl.h +++ b/include/linux/prctl.h @@ -90,4 +90,8 @@ #define PR_MCE_KILL 33 + +/* Set process title memory area for setproctitle() */ +#define PR_SET_PROCTITLE_AREA 34 + #endif /* _LINUX_PRCTL_H */ diff --git a/kernel/fork.c b/kernel/fork.c index 266c6af..13089e3 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -459,6 +459,7 @@ static struct mm_struct * mm_init(struct mm_struct * mm, struct task_struct *p) mm->cached_hole_size = ~0UL; mm_init_aio(mm); mm_init_owner(mm, p); + seqlock_init(&mm->arg_lock); if (likely(!mm_alloc_pgd(mm))) { mm->def_flags = 0; diff --git a/kernel/sys.c b/kernel/sys.c index 255475d..83724d2 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -1563,7 +1563,29 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, } error = 0; break; + case PR_SET_PROCTITLE_AREA: { + struct mm_struct *mm = current->mm; + unsigned long addr = arg2; + unsigned long len = arg3; + unsigned long end = arg2 + arg3; + if (len > PAGE_SIZE) + return -EINVAL; + + if (addr >= end) + return -EINVAL; + + if (!access_ok(VERIFY_READ, addr, len)) { + return -EFAULT; + } + + write_seqlock(&mm->arg_lock); + mm->arg_start = addr; + mm->arg_end = addr + len; + write_sequnlock(&mm->arg_lock); + + return 0; + } default: error = -EINVAL; break;