Dave Jones 03fb1ee
From ba1b23d05259e31d30a78017cdfbc010dcb08aa6 Mon Sep 17 00:00:00 2001
4703e15
From: Kees Cook <keescook@chromium.org>
4703e15
Date: Mon, 26 Nov 2012 09:02:11 -0500
4703e15
Subject: [PATCH 2/2] exec: use -ELOOP for max recursion depth
4703e15
4703e15
To avoid an explosion of request_module calls on a chain of abusive
4703e15
scripts, fail maximum recursion with -ELOOP instead of -ENOEXEC. As soon
4703e15
as maximum recursion depth is hit, the error will fail all the way back
4703e15
up the chain, aborting immediately.
4703e15
4703e15
This also has the side-effect of stopping the user's shell from attempting
4703e15
to reexecute the top-level file as a shell script. As seen in the
4703e15
dash source:
4703e15
4703e15
        if (cmd != path_bshell && errno == ENOEXEC) {
4703e15
                *argv-- = cmd;
4703e15
                *argv = cmd = path_bshell;
4703e15
                goto repeat;
4703e15
        }
4703e15
4703e15
The above logic was designed for running scripts automatically that lacked
4703e15
the "#!" header, not to re-try failed recursion. On a legitimate -ENOEXEC,
4703e15
things continue to behave as the shell expects.
4703e15
4703e15
Additionally, when tracking recursion, the binfmt handlers should not be
4703e15
involved. The recursion being tracked is the depth of calls through
4703e15
search_binary_handler(), so that function should be exclusively responsible
4703e15
for tracking the depth.
4703e15
4703e15
Signed-off-by: Kees Cook <keescook@chromium.org>
4703e15
Cc: halfdog <me@halfdog.net>
4703e15
Cc: P J P <ppandit@redhat.com>
4703e15
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
4703e15
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
4703e15
---
4703e15
 fs/binfmt_em86.c        |  1 -
4703e15
 fs/binfmt_misc.c        |  6 ------
4703e15
 fs/binfmt_script.c      |  4 +---
4703e15
 fs/exec.c               | 10 +++++-----
4703e15
 include/linux/binfmts.h |  2 --
4703e15
 5 files changed, 6 insertions(+), 17 deletions(-)
4703e15
4703e15
diff --git a/fs/binfmt_em86.c b/fs/binfmt_em86.c
4703e15
index 2790c7e..575796a 100644
4703e15
--- a/fs/binfmt_em86.c
4703e15
+++ b/fs/binfmt_em86.c
4703e15
@@ -42,7 +42,6 @@ static int load_em86(struct linux_binprm *bprm,struct pt_regs *regs)
4703e15
 			return -ENOEXEC;
4703e15
 	}
4703e15
 
4703e15
-	bprm->recursion_depth++; /* Well, the bang-shell is implicit... */
4703e15
 	allow_write_access(bprm->file);
4703e15
 	fput(bprm->file);
4703e15
 	bprm->file = NULL;
4703e15
diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
4703e15
index 772428d..f0f1a06 100644
4703e15
--- a/fs/binfmt_misc.c
4703e15
+++ b/fs/binfmt_misc.c
4703e15
@@ -117,10 +117,6 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
4703e15
 	if (!enabled)
4703e15
 		goto _ret;
4703e15
 
4703e15
-	retval = -ENOEXEC;
4703e15
-	if (bprm->recursion_depth > BINPRM_MAX_RECURSION)
4703e15
-		goto _ret;
4703e15
-
4703e15
 	/* to keep locking time low, we copy the interpreter string */
4703e15
 	read_lock(&entries_lock);
4703e15
 	fmt = check_file(bprm);
4703e15
@@ -200,8 +196,6 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
4703e15
 	if (retval < 0)
4703e15
 		goto _error;
4703e15
 
4703e15
-	bprm->recursion_depth++;
4703e15
-
4703e15
 	retval = search_binary_handler (bprm, regs);
4703e15
 	if (retval < 0)
4703e15
 		goto _error;
4703e15
diff --git a/fs/binfmt_script.c b/fs/binfmt_script.c
4703e15
index df49d48..8ae4be1 100644
4703e15
--- a/fs/binfmt_script.c
4703e15
+++ b/fs/binfmt_script.c
4703e15
@@ -22,15 +22,13 @@ static int load_script(struct linux_binprm *bprm,struct pt_regs *regs)
4703e15
 	char interp[BINPRM_BUF_SIZE];
4703e15
 	int retval;
4703e15
 
4703e15
-	if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!') ||
4703e15
-	    (bprm->recursion_depth > BINPRM_MAX_RECURSION))
4703e15
+	if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))
4703e15
 		return -ENOEXEC;
4703e15
 	/*
4703e15
 	 * This section does the #! interpretation.
4703e15
 	 * Sorta complicated, but hopefully it will work.  -TYT
4703e15
 	 */
4703e15
 
4703e15
-	bprm->recursion_depth++;
4703e15
 	allow_write_access(bprm->file);
4703e15
 	fput(bprm->file);
4703e15
 	bprm->file = NULL;
4703e15
diff --git a/fs/exec.c b/fs/exec.c
Dave Jones 03fb1ee
index c6e6de4..85c1f9e 100644
4703e15
--- a/fs/exec.c
4703e15
+++ b/fs/exec.c
Dave Jones 03fb1ee
@@ -1371,6 +1371,10 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
4703e15
 	struct linux_binfmt *fmt;
4703e15
 	pid_t old_pid, old_vpid;
4703e15
 
4703e15
+	/* This allows 4 levels of binfmt rewrites before failing hard. */
4703e15
+	if (depth > 5)
4703e15
+		return -ELOOP;
4703e15
+
4703e15
 	retval = security_bprm_check(bprm);
4703e15
 	if (retval)
4703e15
 		return retval;
Dave Jones 03fb1ee
@@ -1395,12 +1399,8 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
4703e15
 			if (!try_module_get(fmt->module))
4703e15
 				continue;
4703e15
 			read_unlock(&binfmt_lock);
4703e15
+			bprm->recursion_depth = depth + 1;
4703e15
 			retval = fn(bprm, regs);
4703e15
-			/*
4703e15
-			 * Restore the depth counter to its starting value
4703e15
-			 * in this call, so we don't have to rely on every
4703e15
-			 * load_binary function to restore it on return.
4703e15
-			 */
4703e15
 			bprm->recursion_depth = depth;
4703e15
 			if (retval >= 0) {
4703e15
 				if (depth == 0) {
4703e15
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
Dave Jones 03fb1ee
index de0628e..54135f6 100644
4703e15
--- a/include/linux/binfmts.h
4703e15
+++ b/include/linux/binfmts.h
Dave Jones 03fb1ee
@@ -54,8 +54,6 @@ struct linux_binprm {
4703e15
 #define BINPRM_FLAGS_EXECFD_BIT 1
4703e15
 #define BINPRM_FLAGS_EXECFD (1 << BINPRM_FLAGS_EXECFD_BIT)
4703e15
 
4703e15
-#define BINPRM_MAX_RECURSION 4
4703e15
-
4703e15
 /* Function parameter for binfmt->coredump */
4703e15
 struct coredump_params {
Dave Jones 03fb1ee
 	siginfo_t *siginfo;
4703e15
-- 
4703e15
1.8.0
4703e15