Skip to content

Commit bc2bf33

Browse files
committed
exec: Remove recursion from search_binary_handler
Recursion in kernel code is generally a bad idea as it can overflow the kernel stack. Recursion in exec also hides that the code is looping and that the loop changes bprm->file. Instead of recursing in search_binary_handler have the methods that would recurse set bprm->interpreter and return 0. Modify exec_binprm to loop when bprm->interpreter is set. Consolidate all of the reassignments of bprm->file in that loop to make it clear what is going on. The structure of the new loop in exec_binprm is that all errors return immediately, while successful completion (ret == 0 && !bprm->interpreter) just breaks out of the loop and runs what exec_bprm has always run upon successful completion. Fail if the an interpreter is being call after execfd has been set. The code has never properly handled an interpreter being called with execfd being set and with reassignments of bprm->file and the assignment of bprm->executable in generic code it has finally become possible to test and fail when if this problematic condition happens. With the reassignments of bprm->file and the assignment of bprm->executable moved into the generic code add a test to see if bprm->executable is being reassigned. In search_binary_handler remove the test for !bprm->file. With all reassignments of bprm->file moved to exec_binprm bprm->file can never be NULL in search_binary_handler. Link: https://lkml.kernel.org/r/[email protected] Acked-by: Linus Torvalds <[email protected]> Reviewed-by: Kees Cook <[email protected]> Signed-off-by: "Eric W. Biederman" <[email protected]>
1 parent b8a61c9 commit bc2bf33

File tree

6 files changed

+43
-55
lines changed

6 files changed

+43
-55
lines changed

arch/alpha/kernel/binfmt_loader.c

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,6 @@ static int load_binary(struct linux_binprm *bprm)
1919
if (bprm->loader)
2020
return -ENOEXEC;
2121

22-
allow_write_access(bprm->file);
23-
fput(bprm->file);
24-
bprm->file = NULL;
25-
2622
loader = bprm->vma->vm_end - sizeof(void *);
2723

2824
file = open_exec("/sbin/loader");
@@ -33,9 +29,9 @@ static int load_binary(struct linux_binprm *bprm)
3329
/* Remember if the application is TASO. */
3430
bprm->taso = eh->ah.entry < 0x100000000UL;
3531

36-
bprm->file = file;
32+
bprm->interpreter = file;
3733
bprm->loader = loader;
38-
return search_binary_handler(bprm);
34+
return 0;
3935
}
4036

4137
static struct linux_binfmt loader_format = {

fs/binfmt_em86.c

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,6 @@ static int load_em86(struct linux_binprm *bprm)
4848
if (bprm->interp_flags & BINPRM_FLAGS_PATH_INACCESSIBLE)
4949
return -ENOENT;
5050

51-
allow_write_access(bprm->file);
52-
fput(bprm->file);
53-
bprm->file = NULL;
54-
5551
/* Unlike in the script case, we don't have to do any hairy
5652
* parsing to find our interpreter... it's hardcoded!
5753
*/
@@ -89,9 +85,8 @@ static int load_em86(struct linux_binprm *bprm)
8985
if (IS_ERR(file))
9086
return PTR_ERR(file);
9187

92-
bprm->file = file;
93-
94-
return search_binary_handler(bprm);
88+
bprm->interpreter = file;
89+
return 0;
9590
}
9691

9792
static struct linux_binfmt em86_format = {

fs/binfmt_misc.c

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -159,18 +159,9 @@ static int load_misc_binary(struct linux_binprm *bprm)
159159
goto ret;
160160
}
161161

162-
if (fmt->flags & MISC_FMT_OPEN_BINARY) {
163-
/* Pass the open binary to the interpreter */
162+
if (fmt->flags & MISC_FMT_OPEN_BINARY)
164163
bprm->have_execfd = 1;
165-
bprm->executable = bprm->file;
166164

167-
allow_write_access(bprm->file);
168-
bprm->file = NULL;
169-
} else {
170-
allow_write_access(bprm->file);
171-
fput(bprm->file);
172-
bprm->file = NULL;
173-
}
174165
/* make argv[1] be the path to the binary */
175166
retval = copy_strings_kernel(1, &bprm->interp, bprm);
176167
if (retval < 0)
@@ -199,14 +190,11 @@ static int load_misc_binary(struct linux_binprm *bprm)
199190
if (IS_ERR(interp_file))
200191
goto ret;
201192

202-
bprm->file = interp_file;
193+
bprm->interpreter = interp_file;
203194
if (fmt->flags & MISC_FMT_CREDENTIALS)
204195
bprm->preserve_creds = 1;
205196

206-
retval = search_binary_handler(bprm);
207-
if (retval < 0)
208-
goto ret;
209-
197+
retval = 0;
210198
ret:
211199
dput(fmt->dentry);
212200
return retval;

fs/binfmt_script.c

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,6 @@ static int load_script(struct linux_binprm *bprm)
9393
if (bprm->interp_flags & BINPRM_FLAGS_PATH_INACCESSIBLE)
9494
return -ENOENT;
9595

96-
/* Release since we are not mapping a binary into memory. */
97-
allow_write_access(bprm->file);
98-
fput(bprm->file);
99-
bprm->file = NULL;
100-
10196
/*
10297
* OK, we've parsed out the interpreter name and
10398
* (optional) argument.
@@ -138,8 +133,8 @@ static int load_script(struct linux_binprm *bprm)
138133
if (IS_ERR(file))
139134
return PTR_ERR(file);
140135

141-
bprm->file = file;
142-
return search_binary_handler(bprm);
136+
bprm->interpreter = file;
137+
return 0;
143138
}
144139

145140
static struct linux_binfmt script_format = {

fs/exec.c

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1710,16 +1710,12 @@ EXPORT_SYMBOL(remove_arg_zero);
17101710
/*
17111711
* cycle the list of binary formats handler, until one recognizes the image
17121712
*/
1713-
int search_binary_handler(struct linux_binprm *bprm)
1713+
static int search_binary_handler(struct linux_binprm *bprm)
17141714
{
17151715
bool need_retry = IS_ENABLED(CONFIG_MODULES);
17161716
struct linux_binfmt *fmt;
17171717
int retval;
17181718

1719-
/* This allows 4 levels of binfmt rewrites before failing hard. */
1720-
if (bprm->recursion_depth > 5)
1721-
return -ELOOP;
1722-
17231719
retval = prepare_binprm(bprm);
17241720
if (retval < 0)
17251721
return retval;
@@ -1736,14 +1732,11 @@ int search_binary_handler(struct linux_binprm *bprm)
17361732
continue;
17371733
read_unlock(&binfmt_lock);
17381734

1739-
bprm->recursion_depth++;
17401735
retval = fmt->load_binary(bprm);
1741-
bprm->recursion_depth--;
17421736

17431737
read_lock(&binfmt_lock);
17441738
put_binfmt(fmt);
1745-
if (bprm->point_of_no_return || !bprm->file ||
1746-
(retval != -ENOEXEC)) {
1739+
if (bprm->point_of_no_return || (retval != -ENOEXEC)) {
17471740
read_unlock(&binfmt_lock);
17481741
return retval;
17491742
}
@@ -1762,28 +1755,50 @@ int search_binary_handler(struct linux_binprm *bprm)
17621755

17631756
return retval;
17641757
}
1765-
EXPORT_SYMBOL(search_binary_handler);
17661758

17671759
static int exec_binprm(struct linux_binprm *bprm)
17681760
{
17691761
pid_t old_pid, old_vpid;
1770-
int ret;
1762+
int ret, depth;
17711763

17721764
/* Need to fetch pid before load_binary changes it */
17731765
old_pid = current->pid;
17741766
rcu_read_lock();
17751767
old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent));
17761768
rcu_read_unlock();
17771769

1778-
ret = search_binary_handler(bprm);
1779-
if (ret >= 0) {
1780-
audit_bprm(bprm);
1781-
trace_sched_process_exec(current, old_pid, bprm);
1782-
ptrace_event(PTRACE_EVENT_EXEC, old_vpid);
1783-
proc_exec_connector(current);
1770+
/* This allows 4 levels of binfmt rewrites before failing hard. */
1771+
for (depth = 0;; depth++) {
1772+
struct file *exec;
1773+
if (depth > 5)
1774+
return -ELOOP;
1775+
1776+
ret = search_binary_handler(bprm);
1777+
if (ret < 0)
1778+
return ret;
1779+
if (!bprm->interpreter)
1780+
break;
1781+
1782+
exec = bprm->file;
1783+
bprm->file = bprm->interpreter;
1784+
bprm->interpreter = NULL;
1785+
1786+
allow_write_access(exec);
1787+
if (unlikely(bprm->have_execfd)) {
1788+
if (bprm->executable) {
1789+
fput(exec);
1790+
return -ENOEXEC;
1791+
}
1792+
bprm->executable = exec;
1793+
} else
1794+
fput(exec);
17841795
}
17851796

1786-
return ret;
1797+
audit_bprm(bprm);
1798+
trace_sched_process_exec(current, old_pid, bprm);
1799+
ptrace_event(PTRACE_EVENT_EXEC, old_vpid);
1800+
proc_exec_connector(current);
1801+
return 0;
17871802
}
17881803

17891804
/*

include/linux/binfmts.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ struct linux_binprm {
5050
#ifdef __alpha__
5151
unsigned int taso:1;
5252
#endif
53-
unsigned int recursion_depth; /* only for search_binary_handler() */
5453
struct file * executable; /* Executable to pass to the interpreter */
54+
struct file * interpreter;
5555
struct file * file;
5656
struct cred *cred; /* new credentials */
5757
int unsafe; /* how unsafe this exec is (mask of LSM_UNSAFE_*) */
@@ -117,7 +117,6 @@ static inline void insert_binfmt(struct linux_binfmt *fmt)
117117
extern void unregister_binfmt(struct linux_binfmt *);
118118

119119
extern int __must_check remove_arg_zero(struct linux_binprm *);
120-
extern int search_binary_handler(struct linux_binprm *);
121120
extern int begin_new_exec(struct linux_binprm * bprm);
122121
extern void setup_new_exec(struct linux_binprm * bprm);
123122
extern void finalize_exec(struct linux_binprm *bprm);

0 commit comments

Comments
 (0)