All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4] Added PR_SET_PROCTITLE_AREA option for prctl()
@ 2009-11-01 12:16 KOSAKI Motohiro
  2009-11-03  9:47   ` Américo Wang
  0 siblings, 1 reply; 14+ messages in thread
From: KOSAKI Motohiro @ 2009-11-01 12:16 UTC (permalink / raw)
  To: Timo Sirainen, Bryan Donlan, Ulrich Drepper, LKML, Andrew Morton,
	linux-api
  Cc: kosaki.motohiro


ChangeLog
  v3 -> v4
    - Use mutex instead seq_lock as akpm requested.

========================================

From: Timo Sirainen <tss@iki.fi>

Currently glibc2 doesn't have setproctitle(3), so several userland
daemons attempt to emulate it by doing some brutal stack modifications.
This works most of the time, but it has problems. For example:

 % ps -ef |grep avahi-daemon
 avahi     1679     1  0 09:20 ?        00:00:00 avahi-daemon: running [kosadesk.local]

 # cat /proc/1679/cmdline
 avahi-daemon: running [kosadesk.local]

This looks good, but the process has also overwritten its environment
area and made the environ file useless:

 # cat /proc/1679/environ
 adesk.local]

Another problem is that the process title length is limited by the size of
the environment. Security conscious people try to avoid potential information
leaks by clearing most of the environment before running a daemon:

 # env - MINIMUM_NEEDED_VAR=foo /path/to/daemon

The resulting environment size may be too small to fit the wanted process
titles.

This patch makes it possible for userspace to implement setproctitle()
cleanly. It adds a new PR_SET_PROCTITLE_AREA option for prctl(), which
updates task's mm_struct->arg_start and arg_end to the given area.

 test_setproctitle.c
 ================================================
 #include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <sys/prctl.h>

 #define ERR(str) (perror(str), exit(1))

 void settitle(char* title){
         int err;

         err = prctl(34, title, strlen(title)+1);
         if (err < 0)
                 ERR("prctl ");
 }

 void main(void){
         long i;
         char buf[1024];

         for (i = 0; i < 10000000000LL; i++){
                 sprintf(buf, "loooooooooooooooooooooooong string %d",i);
                 settitle(buf);
         }
 }
 ==================================================

Cc: Bryan Donlan <bdonlan@gmail.com>
Cc: Ulrich Drepper <drepper@redhat.com>
Signed-off-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Signed-off-by: Timo Sirainen <tss@iki.fi>
---
 fs/proc/base.c           |   31 ++++++++++++++++++++++---------
 include/linux/mm_types.h |    2 ++
 include/linux/prctl.h    |    3 +++
 kernel/fork.c            |    1 +
 kernel/sys.c             |   22 ++++++++++++++++++++++
 5 files changed, 50 insertions(+), 9 deletions(-)

diff --git a/fs/proc/base.c b/fs/proc/base.c
index 837469a..ac800b4 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -255,32 +255,45 @@ 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);
+
 	if (!mm)
 		goto out;
+
+	/* The process was not constructed yet? */
 	if (!mm->arg_end)
 		goto out_mm;	/* Shh! No looking before we're done */
 
- 	len = mm->arg_end - mm->arg_start;
- 
+	mutex_lock(&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)
+		/* prctl(PR_SET_PROCTITLE_AREA) used */
+		goto out_unlock;
 
-	// If the nul at the end of args has been overwritten, then
-	// assume application is using setproctitle(3).
+	/*
+	 * If the nul at the end of args has been overwritten, then assume
+	 * application is using sendmail's SPT_REUSEARGV style argv override.
+	 */
 	if (res > 0 && buffer[res-1] != '\0' && len < PAGE_SIZE) {
 		len = strnlen(buffer, res);
-		if (len < res) {
-		    res = len;
-		} else {
+		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 += access_process_vm(task, mm->env_start,
+						 buffer+res, len, 0);
 			res = strnlen(buffer, res);
 		}
 	}
+
+out_unlock:
+	mutex_unlock(&mm->arg_lock);
+
 out_mm:
 	mmput(mm);
 out:
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 84a524a..3e2a346 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -12,6 +12,7 @@
 #include <linux/completion.h>
 #include <linux/cpumask.h>
 #include <linux/page-debug-flags.h>
+#include <linux/mutex.h>
 #include <asm/page.h>
 #include <asm/mmu.h>
 
@@ -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;
+	struct mutex 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..da47542 100644
--- a/include/linux/prctl.h
+++ b/include/linux/prctl.h
@@ -90,4 +90,7 @@
 
 #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 4c20fff..881a6b4 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);
+	mutex_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..434ea13 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1564,6 +1564,28 @@ 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;
+
+			mutex_lock(&mm->arg_lock);
+			mm->arg_start = addr;
+			mm->arg_end = addr + len;
+			mutex_unlock(&mm->arg_lock);
+
+			return 0;
+		}
 		default:
 			error = -EINVAL;
 			break;
-- 
1.6.2.5




^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH v4] Added PR_SET_PROCTITLE_AREA option for prctl()
@ 2009-11-03  9:47   ` Américo Wang
  0 siblings, 0 replies; 14+ messages in thread
From: Américo Wang @ 2009-11-03  9:47 UTC (permalink / raw)
  To: KOSAKI Motohiro
  Cc: Timo Sirainen, Bryan Donlan, Ulrich Drepper, LKML, Andrew Morton,
	linux-api

On Sun, Nov 01, 2009 at 09:16:27PM +0900, KOSAKI Motohiro wrote:
>
>ChangeLog
>  v3 -> v4
>    - Use mutex instead seq_lock as akpm requested.
>
>========================================
>
>From: Timo Sirainen <tss@iki.fi>


Please see my comments below.

>
>Currently glibc2 doesn't have setproctitle(3), so several userland
>daemons attempt to emulate it by doing some brutal stack modifications.
>This works most of the time, but it has problems. For example:
>
> % ps -ef |grep avahi-daemon
> avahi     1679     1  0 09:20 ?        00:00:00 avahi-daemon: running [kosadesk.local]
>
> # cat /proc/1679/cmdline
> avahi-daemon: running [kosadesk.local]
>
>This looks good, but the process has also overwritten its environment
>area and made the environ file useless:
>
> # cat /proc/1679/environ
> adesk.local]
>
>Another problem is that the process title length is limited by the size of
>the environment. Security conscious people try to avoid potential information
>leaks by clearing most of the environment before running a daemon:
>
> # env - MINIMUM_NEEDED_VAR=foo /path/to/daemon
>
>The resulting environment size may be too small to fit the wanted process
>titles.
>
>This patch makes it possible for userspace to implement setproctitle()
>cleanly. It adds a new PR_SET_PROCTITLE_AREA option for prctl(), which
>updates task's mm_struct->arg_start and arg_end to the given area.
>
> test_setproctitle.c
> ================================================
> #include <string.h>
> #include <stdlib.h>
> #include <unistd.h>
> #include <stdio.h>
> #include <sys/prctl.h>
>
> #define ERR(str) (perror(str), exit(1))
>
> void settitle(char* title){
>         int err;
>
>         err = prctl(34, title, strlen(title)+1);
>         if (err < 0)
>                 ERR("prctl ");
> }
>
> void main(void){
>         long i;
>         char buf[1024];
>
>         for (i = 0; i < 10000000000LL; i++){
>                 sprintf(buf, "loooooooooooooooooooooooong string %d",i);
>                 settitle(buf);
>         }
> }
> ==================================================
>
>Cc: Bryan Donlan <bdonlan@gmail.com>
>Cc: Ulrich Drepper <drepper@redhat.com>
>Signed-off-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
>Signed-off-by: Timo Sirainen <tss@iki.fi>
>---
> fs/proc/base.c           |   31 ++++++++++++++++++++++---------
> include/linux/mm_types.h |    2 ++
> include/linux/prctl.h    |    3 +++
> kernel/fork.c            |    1 +
> kernel/sys.c             |   22 ++++++++++++++++++++++
> 5 files changed, 50 insertions(+), 9 deletions(-)
>
>diff --git a/fs/proc/base.c b/fs/proc/base.c
>index 837469a..ac800b4 100644
>--- a/fs/proc/base.c
>+++ b/fs/proc/base.c
>@@ -255,32 +255,45 @@ 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);
>+
> 	if (!mm)
> 		goto out;
>+
>+	/* The process was not constructed yet? */
> 	if (!mm->arg_end)
> 		goto out_mm;	/* Shh! No looking before we're done */
> 
>- 	len = mm->arg_end - mm->arg_start;
>- 
>+	mutex_lock(&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)
>+		/* prctl(PR_SET_PROCTITLE_AREA) used */
>+		goto out_unlock;
> 
>-	// If the nul at the end of args has been overwritten, then
>-	// assume application is using setproctitle(3).
>+	/*
>+	 * If the nul at the end of args has been overwritten, then assume
>+	 * application is using sendmail's SPT_REUSEARGV style argv override.
>+	 */
> 	if (res > 0 && buffer[res-1] != '\0' && len < PAGE_SIZE) {
> 		len = strnlen(buffer, res);
>-		if (len < res) {
>-		    res = len;
>-		} else {
>+		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 += access_process_vm(task, mm->env_start,
>+						 buffer+res, len, 0);
> 			res = strnlen(buffer, res);
> 		}
> 	}
>+
>+out_unlock:
>+	mutex_unlock(&mm->arg_lock);
>+
> out_mm:
> 	mmput(mm);
> out:
>diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
>index 84a524a..3e2a346 100644
>--- a/include/linux/mm_types.h
>+++ b/include/linux/mm_types.h
>@@ -12,6 +12,7 @@
> #include <linux/completion.h>
> #include <linux/cpumask.h>
> #include <linux/page-debug-flags.h>
>+#include <linux/mutex.h>
> #include <asm/page.h>
> #include <asm/mmu.h>
> 
>@@ -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;
>+	struct mutex 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..da47542 100644
>--- a/include/linux/prctl.h
>+++ b/include/linux/prctl.h
>@@ -90,4 +90,7 @@
> 
> #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 4c20fff..881a6b4 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);
>+	mutex_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..434ea13 100644
>--- a/kernel/sys.c
>+++ b/kernel/sys.c
>@@ -1564,6 +1564,28 @@ 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;
>+
>+			mutex_lock(&mm->arg_lock);
>+			mm->arg_start = addr;

Is this safe? You're assigning a user-space pointer to kernel space...
Don't we need copy_from_user()?

>+			mm->arg_end = addr + len;


Since you already have 'end', no need to caculate this again. :)


>+			mutex_unlock(&mm->arg_lock);
>+
>+			return 0;
>+		}
> 		default:
> 			error = -EINVAL;
> 			break;


-- 
Live like a child, think like the god.
 

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v4] Added PR_SET_PROCTITLE_AREA option for prctl()
@ 2009-11-03  9:47   ` Américo Wang
  0 siblings, 0 replies; 14+ messages in thread
From: Américo Wang @ 2009-11-03  9:47 UTC (permalink / raw)
  To: KOSAKI Motohiro
  Cc: Timo Sirainen, Bryan Donlan, Ulrich Drepper, LKML, Andrew Morton,
	linux-api-u79uwXL29TY76Z2rM5mHXA

On Sun, Nov 01, 2009 at 09:16:27PM +0900, KOSAKI Motohiro wrote:
>
>ChangeLog
>  v3 -> v4
>    - Use mutex instead seq_lock as akpm requested.
>
>========================================
>
>From: Timo Sirainen <tss-X3B1VOXEql0@public.gmane.org>


Please see my comments below.

>
>Currently glibc2 doesn't have setproctitle(3), so several userland
>daemons attempt to emulate it by doing some brutal stack modifications.
>This works most of the time, but it has problems. For example:
>
> % ps -ef |grep avahi-daemon
> avahi     1679     1  0 09:20 ?        00:00:00 avahi-daemon: running [kosadesk.local]
>
> # cat /proc/1679/cmdline
> avahi-daemon: running [kosadesk.local]
>
>This looks good, but the process has also overwritten its environment
>area and made the environ file useless:
>
> # cat /proc/1679/environ
> adesk.local]
>
>Another problem is that the process title length is limited by the size of
>the environment. Security conscious people try to avoid potential information
>leaks by clearing most of the environment before running a daemon:
>
> # env - MINIMUM_NEEDED_VAR=foo /path/to/daemon
>
>The resulting environment size may be too small to fit the wanted process
>titles.
>
>This patch makes it possible for userspace to implement setproctitle()
>cleanly. It adds a new PR_SET_PROCTITLE_AREA option for prctl(), which
>updates task's mm_struct->arg_start and arg_end to the given area.
>
> test_setproctitle.c
> ================================================
> #include <string.h>
> #include <stdlib.h>
> #include <unistd.h>
> #include <stdio.h>
> #include <sys/prctl.h>
>
> #define ERR(str) (perror(str), exit(1))
>
> void settitle(char* title){
>         int err;
>
>         err = prctl(34, title, strlen(title)+1);
>         if (err < 0)
>                 ERR("prctl ");
> }
>
> void main(void){
>         long i;
>         char buf[1024];
>
>         for (i = 0; i < 10000000000LL; i++){
>                 sprintf(buf, "loooooooooooooooooooooooong string %d",i);
>                 settitle(buf);
>         }
> }
> ==================================================
>
>Cc: Bryan Donlan <bdonlan-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>Cc: Ulrich Drepper <drepper-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>Signed-off-by: KOSAKI Motohiro <kosaki.motohiro-+CUm20s59erQFUHtdCDX3A@public.gmane.org>
>Signed-off-by: Timo Sirainen <tss-X3B1VOXEql0@public.gmane.org>
>---
> fs/proc/base.c           |   31 ++++++++++++++++++++++---------
> include/linux/mm_types.h |    2 ++
> include/linux/prctl.h    |    3 +++
> kernel/fork.c            |    1 +
> kernel/sys.c             |   22 ++++++++++++++++++++++
> 5 files changed, 50 insertions(+), 9 deletions(-)
>
>diff --git a/fs/proc/base.c b/fs/proc/base.c
>index 837469a..ac800b4 100644
>--- a/fs/proc/base.c
>+++ b/fs/proc/base.c
>@@ -255,32 +255,45 @@ 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);
>+
> 	if (!mm)
> 		goto out;
>+
>+	/* The process was not constructed yet? */
> 	if (!mm->arg_end)
> 		goto out_mm;	/* Shh! No looking before we're done */
> 
>- 	len = mm->arg_end - mm->arg_start;
>- 
>+	mutex_lock(&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)
>+		/* prctl(PR_SET_PROCTITLE_AREA) used */
>+		goto out_unlock;
> 
>-	// If the nul at the end of args has been overwritten, then
>-	// assume application is using setproctitle(3).
>+	/*
>+	 * If the nul at the end of args has been overwritten, then assume
>+	 * application is using sendmail's SPT_REUSEARGV style argv override.
>+	 */
> 	if (res > 0 && buffer[res-1] != '\0' && len < PAGE_SIZE) {
> 		len = strnlen(buffer, res);
>-		if (len < res) {
>-		    res = len;
>-		} else {
>+		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 += access_process_vm(task, mm->env_start,
>+						 buffer+res, len, 0);
> 			res = strnlen(buffer, res);
> 		}
> 	}
>+
>+out_unlock:
>+	mutex_unlock(&mm->arg_lock);
>+
> out_mm:
> 	mmput(mm);
> out:
>diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
>index 84a524a..3e2a346 100644
>--- a/include/linux/mm_types.h
>+++ b/include/linux/mm_types.h
>@@ -12,6 +12,7 @@
> #include <linux/completion.h>
> #include <linux/cpumask.h>
> #include <linux/page-debug-flags.h>
>+#include <linux/mutex.h>
> #include <asm/page.h>
> #include <asm/mmu.h>
> 
>@@ -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;
>+	struct mutex 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..da47542 100644
>--- a/include/linux/prctl.h
>+++ b/include/linux/prctl.h
>@@ -90,4 +90,7 @@
> 
> #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 4c20fff..881a6b4 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);
>+	mutex_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..434ea13 100644
>--- a/kernel/sys.c
>+++ b/kernel/sys.c
>@@ -1564,6 +1564,28 @@ 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;
>+
>+			mutex_lock(&mm->arg_lock);
>+			mm->arg_start = addr;

Is this safe? You're assigning a user-space pointer to kernel space...
Don't we need copy_from_user()?

>+			mm->arg_end = addr + len;


Since you already have 'end', no need to caculate this again. :)


>+			mutex_unlock(&mm->arg_lock);
>+
>+			return 0;
>+		}
> 		default:
> 			error = -EINVAL;
> 			break;


-- 
Live like a child, think like the god.
 

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v4] Added PR_SET_PROCTITLE_AREA option for prctl()
@ 2009-11-03 14:07     ` KOSAKI Motohiro
  0 siblings, 0 replies; 14+ messages in thread
From: KOSAKI Motohiro @ 2009-11-03 14:07 UTC (permalink / raw)
  To: Americo Wang
  Cc: kosaki.motohiro, Timo Sirainen, Bryan Donlan, Ulrich Drepper,
	LKML, Andrew Morton, linux-api

> >+		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;
> >+
> >+			mutex_lock(&mm->arg_lock);
> >+			mm->arg_start = addr;
> 
> Is this safe? You're assigning a user-space pointer to kernel space...
> Don't we need copy_from_user()?

mm->arg_start, arg_end are defined so.
Please see current implementation. 


> >+			mm->arg_end = addr + len;
> 
> Since you already have 'end', no need to caculate this again. :)

Good catch :)



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v4] Added PR_SET_PROCTITLE_AREA option for prctl()
@ 2009-11-03 14:07     ` KOSAKI Motohiro
  0 siblings, 0 replies; 14+ messages in thread
From: KOSAKI Motohiro @ 2009-11-03 14:07 UTC (permalink / raw)
  To: Americo Wang
  Cc: kosaki.motohiro-+CUm20s59erQFUHtdCDX3A, Timo Sirainen,
	Bryan Donlan, Ulrich Drepper, LKML, Andrew Morton,
	linux-api-u79uwXL29TY76Z2rM5mHXA

> >+		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;
> >+
> >+			mutex_lock(&mm->arg_lock);
> >+			mm->arg_start = addr;
> 
> Is this safe? You're assigning a user-space pointer to kernel space...
> Don't we need copy_from_user()?

mm->arg_start, arg_end are defined so.
Please see current implementation. 


> >+			mm->arg_end = addr + len;
> 
> Since you already have 'end', no need to caculate this again. :)

Good catch :)

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v5] Added PR_SET_PROCTITLE_AREA option for prctl()
@ 2009-11-03 15:26       ` KOSAKI Motohiro
  0 siblings, 0 replies; 14+ messages in thread
From: KOSAKI Motohiro @ 2009-11-03 15:26 UTC (permalink / raw)
  To: KOSAKI Motohiro
  Cc: kosaki.motohiro, Americo Wang, Timo Sirainen, Bryan Donlan,
	Ulrich Drepper, LKML, Andrew Morton, linux-api

> > >+		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;
> > >+
> > >+			mutex_lock(&mm->arg_lock);
> > >+			mm->arg_start = addr;
> > 
> > Is this safe? You're assigning a user-space pointer to kernel space...
> > Don't we need copy_from_user()?
> 
> mm->arg_start, arg_end are defined so.
> Please see current implementation. 
> 
> 
> > >+			mm->arg_end = addr + len;
> > 
> > Since you already have 'end', no need to caculate this again. :)
> 
> Good catch :)
> 
> 

Fixed.


ChangeLog
  v4 -> v5
    - nit: kill duplicate calculation in prctl()
  v3 -> v4
    - Use mutex instead seq_lock.

========================================

Subject: [PATCH v5] Added PR_SET_PROCTITLE_AREA option for prctl()
From: Timo Sirainen <tss@iki.fi>

Currently glibc2 doesn't have setproctitle(3), so several userland
daemons attempt to emulate it by doing some brutal stack modifications.
This works most of the time, but it has problems. For example:

 % ps -ef |grep avahi-daemon
 avahi     1679     1  0 09:20 ?        00:00:00 avahi-daemon: running [kosadesk.local]

 # cat /proc/1679/cmdline
 avahi-daemon: running [kosadesk.local]

This looks good, but the process has also overwritten its environment
area and made the environ file useless:

 # cat /proc/1679/environ
 adesk.local]

Another problem is that the process title length is limited by the size of
the environment. Security conscious people try to avoid potential information
leaks by clearing most of the environment before running a daemon:

 # env - MINIMUM_NEEDED_VAR=foo /path/to/daemon

The resulting environment size may be too small to fit the wanted process
titles.

This patch makes it possible for userspace to implement setproctitle()
cleanly. It adds a new PR_SET_PROCTITLE_AREA option for prctl(), which
updates task's mm_struct->arg_start and arg_end to the given area.

 test_setproctitle.c
 ================================================
 #include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <sys/prctl.h>

 #define ERR(str) (perror(str), exit(1))

 void settitle(char* title){
         int err;

         err = prctl(34, title, strlen(title)+1);
         if (err < 0)
                 ERR("prctl ");
 }

 void main(void){
         long i;
         char buf[1024];

         for (i = 0; i < 10000000000LL; i++){
                 sprintf(buf, "loooooooooooooooooooooooong string %d",i);
                 settitle(buf);
         }
 }
 ==================================================

Cc: Bryan Donlan <bdonlan@gmail.com>
Cc: Ulrich Drepper <drepper@redhat.com>
Signed-off-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Signed-off-by: Timo Sirainen <tss@iki.fi>
---
 fs/proc/base.c           |   31 ++++++++++++++++++++++---------
 include/linux/mm_types.h |    2 ++
 include/linux/prctl.h    |    3 +++
 kernel/fork.c            |    1 +
 kernel/sys.c             |   22 ++++++++++++++++++++++
 5 files changed, 50 insertions(+), 9 deletions(-)

diff --git a/fs/proc/base.c b/fs/proc/base.c
index 837469a..ac800b4 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -255,32 +255,45 @@ 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);
+
 	if (!mm)
 		goto out;
+
+	/* The process was not constructed yet? */
 	if (!mm->arg_end)
 		goto out_mm;	/* Shh! No looking before we're done */
 
- 	len = mm->arg_end - mm->arg_start;
- 
+	mutex_lock(&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)
+		/* prctl(PR_SET_PROCTITLE_AREA) used */
+		goto out_unlock;
 
-	// If the nul at the end of args has been overwritten, then
-	// assume application is using setproctitle(3).
+	/*
+	 * If the nul at the end of args has been overwritten, then assume
+	 * application is using sendmail's SPT_REUSEARGV style argv override.
+	 */
 	if (res > 0 && buffer[res-1] != '\0' && len < PAGE_SIZE) {
 		len = strnlen(buffer, res);
-		if (len < res) {
-		    res = len;
-		} else {
+		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 += access_process_vm(task, mm->env_start,
+						 buffer+res, len, 0);
 			res = strnlen(buffer, res);
 		}
 	}
+
+out_unlock:
+	mutex_unlock(&mm->arg_lock);
+
 out_mm:
 	mmput(mm);
 out:
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 84a524a..3e2a346 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -12,6 +12,7 @@
 #include <linux/completion.h>
 #include <linux/cpumask.h>
 #include <linux/page-debug-flags.h>
+#include <linux/mutex.h>
 #include <asm/page.h>
 #include <asm/mmu.h>
 
@@ -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;
+	struct mutex 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..da47542 100644
--- a/include/linux/prctl.h
+++ b/include/linux/prctl.h
@@ -90,4 +90,7 @@
 
 #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 4c20fff..881a6b4 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);
+	mutex_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..bde6957 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1564,6 +1564,28 @@ 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;
+
+			mutex_lock(&mm->arg_lock);
+			mm->arg_start = addr;
+			mm->arg_end = end;
+			mutex_unlock(&mm->arg_lock);
+
+			return 0;
+		}
 		default:
 			error = -EINVAL;
 			break;
-- 
1.6.2.5





^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v5] Added PR_SET_PROCTITLE_AREA option for prctl()
@ 2009-11-03 15:26       ` KOSAKI Motohiro
  0 siblings, 0 replies; 14+ messages in thread
From: KOSAKI Motohiro @ 2009-11-03 15:26 UTC (permalink / raw)
  Cc: kosaki.motohiro-+CUm20s59erQFUHtdCDX3A, Americo Wang,
	Timo Sirainen, Bryan Donlan, Ulrich Drepper, LKML, Andrew Morton,
	linux-api-u79uwXL29TY76Z2rM5mHXA

> > >+		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;
> > >+
> > >+			mutex_lock(&mm->arg_lock);
> > >+			mm->arg_start = addr;
> > 
> > Is this safe? You're assigning a user-space pointer to kernel space...
> > Don't we need copy_from_user()?
> 
> mm->arg_start, arg_end are defined so.
> Please see current implementation. 
> 
> 
> > >+			mm->arg_end = addr + len;
> > 
> > Since you already have 'end', no need to caculate this again. :)
> 
> Good catch :)
> 
> 

Fixed.


ChangeLog
  v4 -> v5
    - nit: kill duplicate calculation in prctl()
  v3 -> v4
    - Use mutex instead seq_lock.

========================================

Subject: [PATCH v5] Added PR_SET_PROCTITLE_AREA option for prctl()
From: Timo Sirainen <tss-X3B1VOXEql0@public.gmane.org>

Currently glibc2 doesn't have setproctitle(3), so several userland
daemons attempt to emulate it by doing some brutal stack modifications.
This works most of the time, but it has problems. For example:

 % ps -ef |grep avahi-daemon
 avahi     1679     1  0 09:20 ?        00:00:00 avahi-daemon: running [kosadesk.local]

 # cat /proc/1679/cmdline
 avahi-daemon: running [kosadesk.local]

This looks good, but the process has also overwritten its environment
area and made the environ file useless:

 # cat /proc/1679/environ
 adesk.local]

Another problem is that the process title length is limited by the size of
the environment. Security conscious people try to avoid potential information
leaks by clearing most of the environment before running a daemon:

 # env - MINIMUM_NEEDED_VAR=foo /path/to/daemon

The resulting environment size may be too small to fit the wanted process
titles.

This patch makes it possible for userspace to implement setproctitle()
cleanly. It adds a new PR_SET_PROCTITLE_AREA option for prctl(), which
updates task's mm_struct->arg_start and arg_end to the given area.

 test_setproctitle.c
 ================================================
 #include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <sys/prctl.h>

 #define ERR(str) (perror(str), exit(1))

 void settitle(char* title){
         int err;

         err = prctl(34, title, strlen(title)+1);
         if (err < 0)
                 ERR("prctl ");
 }

 void main(void){
         long i;
         char buf[1024];

         for (i = 0; i < 10000000000LL; i++){
                 sprintf(buf, "loooooooooooooooooooooooong string %d",i);
                 settitle(buf);
         }
 }
 ==================================================

Cc: Bryan Donlan <bdonlan-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Cc: Ulrich Drepper <drepper-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Signed-off-by: KOSAKI Motohiro <kosaki.motohiro-+CUm20s59erQFUHtdCDX3A@public.gmane.org>
Signed-off-by: Timo Sirainen <tss-X3B1VOXEql0@public.gmane.org>
---
 fs/proc/base.c           |   31 ++++++++++++++++++++++---------
 include/linux/mm_types.h |    2 ++
 include/linux/prctl.h    |    3 +++
 kernel/fork.c            |    1 +
 kernel/sys.c             |   22 ++++++++++++++++++++++
 5 files changed, 50 insertions(+), 9 deletions(-)

diff --git a/fs/proc/base.c b/fs/proc/base.c
index 837469a..ac800b4 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -255,32 +255,45 @@ 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);
+
 	if (!mm)
 		goto out;
+
+	/* The process was not constructed yet? */
 	if (!mm->arg_end)
 		goto out_mm;	/* Shh! No looking before we're done */
 
- 	len = mm->arg_end - mm->arg_start;
- 
+	mutex_lock(&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)
+		/* prctl(PR_SET_PROCTITLE_AREA) used */
+		goto out_unlock;
 
-	// If the nul at the end of args has been overwritten, then
-	// assume application is using setproctitle(3).
+	/*
+	 * If the nul at the end of args has been overwritten, then assume
+	 * application is using sendmail's SPT_REUSEARGV style argv override.
+	 */
 	if (res > 0 && buffer[res-1] != '\0' && len < PAGE_SIZE) {
 		len = strnlen(buffer, res);
-		if (len < res) {
-		    res = len;
-		} else {
+		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 += access_process_vm(task, mm->env_start,
+						 buffer+res, len, 0);
 			res = strnlen(buffer, res);
 		}
 	}
+
+out_unlock:
+	mutex_unlock(&mm->arg_lock);
+
 out_mm:
 	mmput(mm);
 out:
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 84a524a..3e2a346 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -12,6 +12,7 @@
 #include <linux/completion.h>
 #include <linux/cpumask.h>
 #include <linux/page-debug-flags.h>
+#include <linux/mutex.h>
 #include <asm/page.h>
 #include <asm/mmu.h>
 
@@ -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;
+	struct mutex 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..da47542 100644
--- a/include/linux/prctl.h
+++ b/include/linux/prctl.h
@@ -90,4 +90,7 @@
 
 #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 4c20fff..881a6b4 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);
+	mutex_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..bde6957 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1564,6 +1564,28 @@ 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;
+
+			mutex_lock(&mm->arg_lock);
+			mm->arg_start = addr;
+			mm->arg_end = end;
+			mutex_unlock(&mm->arg_lock);
+
+			return 0;
+		}
 		default:
 			error = -EINVAL;
 			break;
-- 
1.6.2.5

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH v5] Added PR_SET_PROCTITLE_AREA option for prctl()
@ 2009-11-03 16:12         ` Américo Wang
  0 siblings, 0 replies; 14+ messages in thread
From: Américo Wang @ 2009-11-03 16:12 UTC (permalink / raw)
  To: KOSAKI Motohiro
  Cc: Americo Wang, Timo Sirainen, Bryan Donlan, Ulrich Drepper, LKML,
	Andrew Morton, linux-api

On Wed, Nov 04, 2009 at 12:26:44AM +0900, KOSAKI Motohiro wrote:
>> > >+		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;
>> > >+
>> > >+			mutex_lock(&mm->arg_lock);
>> > >+			mm->arg_start = addr;
>> > 
>> > Is this safe? You're assigning a user-space pointer to kernel space...
>> > Don't we need copy_from_user()?
>> 
>> mm->arg_start, arg_end are defined so.
>> Please see current implementation. 

Hmm, yeah.

>> 
>> 
>> > >+			mm->arg_end = addr + len;
>> > 
>> > Since you already have 'end', no need to caculate this again. :)
>> 
>> Good catch :)
>> 
>> 
>
>Fixed.
>
>
>ChangeLog
>  v4 -> v5
>    - nit: kill duplicate calculation in prctl()
>  v3 -> v4
>    - Use mutex instead seq_lock.
>
>========================================
>
>Subject: [PATCH v5] Added PR_SET_PROCTITLE_AREA option for prctl()
>From: Timo Sirainen <tss@iki.fi>
>
>Currently glibc2 doesn't have setproctitle(3), so several userland
>daemons attempt to emulate it by doing some brutal stack modifications.
>This works most of the time, but it has problems. For example:
>
> % ps -ef |grep avahi-daemon
> avahi     1679     1  0 09:20 ?        00:00:00 avahi-daemon: running [kosadesk.local]
>
> # cat /proc/1679/cmdline
> avahi-daemon: running [kosadesk.local]
>
>This looks good, but the process has also overwritten its environment
>area and made the environ file useless:
>
> # cat /proc/1679/environ
> adesk.local]
>
>Another problem is that the process title length is limited by the size of
>the environment. Security conscious people try to avoid potential information
>leaks by clearing most of the environment before running a daemon:
>
> # env - MINIMUM_NEEDED_VAR=foo /path/to/daemon
>
>The resulting environment size may be too small to fit the wanted process
>titles.
>
>This patch makes it possible for userspace to implement setproctitle()
>cleanly. It adds a new PR_SET_PROCTITLE_AREA option for prctl(), which
>updates task's mm_struct->arg_start and arg_end to the given area.
>
> test_setproctitle.c
> ================================================
> #include <string.h>
> #include <stdlib.h>
> #include <unistd.h>
> #include <stdio.h>
> #include <sys/prctl.h>
>
> #define ERR(str) (perror(str), exit(1))
>
> void settitle(char* title){
>         int err;
>
>         err = prctl(34, title, strlen(title)+1);
>         if (err < 0)
>                 ERR("prctl ");
> }
>
> void main(void){
>         long i;
>         char buf[1024];
>
>         for (i = 0; i < 10000000000LL; i++){
>                 sprintf(buf, "loooooooooooooooooooooooong string %d",i);
>                 settitle(buf);
>         }
> }
> ==================================================
>
>Cc: Bryan Donlan <bdonlan@gmail.com>
>Cc: Ulrich Drepper <drepper@redhat.com>
>Signed-off-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
>Signed-off-by: Timo Sirainen <tss@iki.fi>


This version looks good,

Reviewed-by: WANG Cong <xiyou.wangcong@gmail.com>

Thanks!

>---
> fs/proc/base.c           |   31 ++++++++++++++++++++++---------
> include/linux/mm_types.h |    2 ++
> include/linux/prctl.h    |    3 +++
> kernel/fork.c            |    1 +
> kernel/sys.c             |   22 ++++++++++++++++++++++
> 5 files changed, 50 insertions(+), 9 deletions(-)
>
>diff --git a/fs/proc/base.c b/fs/proc/base.c
>index 837469a..ac800b4 100644
>--- a/fs/proc/base.c
>+++ b/fs/proc/base.c
>@@ -255,32 +255,45 @@ 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);
>+
> 	if (!mm)
> 		goto out;
>+
>+	/* The process was not constructed yet? */
> 	if (!mm->arg_end)
> 		goto out_mm;	/* Shh! No looking before we're done */
> 
>- 	len = mm->arg_end - mm->arg_start;
>- 
>+	mutex_lock(&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)
>+		/* prctl(PR_SET_PROCTITLE_AREA) used */
>+		goto out_unlock;
> 
>-	// If the nul at the end of args has been overwritten, then
>-	// assume application is using setproctitle(3).
>+	/*
>+	 * If the nul at the end of args has been overwritten, then assume
>+	 * application is using sendmail's SPT_REUSEARGV style argv override.
>+	 */
> 	if (res > 0 && buffer[res-1] != '\0' && len < PAGE_SIZE) {
> 		len = strnlen(buffer, res);
>-		if (len < res) {
>-		    res = len;
>-		} else {
>+		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 += access_process_vm(task, mm->env_start,
>+						 buffer+res, len, 0);
> 			res = strnlen(buffer, res);
> 		}
> 	}
>+
>+out_unlock:
>+	mutex_unlock(&mm->arg_lock);
>+
> out_mm:
> 	mmput(mm);
> out:
>diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
>index 84a524a..3e2a346 100644
>--- a/include/linux/mm_types.h
>+++ b/include/linux/mm_types.h
>@@ -12,6 +12,7 @@
> #include <linux/completion.h>
> #include <linux/cpumask.h>
> #include <linux/page-debug-flags.h>
>+#include <linux/mutex.h>
> #include <asm/page.h>
> #include <asm/mmu.h>
> 
>@@ -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;
>+	struct mutex 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..da47542 100644
>--- a/include/linux/prctl.h
>+++ b/include/linux/prctl.h
>@@ -90,4 +90,7 @@
> 
> #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 4c20fff..881a6b4 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);
>+	mutex_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..bde6957 100644
>--- a/kernel/sys.c
>+++ b/kernel/sys.c
>@@ -1564,6 +1564,28 @@ 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;
>+
>+			mutex_lock(&mm->arg_lock);
>+			mm->arg_start = addr;
>+			mm->arg_end = end;
>+			mutex_unlock(&mm->arg_lock);
>+
>+			return 0;
>+		}
> 		default:
> 			error = -EINVAL;
> 			break;
>-- 
>1.6.2.5
>
>
>
>

-- 
Live like a child, think like the god.
 

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v5] Added PR_SET_PROCTITLE_AREA option for prctl()
@ 2009-11-03 16:12         ` Américo Wang
  0 siblings, 0 replies; 14+ messages in thread
From: Américo Wang @ 2009-11-03 16:12 UTC (permalink / raw)
  To: KOSAKI Motohiro
  Cc: Americo Wang, Timo Sirainen, Bryan Donlan, Ulrich Drepper, LKML,
	Andrew Morton, linux-api-u79uwXL29TY76Z2rM5mHXA

On Wed, Nov 04, 2009 at 12:26:44AM +0900, KOSAKI Motohiro wrote:
>> > >+		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;
>> > >+
>> > >+			mutex_lock(&mm->arg_lock);
>> > >+			mm->arg_start = addr;
>> > 
>> > Is this safe? You're assigning a user-space pointer to kernel space...
>> > Don't we need copy_from_user()?
>> 
>> mm->arg_start, arg_end are defined so.
>> Please see current implementation. 

Hmm, yeah.

>> 
>> 
>> > >+			mm->arg_end = addr + len;
>> > 
>> > Since you already have 'end', no need to caculate this again. :)
>> 
>> Good catch :)
>> 
>> 
>
>Fixed.
>
>
>ChangeLog
>  v4 -> v5
>    - nit: kill duplicate calculation in prctl()
>  v3 -> v4
>    - Use mutex instead seq_lock.
>
>========================================
>
>Subject: [PATCH v5] Added PR_SET_PROCTITLE_AREA option for prctl()
>From: Timo Sirainen <tss-X3B1VOXEql0@public.gmane.org>
>
>Currently glibc2 doesn't have setproctitle(3), so several userland
>daemons attempt to emulate it by doing some brutal stack modifications.
>This works most of the time, but it has problems. For example:
>
> % ps -ef |grep avahi-daemon
> avahi     1679     1  0 09:20 ?        00:00:00 avahi-daemon: running [kosadesk.local]
>
> # cat /proc/1679/cmdline
> avahi-daemon: running [kosadesk.local]
>
>This looks good, but the process has also overwritten its environment
>area and made the environ file useless:
>
> # cat /proc/1679/environ
> adesk.local]
>
>Another problem is that the process title length is limited by the size of
>the environment. Security conscious people try to avoid potential information
>leaks by clearing most of the environment before running a daemon:
>
> # env - MINIMUM_NEEDED_VAR=foo /path/to/daemon
>
>The resulting environment size may be too small to fit the wanted process
>titles.
>
>This patch makes it possible for userspace to implement setproctitle()
>cleanly. It adds a new PR_SET_PROCTITLE_AREA option for prctl(), which
>updates task's mm_struct->arg_start and arg_end to the given area.
>
> test_setproctitle.c
> ================================================
> #include <string.h>
> #include <stdlib.h>
> #include <unistd.h>
> #include <stdio.h>
> #include <sys/prctl.h>
>
> #define ERR(str) (perror(str), exit(1))
>
> void settitle(char* title){
>         int err;
>
>         err = prctl(34, title, strlen(title)+1);
>         if (err < 0)
>                 ERR("prctl ");
> }
>
> void main(void){
>         long i;
>         char buf[1024];
>
>         for (i = 0; i < 10000000000LL; i++){
>                 sprintf(buf, "loooooooooooooooooooooooong string %d",i);
>                 settitle(buf);
>         }
> }
> ==================================================
>
>Cc: Bryan Donlan <bdonlan-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>Cc: Ulrich Drepper <drepper-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>Signed-off-by: KOSAKI Motohiro <kosaki.motohiro-+CUm20s59erQFUHtdCDX3A@public.gmane.org>
>Signed-off-by: Timo Sirainen <tss-X3B1VOXEql0@public.gmane.org>


This version looks good,

Reviewed-by: WANG Cong <xiyou.wangcong-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

Thanks!

>---
> fs/proc/base.c           |   31 ++++++++++++++++++++++---------
> include/linux/mm_types.h |    2 ++
> include/linux/prctl.h    |    3 +++
> kernel/fork.c            |    1 +
> kernel/sys.c             |   22 ++++++++++++++++++++++
> 5 files changed, 50 insertions(+), 9 deletions(-)
>
>diff --git a/fs/proc/base.c b/fs/proc/base.c
>index 837469a..ac800b4 100644
>--- a/fs/proc/base.c
>+++ b/fs/proc/base.c
>@@ -255,32 +255,45 @@ 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);
>+
> 	if (!mm)
> 		goto out;
>+
>+	/* The process was not constructed yet? */
> 	if (!mm->arg_end)
> 		goto out_mm;	/* Shh! No looking before we're done */
> 
>- 	len = mm->arg_end - mm->arg_start;
>- 
>+	mutex_lock(&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)
>+		/* prctl(PR_SET_PROCTITLE_AREA) used */
>+		goto out_unlock;
> 
>-	// If the nul at the end of args has been overwritten, then
>-	// assume application is using setproctitle(3).
>+	/*
>+	 * If the nul at the end of args has been overwritten, then assume
>+	 * application is using sendmail's SPT_REUSEARGV style argv override.
>+	 */
> 	if (res > 0 && buffer[res-1] != '\0' && len < PAGE_SIZE) {
> 		len = strnlen(buffer, res);
>-		if (len < res) {
>-		    res = len;
>-		} else {
>+		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 += access_process_vm(task, mm->env_start,
>+						 buffer+res, len, 0);
> 			res = strnlen(buffer, res);
> 		}
> 	}
>+
>+out_unlock:
>+	mutex_unlock(&mm->arg_lock);
>+
> out_mm:
> 	mmput(mm);
> out:
>diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
>index 84a524a..3e2a346 100644
>--- a/include/linux/mm_types.h
>+++ b/include/linux/mm_types.h
>@@ -12,6 +12,7 @@
> #include <linux/completion.h>
> #include <linux/cpumask.h>
> #include <linux/page-debug-flags.h>
>+#include <linux/mutex.h>
> #include <asm/page.h>
> #include <asm/mmu.h>
> 
>@@ -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;
>+	struct mutex 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..da47542 100644
>--- a/include/linux/prctl.h
>+++ b/include/linux/prctl.h
>@@ -90,4 +90,7 @@
> 
> #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 4c20fff..881a6b4 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);
>+	mutex_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..bde6957 100644
>--- a/kernel/sys.c
>+++ b/kernel/sys.c
>@@ -1564,6 +1564,28 @@ 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;
>+
>+			mutex_lock(&mm->arg_lock);
>+			mm->arg_start = addr;
>+			mm->arg_end = end;
>+			mutex_unlock(&mm->arg_lock);
>+
>+			return 0;
>+		}
> 		default:
> 			error = -EINVAL;
> 			break;
>-- 
>1.6.2.5
>
>
>
>

-- 
Live like a child, think like the god.
 

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v5] Added PR_SET_PROCTITLE_AREA option for prctl()
  2009-11-03 15:26       ` KOSAKI Motohiro
  (?)
  (?)
@ 2009-11-09 22:47       ` Andrew Morton
  2009-11-10  0:00           ` Bryan Donlan
  -1 siblings, 1 reply; 14+ messages in thread
From: Andrew Morton @ 2009-11-09 22:47 UTC (permalink / raw)
  To: KOSAKI Motohiro
  Cc: Americo Wang, Timo Sirainen, Bryan Donlan, Ulrich Drepper, LKML,
	linux-api

On Wed, 4 Nov 2009 00:26:44 +0900 (JST)
KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com> wrote:
> ========================================
> 
> Subject: [PATCH v5] Added PR_SET_PROCTITLE_AREA option for prctl()
> From: Timo Sirainen <tss@iki.fi>
> 
> Currently glibc2 doesn't have setproctitle(3), so several userland
> daemons attempt to emulate it by doing some brutal stack modifications.
> This works most of the time, but it has problems. For example:
> 
>  % ps -ef |grep avahi-daemon
>  avahi     1679     1  0 09:20 ?        00:00:00 avahi-daemon: running [kosadesk.local]
> 
>  # cat /proc/1679/cmdline
>  avahi-daemon: running [kosadesk.local]
> 
> This looks good, but the process has also overwritten its environment
> area and made the environ file useless:
> 
>  # cat /proc/1679/environ
>  adesk.local]
> 
> Another problem is that the process title length is limited by the size of
> the environment. Security conscious people try to avoid potential information
> leaks by clearing most of the environment before running a daemon:
> 
>  # env - MINIMUM_NEEDED_VAR=foo /path/to/daemon
> 
> The resulting environment size may be too small to fit the wanted process
> titles.
> 
> This patch makes it possible for userspace to implement setproctitle()
> cleanly. It adds a new PR_SET_PROCTITLE_AREA option for prctl(), which
> updates task's mm_struct->arg_start and arg_end to the given area.
> 
>  test_setproctitle.c
>  ================================================
>  #include <string.h>
>  #include <stdlib.h>
>  #include <unistd.h>
>  #include <stdio.h>
>  #include <sys/prctl.h>
> 
>  #define ERR(str) (perror(str), exit(1))
> 
>  void settitle(char* title){
>          int err;
> 
>          err = prctl(34, title, strlen(title)+1);
>          if (err < 0)
>                  ERR("prctl ");
>  }
> 
>  void main(void){
>          long i;
>          char buf[1024];
> 
>          for (i = 0; i < 10000000000LL; i++){
>                  sprintf(buf, "loooooooooooooooooooooooong string %d",i);
>                  settitle(buf);
>          }
>  }

What happens if userspace unmaps the memory after telling the kernel to
use it?

Will processes which try to read the command line get an error reading
/proc?  If so, do all the commandline-reading programs in the world
handle this in an appropriate fashion?


> diff --git a/fs/proc/base.c b/fs/proc/base.c
> index 837469a..ac800b4 100644
> --- a/fs/proc/base.c
> +++ b/fs/proc/base.c
> @@ -255,32 +255,45 @@ 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);
> +
>  	if (!mm)
>  		goto out;
> +
> +	/* The process was not constructed yet? */
>  	if (!mm->arg_end)
>  		goto out_mm;	/* Shh! No looking before we're done */
>  
> - 	len = mm->arg_end - mm->arg_start;
> - 
> +	mutex_lock(&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)
> +		/* prctl(PR_SET_PROCTITLE_AREA) used */
> +		goto out_unlock;
>  
> -	// If the nul at the end of args has been overwritten, then
> -	// assume application is using setproctitle(3).
> +	/*
> +	 * If the nul at the end of args has been overwritten, then assume
> +	 * application is using sendmail's SPT_REUSEARGV style argv override.
> +	 */
>  	if (res > 0 && buffer[res-1] != '\0' && len < PAGE_SIZE) {
>  		len = strnlen(buffer, res);
> -		if (len < res) {
> -		    res = len;
> -		} else {
> +		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 += access_process_vm(task, mm->env_start,
> +						 buffer+res, len, 0);
>  			res = strnlen(buffer, res);
>  		}
>  	}
> +
> +out_unlock:
> +	mutex_unlock(&mm->arg_lock);
> +
>  out_mm:
>  	mmput(mm);
>  out:
> diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
> index 84a524a..3e2a346 100644
> --- a/include/linux/mm_types.h
> +++ b/include/linux/mm_types.h
> @@ -12,6 +12,7 @@
>  #include <linux/completion.h>
>  #include <linux/cpumask.h>
>  #include <linux/page-debug-flags.h>
> +#include <linux/mutex.h>
>  #include <asm/page.h>
>  #include <asm/mmu.h>
>  
> @@ -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;
> +	struct mutex arg_lock;
>  	unsigned long arg_start, arg_end, env_start, env_end;
>  
>  	unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */

Please document the role of arg_lock with a code comment here.

> diff --git a/include/linux/prctl.h b/include/linux/prctl.h
> index 9311505..da47542 100644
> --- a/include/linux/prctl.h
> +++ b/include/linux/prctl.h
> @@ -90,4 +90,7 @@
>  
>  #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 4c20fff..881a6b4 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);
> +	mutex_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..bde6957 100644
> --- a/kernel/sys.c
> +++ b/kernel/sys.c
> @@ -1564,6 +1564,28 @@ 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;

It's unobvious (to me) why this access_ok() check is here.  If that
wasn't totally dumb of me, please add a comment so the next reader
won't be similarly mystified.


> +			mutex_lock(&mm->arg_lock);
> +			mm->arg_start = addr;
> +			mm->arg_end = end;
> +			mutex_unlock(&mm->arg_lock);
> +
> +			return 0;
> +		}
>  		default:
>  			error = -EINVAL;
>  			break;



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v5] Added PR_SET_PROCTITLE_AREA option for prctl()
@ 2009-11-10  0:00           ` Bryan Donlan
  0 siblings, 0 replies; 14+ messages in thread
From: Bryan Donlan @ 2009-11-10  0:00 UTC (permalink / raw)
  To: Andrew Morton
  Cc: KOSAKI Motohiro, Americo Wang, Timo Sirainen, Ulrich Drepper,
	LKML, linux-api

On Mon, Nov 9, 2009 at 5:47 PM, Andrew Morton <akpm@linux-foundation.org> wrote:

> What happens if userspace unmaps the memory after telling the kernel to
> use it?
>
> Will processes which try to read the command line get an error reading
> /proc?  If so, do all the commandline-reading programs in the world
> handle this in an appropriate fashion?

This case can already occur in the current code; the userspace process
would have to munmap() the top of its stack, but it certainly can do
so if it tries. In any case, access_process_vm() then returns 0
because of the fault, and thus /proc/pid/cmdline is seen to have zero
length. Since a zero-length /proc/pid/cmdline occurs with kernel
threads as well, we know this isn't a problem.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v5] Added PR_SET_PROCTITLE_AREA option for prctl()
@ 2009-11-10  0:00           ` Bryan Donlan
  0 siblings, 0 replies; 14+ messages in thread
From: Bryan Donlan @ 2009-11-10  0:00 UTC (permalink / raw)
  To: Andrew Morton
  Cc: KOSAKI Motohiro, Americo Wang, Timo Sirainen, Ulrich Drepper,
	LKML, linux-api-u79uwXL29TY76Z2rM5mHXA

On Mon, Nov 9, 2009 at 5:47 PM, Andrew Morton <akpm-de/tnXTf+JLsfHDXvbKv3Sm6D+HspMUB@public.gmane.orgg> wrote:

> What happens if userspace unmaps the memory after telling the kernel to
> use it?
>
> Will processes which try to read the command line get an error reading
> /proc?  If so, do all the commandline-reading programs in the world
> handle this in an appropriate fashion?

This case can already occur in the current code; the userspace process
would have to munmap() the top of its stack, but it certainly can do
so if it tries. In any case, access_process_vm() then returns 0
because of the fault, and thus /proc/pid/cmdline is seen to have zero
length. Since a zero-length /proc/pid/cmdline occurs with kernel
threads as well, we know this isn't a problem.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v5] Added PR_SET_PROCTITLE_AREA option for prctl()
  2009-11-10  0:00           ` Bryan Donlan
  (?)
@ 2009-11-10 11:06           ` KOSAKI Motohiro
  -1 siblings, 0 replies; 14+ messages in thread
From: KOSAKI Motohiro @ 2009-11-10 11:06 UTC (permalink / raw)
  To: Bryan Donlan
  Cc: Andrew Morton, Americo Wang, Timo Sirainen, Ulrich Drepper, LKML,
	linux-api

2009/11/10 Bryan Donlan <bdonlan@gmail.com>:
> On Mon, Nov 9, 2009 at 5:47 PM, Andrew Morton <akpm@linux-foundation.org> wrote:
>
>> What happens if userspace unmaps the memory after telling the kernel to
>> use it?
>>
>> Will processes which try to read the command line get an error reading
>> /proc?  If so, do all the commandline-reading programs in the world
>> handle this in an appropriate fashion?
>
> This case can already occur in the current code; the userspace process
> would have to munmap() the top of its stack, but it certainly can do
> so if it tries. In any case, access_process_vm() then returns 0
> because of the fault, and thus /proc/pid/cmdline is seen to have zero
> length. Since a zero-length /proc/pid/cmdline occurs with kernel
> threads as well, we know this isn't a problem.

Plus, ps can read under exiting process. In this case, task->mm is NULL and
proc_pid_cmdline return 0.

procps tools are already NUL safe since long time ago.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v4] Added PR_SET_PROCTITLE_AREA option for prctl()
@ 2009-11-01 12:16 KOSAKI Motohiro
  0 siblings, 0 replies; 14+ messages in thread
From: KOSAKI Motohiro @ 2009-11-01 12:16 UTC (permalink / raw)
  To: Timo Sirainen, Bryan Donlan, Ulrich Drepper, LKML, Andrew Morton
  Cc: kosaki.motohiro-+CUm20s59erQFUHtdCDX3A


ChangeLog
  v3 -> v4
    - Use mutex instead seq_lock as akpm requested.

========================================

From: Timo Sirainen <tss-X3B1VOXEql0@public.gmane.org>

Currently glibc2 doesn't have setproctitle(3), so several userland
daemons attempt to emulate it by doing some brutal stack modifications.
This works most of the time, but it has problems. For example:

 % ps -ef |grep avahi-daemon
 avahi     1679     1  0 09:20 ?        00:00:00 avahi-daemon: running [kosadesk.local]

 # cat /proc/1679/cmdline
 avahi-daemon: running [kosadesk.local]

This looks good, but the process has also overwritten its environment
area and made the environ file useless:

 # cat /proc/1679/environ
 adesk.local]

Another problem is that the process title length is limited by the size of
the environment. Security conscious people try to avoid potential information
leaks by clearing most of the environment before running a daemon:

 # env - MINIMUM_NEEDED_VAR=foo /path/to/daemon

The resulting environment size may be too small to fit the wanted process
titles.

This patch makes it possible for userspace to implement setproctitle()
cleanly. It adds a new PR_SET_PROCTITLE_AREA option for prctl(), which
updates task's mm_struct->arg_start and arg_end to the given area.

 test_setproctitle.c
 ================================================
 #include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <sys/prctl.h>

 #define ERR(str) (perror(str), exit(1))

 void settitle(char* title){
         int err;

         err = prctl(34, title, strlen(title)+1);
         if (err < 0)
                 ERR("prctl ");
 }

 void main(void){
         long i;
         char buf[1024];

         for (i = 0; i < 10000000000LL; i++){
                 sprintf(buf, "loooooooooooooooooooooooong string %d",i);
                 settitle(buf);
         }
 }
 ==================================================

Cc: Bryan Donlan <bdonlan-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Cc: Ulrich Drepper <drepper-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Signed-off-by: KOSAKI Motohiro <kosaki.motohiro-+CUm20s59erQFUHtdCDX3A@public.gmane.org>
Signed-off-by: Timo Sirainen <tss-X3B1VOXEql0@public.gmane.org>
---
 fs/proc/base.c           |   31 ++++++++++++++++++++++---------
 include/linux/mm_types.h |    2 ++
 include/linux/prctl.h    |    3 +++
 kernel/fork.c            |    1 +
 kernel/sys.c             |   22 ++++++++++++++++++++++
 5 files changed, 50 insertions(+), 9 deletions(-)

diff --git a/fs/proc/base.c b/fs/proc/base.c
index 837469a..ac800b4 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -255,32 +255,45 @@ 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);
+
 	if (!mm)
 		goto out;
+
+	/* The process was not constructed yet? */
 	if (!mm->arg_end)
 		goto out_mm;	/* Shh! No looking before we're done */
 
- 	len = mm->arg_end - mm->arg_start;
- 
+	mutex_lock(&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)
+		/* prctl(PR_SET_PROCTITLE_AREA) used */
+		goto out_unlock;
 
-	// If the nul at the end of args has been overwritten, then
-	// assume application is using setproctitle(3).
+	/*
+	 * If the nul at the end of args has been overwritten, then assume
+	 * application is using sendmail's SPT_REUSEARGV style argv override.
+	 */
 	if (res > 0 && buffer[res-1] != '\0' && len < PAGE_SIZE) {
 		len = strnlen(buffer, res);
-		if (len < res) {
-		    res = len;
-		} else {
+		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 += access_process_vm(task, mm->env_start,
+						 buffer+res, len, 0);
 			res = strnlen(buffer, res);
 		}
 	}
+
+out_unlock:
+	mutex_unlock(&mm->arg_lock);
+
 out_mm:
 	mmput(mm);
 out:
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 84a524a..3e2a346 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -12,6 +12,7 @@
 #include <linux/completion.h>
 #include <linux/cpumask.h>
 #include <linux/page-debug-flags.h>
+#include <linux/mutex.h>
 #include <asm/page.h>
 #include <asm/mmu.h>
 
@@ -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;
+	struct mutex 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..da47542 100644
--- a/include/linux/prctl.h
+++ b/include/linux/prctl.h
@@ -90,4 +90,7 @@
 
 #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 4c20fff..881a6b4 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);
+	mutex_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..434ea13 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1564,6 +1564,28 @@ 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;
+
+			mutex_lock(&mm->arg_lock);
+			mm->arg_start = addr;
+			mm->arg_end = addr + len;
+			mutex_unlock(&mm->arg_lock);
+
+			return 0;
+		}
 		default:
 			error = -EINVAL;
 			break;
-- 
1.6.2.5

^ permalink raw reply related	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2009-11-10 11:06 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-11-01 12:16 [PATCH v4] Added PR_SET_PROCTITLE_AREA option for prctl() KOSAKI Motohiro
2009-11-03  9:47 ` Américo Wang
2009-11-03  9:47   ` Américo Wang
2009-11-03 14:07   ` KOSAKI Motohiro
2009-11-03 14:07     ` KOSAKI Motohiro
2009-11-03 15:26     ` [PATCH v5] " KOSAKI Motohiro
2009-11-03 15:26       ` KOSAKI Motohiro
2009-11-03 16:12       ` Américo Wang
2009-11-03 16:12         ` Américo Wang
2009-11-09 22:47       ` Andrew Morton
2009-11-10  0:00         ` Bryan Donlan
2009-11-10  0:00           ` Bryan Donlan
2009-11-10 11:06           ` KOSAKI Motohiro
  -- strict thread matches above, loose matches on Subject: below --
2009-11-01 12:16 [PATCH v4] " KOSAKI Motohiro

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.