History

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.

Eric W. Biederman committed 6 years ago

Commit bc2bf331 parent b8a61c96 files · +43 −55Browse files

Unified · Split

6 files
  1. arch/alpha/kernel/binfmt_loader.c +2 −6
  2. fs/binfmt_em86.c +2 −7
  3. fs/binfmt_misc.c +3 −15
  4. fs/binfmt_script.c +2 −7
  5. fs/exec.c +33 −18
  6. include/linux/binfmts.h +1 −2
arch/alpha/kernel/binfmt_loader.c+2−6
@@ -19,10 +19,6 @@ static int load_binary(struct linux_binprm *bprm) · 18 unmodified lines
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) · 4 unmodified lines
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+2−7
@@ -48,10 +48,6 @@ static int load_em86(struct linux_binprm *bprm) · 47 unmodified lines
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) · 31 unmodified lines
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+3−15
@@ -159,18 +159,9 @@ static int load_misc_binary(struct linux_binprm *bprm) · 158 unmodified lines
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) · 22 unmodified lines
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+2−7
@@ -93,11 +93,6 @@ static int load_script(struct linux_binprm *bprm) · 92 unmodified lines
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) · 34 unmodified lines
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+33−18
@@ -1710,16 +1710,12 @@ EXPORT_SYMBOL(remove_arg_zero); · 1709 unmodified lines
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) · 10 unmodified lines
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,12 +1755,11 @@ int search_binary_handler(struct linux_binprm *bprm) · 12 unmodified lines
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;
@@ -1775,15 +1767,38 @@ static int exec_binprm(struct linux_binprm *bprm) · 1 unmodified line
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+1−2
@@ -50,8 +50,8 @@ struct linux_binprm { · 49 unmodified lines
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) · 59 unmodified lines
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);