Skip to contentSkip to conversation

exec: minor cleanups + minor fix

PATCH Superseded Oleg Nesterov 3 participants 14 messages Aug 1, 2013 – Aug 4, 2013

Subsystems (2)

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore
#0 lore raw

cover exec: minor cleanups + minor fix

Andrew,

By discussion with Zach, please drop

fs-binfmts-add-a-name-field-to-the-binfmt-struct.patch fs-binfmts-better-handling-of-binfmt-loops.patch fs-binfmts-whitespace-fixes-with-scripts-cleanfile.patch

Zach is working on v2.

Meanwhile, can't we cleanup search_binary_handler() a bit? It doesn't look nice imho. And we can certainly cleanup it more (perhaps I'll try tomorrow). The error handling, request_module() logic and even "for (try=0; try<2; try++)" looks horrible imho.

Oleg.

#1 lore raw

PATCH 1/3 exec: introduce exec_binprm() for "depth == 0" code

task_pid_nr_ns() and trace/ptrace code in the middle of the recursive search_binary_handler() looks confusing and imho annoying. We only need this code if "depth == 0", lets add a simple helper which calls search_binary_handler() and does trace_sched_process_exec() + ptrace_event().

The patch also moves the setting of task->did_exec, we need to do this only once.

Note: we can kill either task->did_exec or PF_FORKNOEXEC.

---
 fs/exec.c |   36 ++++++++++++++++++++++--------------
 1 files changed, 22 insertions(+), 14 deletions(-)
fs/exec.c +22 -14
index 9c73def..a9ae4f2 100644
@@ -1373,7 +1373,6 @@ int search_binary_handler(struct linux_binprm *bprm)
 	unsigned int depth = bprm->recursion_depth;
 	int try,retval;
 	struct linux_binfmt *fmt;
-	pid_t old_pid, old_vpid;
 
 	/* This allows 4 levels of binfmt rewrites before failing hard. */
 	if (depth > 5)
@@ -1387,12 +1386,6 @@ int search_binary_handler(struct linux_binprm *bprm)
 	if (retval)
 		return retval;
 
-	/* Need to fetch pid before load_binary changes it */
-	old_pid = current->pid;
-	rcu_read_lock();
-	old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent));
-	rcu_read_unlock();
-
 	retval = -ENOENT;
 	for (try=0; try<2; try++) {
 		read_lock(&binfmt_lock);
@@ -1407,16 +1400,11 @@ int search_binary_handler(struct linux_binprm *bprm)
 			retval = fn(bprm);
 			bprm->recursion_depth = depth;
 			if (retval >= 0) {
-				if (depth == 0) {
-					trace_sched_process_exec(current, old_pid, bprm);
-					ptrace_event(PTRACE_EVENT_EXEC, old_vpid);
-				}
 				put_binfmt(fmt);
 				allow_write_access(bprm->file);
 				if (bprm->file)
 					fput(bprm->file);
 				bprm->file = NULL;
-				current->did_exec = 1;
 				proc_exec_connector(current);
 				return retval;
 			}
@@ -1450,9 +1438,29 @@ int search_binary_handler(struct linux_binprm *bprm)
 	}
 	return retval;
 }
-
 EXPORT_SYMBOL(search_binary_handler);
 
+static int exec_binprm(struct linux_binprm *bprm)
+{
+	pid_t old_pid, old_vpid;
+	int ret;
+
+	/* Need to fetch pid before load_binary changes it */
+	old_pid = current->pid;
+	rcu_read_lock();
+	old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent));
+	rcu_read_unlock();
+
+	ret = search_binary_handler(bprm);
+	if (ret >= 0) {
+		trace_sched_process_exec(current, old_pid, bprm);
+		ptrace_event(PTRACE_EVENT_EXEC, old_vpid);
+		current->did_exec = 1;
+	}
+
+	return ret;
+}
+
 /*
  * sys_execve() executes a new program.
  */
@@ -1541,7 +1549,7 @@ static int do_execve_common(const char *filename,
 	if (retval < 0)
 		goto out;
 
-	retval = search_binary_handler(bprm);
+	retval = exec_binprm(bprm);
 	if (retval < 0)
 		goto out;
 
Signature · 1 line
1.5.5.1
#5 lore raw

PATCH 2/3 exec: kill "int depth" in search_binary_handler()

In reply to Oleg Nesterov

Nobody except search_binary_handler() should touch ->recursion_depth, "int depth" buys nothing but complicates the code, kill it.

Probably we should also kill "fn" and the !NULL check, ->load_binary should be always defined. And it can not go away after read_unlock() or this code is buggy anyway.

---
 fs/exec.c |    9 ++++-----
 1 files changed, 4 insertions(+), 5 deletions(-)
fs/exec.c +4 -5
index a9ae4f2..f32079c 100644
@@ -1370,12 +1370,11 @@ EXPORT_SYMBOL(remove_arg_zero);
  */
 int search_binary_handler(struct linux_binprm *bprm)
 {
-	unsigned int depth = bprm->recursion_depth;
-	int try,retval;
+	int try, retval;
 	struct linux_binfmt *fmt;
 
 	/* This allows 4 levels of binfmt rewrites before failing hard. */
-	if (depth > 5)
+	if (bprm->recursion_depth > 5)
 		return -ELOOP;
 
 	retval = security_bprm_check(bprm);
@@ -1396,9 +1395,9 @@ int search_binary_handler(struct linux_binprm *bprm)
 			if (!try_module_get(fmt->module))
 				continue;
 			read_unlock(&binfmt_lock);
-			bprm->recursion_depth = depth + 1;
+			bprm->recursion_depth++;
 			retval = fn(bprm);
-			bprm->recursion_depth = depth;
+			bprm->recursion_depth--;
 			if (retval >= 0) {
 				put_binfmt(fmt);
 				allow_write_access(bprm->file);
Signature · 1 line
1.5.5.1
#9 lore raw

PATCH 3/3 exec: proc_exec_connector() should be called only once

In reply to Oleg Nesterov

A separate one-liner with the minor fix.

PROC_EVENT_EXEC reports the "exec" event, but this message is sent at least twice if search_binary_handler() is called by ->load_binary() recursively, say, load_script().

Move it to exec_binprm(), this is "depth == 0" code too.

---
 fs/exec.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
fs/exec.c +1 -1
index f32079c..ad7d624 100644
@@ -1404,7 +1404,6 @@ int search_binary_handler(struct linux_binprm *bprm)
 				if (bprm->file)
 					fput(bprm->file);
 				bprm->file = NULL;
-				proc_exec_connector(current);
 				return retval;
 			}
 			read_lock(&binfmt_lock);
@@ -1455,6 +1454,7 @@ static int exec_binprm(struct linux_binprm *bprm)
 		trace_sched_process_exec(current, old_pid, bprm);
 		ptrace_event(PTRACE_EVENT_EXEC, old_vpid);
 		current->did_exec = 1;
+		proc_exec_connector(current);
 	}
 
 	return ret;
Signature · 1 line
1.5.5.1
#11 lore raw
In reply to Oleg Nesterov
Quoted 9 lines · Oleg Nesterov

Andrew,

By discussion with Zach, please drop

fs-binfmts-add-a-name-field-to-the-binfmt-struct.patch fs-binfmts-better-handling-of-binfmt-loops.patch fs-binfmts-whitespace-fixes-with-scripts-cleanfile.patch

Quoted 6 lines

Zach is working on v2.

Meanwhile, can't we cleanup search_binary_handler() a bit? It doesn't look nice imho. And we can certainly cleanup it more (perhaps I'll try tomorrow). The error handling, request_module() logic and even "for (try=0; try<2; try++)" looks horrible imho.

Seriously, I think this should be cleanuped too.

Zach, I am sorry if you already rebased your changes, I'll send a couple more patches today.

Oleg.

#12 lore raw
Quoted 17 lines · Oleg Nesterov
Quoted 9 lines · Oleg Nesterov

Andrew,

By discussion with Zach, please drop

fs-binfmts-add-a-name-field-to-the-binfmt-struct.patch fs-binfmts-better-handling-of-binfmt-loops.patch fs-binfmts-whitespace-fixes-with-scripts-cleanfile.patch

Quoted 6 lines

Zach is working on v2.

Meanwhile, can't we cleanup search_binary_handler() a bit? It doesn't look nice imho. And we can certainly cleanup it more (perhaps I'll try tomorrow). The error handling, request_module() logic and even "for (try=0; try<2; try++)" looks horrible imho.

Quoted 5 lines

Seriously, I think this should be cleanuped too.

Zach, I am sorry if you already rebased your changes, I'll send a couple more patches today.

Don't worry -- I've been holding off on sending v2 for your changes today. It's not much git wrangling to apply your additional patches anyway.

Quoted 2 lines

Oleg.

#6 lore raw

Re: [PATCH 2/3] exec: kill "int depth" in search_binary_handler()

In reply to Oleg Nesterov
Quoted 2 lines · Oleg Nesterov

Nobody except search_binary_handler() should touch ->recursion_depth, "int depth" buys nothing but complicates the code, kill it.

I'd like to see a comment added to binfmts.h's recursion_depth field that reminds people that recursion_depth is for search_binary_handler()'s use only, and a binfmt loader shouldn't touch it. Besides that, yeah, sensible clean up.

-Kees

Signature · 2 lines
Kees Cook
Chrome OS Security
#7 lore raw

PATCH v2 2/3 exec: kill "int depth" in search_binary_handler()

Quoted 9 lines · Kees Cook
Quoted 2 lines · Oleg Nesterov

Nobody except search_binary_handler() should touch ->recursion_depth, "int depth" buys nothing but complicates the code, kill it.

I'd like to see a comment added to binfmts.h's recursion_depth field that reminds people that recursion_depth is for search_binary_handler()'s use only, and a binfmt loader shouldn't touch it.

And this comment probably makes sense even without this change

Quoted 1 line

Besides that, yeah, sensible clean up.

OK, thanks, please see v2. The only change is the comment in .h

------------------------------------------------------------------------------
Subject: [PATCH 1/1] exec: kill "int depth" in search_binary_handler()

Nobody except search_binary_handler() should touch ->recursion_depth, "int depth" buys nothing but complicates the code, kill it.

Probably we should also kill "fn" and the !NULL check, ->load_binary should be always defined. And it can not go away after read_unlock() or this code is buggy anyway.

v2: add the comment about linux_binprm->recursion_depth

---
 fs/exec.c               |    9 ++++-----
 include/linux/binfmts.h |    2 +-
 2 files changed, 5 insertions(+), 6 deletions(-)
fs/exec.c +4 -5
index a9ae4f2..f32079c 100644
@@ -1370,12 +1370,11 @@ EXPORT_SYMBOL(remove_arg_zero);
  */
 int search_binary_handler(struct linux_binprm *bprm)
 {
-	unsigned int depth = bprm->recursion_depth;
-	int try,retval;
+	int try, retval;
 	struct linux_binfmt *fmt;
 
 	/* This allows 4 levels of binfmt rewrites before failing hard. */
-	if (depth > 5)
+	if (bprm->recursion_depth > 5)
 		return -ELOOP;
 
 	retval = security_bprm_check(bprm);
@@ -1396,9 +1395,9 @@ int search_binary_handler(struct linux_binprm *bprm)
 			if (!try_module_get(fmt->module))
 				continue;
 			read_unlock(&binfmt_lock);
-			bprm->recursion_depth = depth + 1;
+			bprm->recursion_depth++;
 			retval = fn(bprm);
-			bprm->recursion_depth = depth;
+			bprm->recursion_depth--;
 			if (retval >= 0) {
 				put_binfmt(fmt);
 				allow_write_access(bprm->file);
include/linux/binfmts.h +1 -1
index 70cf138..e8112ae 100644
@@ -31,7 +31,7 @@ struct linux_binprm {
 #ifdef __alpha__
 	unsigned int taso:1;
 #endif
-	unsigned int recursion_depth;
+	unsigned int recursion_depth; /* only for search_binary_handler() */
 	struct file * file;
 	struct cred *cred;	/* new credentials */
 	int unsafe;		/* how unsafe this exec is (mask of LSM_UNSAFE_*) */
Signature · 1 line
1.5.5.1
#2 lore raw

Re: [PATCH 1/3] exec: introduce exec_binprm() for "depth == 0" code

In reply to Oleg Nesterov
Quoted 72 lines · Oleg Nesterov

task_pid_nr_ns() and trace/ptrace code in the middle of the recursive search_binary_handler() looks confusing and imho annoying. We only need this code if "depth == 0", lets add a simple helper which calls search_binary_handler() and does trace_sched_process_exec() + ptrace_event().

The patch also moves the setting of task->did_exec, we need to do this only once.

Note: we can kill either task->did_exec or PF_FORKNOEXEC.

---
 fs/exec.c |   36 ++++++++++++++++++++++--------------
 1 files changed, 22 insertions(+), 14 deletions(-)
fs/exec.c +7 -13
index 9c73def..a9ae4f2 100644
@@ -1373,7 +1373,6 @@ int search_binary_handler(struct linux_binprm *bprm)
        unsigned int depth = bprm->recursion_depth;
        int try,retval;
        struct linux_binfmt *fmt;
-       pid_t old_pid, old_vpid;
 
        /* This allows 4 levels of binfmt rewrites before failing hard. */
        if (depth > 5)
@@ -1387,12 +1386,6 @@ int search_binary_handler(struct linux_binprm *bprm)
        if (retval)
                return retval;
 
-       /* Need to fetch pid before load_binary changes it */
-       old_pid = current->pid;
-       rcu_read_lock();
-       old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent));
-       rcu_read_unlock();
-
        retval = -ENOENT;
        for (try=0; try<2; try++) {
                read_lock(&binfmt_lock);
@@ -1407,16 +1400,11 @@ int search_binary_handler(struct linux_binprm *bprm)
                        retval = fn(bprm);
                        bprm->recursion_depth = depth;
                        if (retval >= 0) {
-                               if (depth == 0) {
-                                       trace_sched_process_exec(current, old_pid, bprm);
-                                       ptrace_event(PTRACE_EVENT_EXEC, old_vpid);
-                               }
                                put_binfmt(fmt);
                                allow_write_access(bprm->file);
                                if (bprm->file)
                                        fput(bprm->file);
                                bprm->file = NULL;
-                               current->did_exec = 1;
                                proc_exec_connector(current);
                                return retval;
                        }
@@ -1450,9 +1438,29 @@ int search_binary_handler(struct linux_binprm *bprm)
        }
        return retval;
 }
-
 EXPORT_SYMBOL(search_binary_handler);
 
+static int exec_binprm(struct linux_binprm *bprm)
+{
+       pid_t old_pid, old_vpid;
+       int ret;
+
+       /* Need to fetch pid before load_binary changes it */
+       old_pid = current->pid;

fs/exec.c int search_binary_handler(struct linux_binprm *bprm) · line 1459

Quoted 10 lines
fs/exec.c +10 -0
+       rcu_read_lock();
+       old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent));
+       rcu_read_unlock();
+
+       ret = search_binary_handler(bprm);
+       if (ret >= 0) {
+               trace_sched_process_exec(current, old_pid, bprm);
+               ptrace_event(PTRACE_EVENT_EXEC, old_vpid);
+               current->did_exec = 1;
+       }

Cleanup looks good. One idea here, though: this could be made more pretty by doing:

if (ret < 0)

    return ret;

to avoid the indentation for the "expected" code path.

-Kees

Quoted 19 lines
fs/exec.c +5 -2
+
+       return ret;
+}
+
 /*
  * sys_execve() executes a new program.
  */
@@ -1541,7 +1549,7 @@ static int do_execve_common(const char *filename,
        if (retval < 0)
                goto out;
 
-       retval = search_binary_handler(bprm);
+       retval = exec_binprm(bprm);
        if (retval < 0)
                goto out;
 
--

1.5.5.1

Signature · 2 lines
Kees Cook
Chrome OS Security
Acked-by on v1 3/3
#10 lore raw

Re: [PATCH 3/3] exec: proc_exec_connector() should be called only once

In reply to Oleg Nesterov
Quoted 9 lines · Oleg Nesterov

A separate one-liner with the minor fix.

PROC_EVENT_EXEC reports the "exec" event, but this message is sent at least twice if search_binary_handler() is called by ->load_binary() recursively, say, load_script().

Move it to exec_binprm(), this is "depth == 0" code too.

Yeah, looks right. I almost mentioned this while reading the other cleanup patch, but then you fixed it too. :)

Thanks,

-Kees

Signature · 2 lines
Kees Cook
Chrome OS Security
Acked-by on v2 2/3
#8 lore raw

Re: [PATCH v2 2/3] exec: kill "int depth" in search_binary_handler()

In reply to Oleg Nesterov
Quoted 6 lines · Oleg Nesterov
Quoted 5 lines · Kees Cook
Quoted 2 lines · Oleg Nesterov

Nobody except search_binary_handler() should touch ->recursion_depth, "int depth" buys nothing but complicates the code, kill it.

Quoted 6 lines
Quoted 4 lines

I'd like to see a comment added to binfmts.h's recursion_depth field that reminds people that recursion_depth is for search_binary_handler()'s use only, and a binfmt loader shouldn't touch it.

And this comment probably makes sense even without this change

Yeah totally agreed -- I should have added this when I reorganized the depth handling earlier. :)

Quoted 10 lines
Quoted 1 line

Besides that, yeah, sensible clean up.

OK, thanks, please see v2. The only change is the comment in .h

------------------------------------------------------------------------------
Subject: [PATCH 1/1] exec: kill "int depth" in search_binary_handler()

Nobody except search_binary_handler() should touch ->recursion_depth, "int depth" buys nothing but complicates the code, kill it.

Quoted 7 lines

Probably we should also kill "fn" and the !NULL check, ->load_binary should be always defined. And it can not go away after read_unlock() or this code is buggy anyway.

v2: add the comment about linux_binprm->recursion_depth

Thanks,

-Kees

Quoted 53 lines
---
 fs/exec.c               |    9 ++++-----
 include/linux/binfmts.h |    2 +-
 2 files changed, 5 insertions(+), 6 deletions(-)
fs/exec.c +4 -5
index a9ae4f2..f32079c 100644
@@ -1370,12 +1370,11 @@ EXPORT_SYMBOL(remove_arg_zero);
  */
 int search_binary_handler(struct linux_binprm *bprm)
 {
-       unsigned int depth = bprm->recursion_depth;
-       int try,retval;
+       int try, retval;
        struct linux_binfmt *fmt;
 
        /* This allows 4 levels of binfmt rewrites before failing hard. */
-       if (depth > 5)
+       if (bprm->recursion_depth > 5)
                return -ELOOP;
 
        retval = security_bprm_check(bprm);
@@ -1396,9 +1395,9 @@ int search_binary_handler(struct linux_binprm *bprm)
                        if (!try_module_get(fmt->module))
                                continue;
                        read_unlock(&binfmt_lock);
-                       bprm->recursion_depth = depth + 1;
+                       bprm->recursion_depth++;
                        retval = fn(bprm);
-                       bprm->recursion_depth = depth;
+                       bprm->recursion_depth--;
                        if (retval >= 0) {
                                put_binfmt(fmt);
                                allow_write_access(bprm->file);
include/linux/binfmts.h +1 -2
index 70cf138..e8112ae 100644
@@ -31,7 +31,7 @@ struct linux_binprm {
 #ifdef __alpha__
        unsigned int taso:1;
 #endif
-       unsigned int recursion_depth;
+       unsigned int recursion_depth; /* only for search_binary_handler() */
        struct file * file;
        struct cred *cred;      /* new credentials */
        int unsafe;             /* how unsafe this exec is (mask of LSM_UNSAFE_*) */
--

1.5.5.1

Signature · 2 lines
Kees Cook
Chrome OS Security
#3 lore raw

Re: [PATCH 1/3] exec: introduce exec_binprm() for "depth == 0" code

In reply to Kees Cook
Quoted 23 lines · Kees Cook
Quoted 7 lines · Oleg Nesterov
fs/exec.c +7 -0
+static int exec_binprm(struct linux_binprm *bprm)
+{
+       pid_t old_pid, old_vpid;
+       int ret;
+
+       /* Need to fetch pid before load_binary changes it */
+       old_pid = current->pid;

fs/exec.c int search_binary_handler(struct linux_binprm *bprm) · line 1459

Quoted 10 lines
fs/exec.c +10 -0
+       rcu_read_lock();
+       old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent));
+       rcu_read_unlock();
+
+       ret = search_binary_handler(bprm);
+       if (ret >= 0) {
+               trace_sched_process_exec(current, old_pid, bprm);
+               ptrace_event(PTRACE_EVENT_EXEC, old_vpid);
+               current->did_exec = 1;
+       }

Cleanup looks good. One idea here, though: this could be made more pretty by doing:

Quoted 4 lines

if (ret < 0)

    return ret;

to avoid the indentation for the "expected" code path.

Well, I do not reallt mind. But this "if" block is simple and small, we do we need another "return" ?

To me the code looks more readable this way, but I can redo/resend.

Oleg.

#13 lore raw
In reply to Oleg Nesterov

Andrew,

please ignore this and the next series, I'll resend everything with a couple of cosmetic changes.

Plus I believe audit_bprm() is not right too, this needs another patch.

Quoted 17 lines · Oleg Nesterov

Andrew,

By discussion with Zach, please drop

fs-binfmts-add-a-name-field-to-the-binfmt-struct.patch fs-binfmts-better-handling-of-binfmt-loops.patch fs-binfmts-whitespace-fixes-with-scripts-cleanfile.patch

Zach is working on v2.

Meanwhile, can't we cleanup search_binary_handler() a bit? It doesn't look nice imho. And we can certainly cleanup it more (perhaps I'll try tomorrow). The error handling, request_module() logic and even "for (try=0; try<2; try++)" looks horrible imho.

Oleg.

Acked-by
#4 lore raw

Re: [PATCH 1/3] exec: introduce exec_binprm() for "depth == 0" code

In reply to Oleg Nesterov
Quoted 27 lines · Oleg Nesterov
Quoted 26 lines · Kees Cook
Quoted 7 lines · Oleg Nesterov
fs/exec.c +7 -0
+static int exec_binprm(struct linux_binprm *bprm)
+{
+       pid_t old_pid, old_vpid;
+       int ret;
+
+       /* Need to fetch pid before load_binary changes it */
+       old_pid = current->pid;

fs/exec.c int search_binary_handler(struct linux_binprm *bprm) · line 1459

Quoted 10 lines
fs/exec.c +10 -0
+       rcu_read_lock();
+       old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent));
+       rcu_read_unlock();
+
+       ret = search_binary_handler(bprm);
+       if (ret >= 0) {
+               trace_sched_process_exec(current, old_pid, bprm);
+               ptrace_event(PTRACE_EVENT_EXEC, old_vpid);
+               current->did_exec = 1;
+       }

Cleanup looks good. One idea here, though: this could be made more pretty by doing:

if (ret < 0)

    return ret;
Quoted 6 lines
Quoted 1 line

to avoid the indentation for the "expected" code path.

Well, I do not reallt mind. But this "if" block is simple and small, we do we need another "return" ?

To me the code looks more readable this way, but I can redo/resend.

Cool, that's fine how it is. It was just a style suggestion. :)

Thanks!

-Kees

Signature · 2 lines
Kees Cook
Chrome OS Security