Skip to content

Commit 9d9488d

Browse files
committed
exec: Control flow simplifications
It is hard to follow the control flow in exec.c as the code has evolved over time and something that used to work one way now works another. This set of changes attempts to address the worst of that, to remove unnecessary work and to make the code a little easier to follow. The churn is a bit higher than the last version of this patchset, with renaming and cleaning up of comments. I have split security_bprm_set_creds into security_bprm_creds_for_exec and security_bprm_repopulate_creds. My goal was to make it clear that one hook completes its work while the other recaculates it's work each time a new interpreter is selected. I have added a new change at the beginning to make it clear that neither security_bprm_creds_for_exec nor security_bprm_repopulate_creds needs to be implemented as prepare_exec_creds properly does the work of setting up credentials unless something special is going on. I have made the execfd support generic and moved out of binfmt_misc so that I can remove the recursion. I have moved reassigning bprm->file into the loop that replaces the recursion. In doing so I discovered that binfmt_misc was naughty and was returning -ENOEXEC in such a way that the search_binary_handler loop could not continue. So I added a change to remove that naughtiness. Eric W. Biederman (8): exec: Teach prepare_exec_creds how exec treats uids & gids exec: Factor security_bprm_creds_for_exec out of security_bprm_set_creds exec: Convert security_bprm_set_creds into security_bprm_repopulate_creds exec: Allow load_misc_binary to call prepare_binfmt unconditionally exec: Move the call of prepare_binprm into search_binary_handler exec/binfmt_script: Don't modify bprm->buf and then return -ENOEXEC exec: Generic execfd support exec: Remove recursion from search_binary_handler arch/alpha/kernel/binfmt_loader.c | 11 +---- fs/binfmt_elf.c | 4 +- fs/binfmt_elf_fdpic.c | 4 +- fs/binfmt_em86.c | 13 +---- fs/binfmt_misc.c | 69 ++++----------------------- fs/binfmt_script.c | 82 ++++++++++++++------------------ fs/exec.c | 97 ++++++++++++++++++++++++++------------ include/linux/binfmts.h | 36 ++++++-------- include/linux/lsm_hook_defs.h | 3 +- include/linux/lsm_hooks.h | 52 +++++++++++--------- include/linux/security.h | 14 ++++-- kernel/cred.c | 3 ++ security/apparmor/domain.c | 7 +-- security/apparmor/include/domain.h | 2 +- security/apparmor/lsm.c | 2 +- security/commoncap.c | 9 ++-- security/security.c | 9 +++- security/selinux/hooks.c | 8 ++-- security/smack/smack_lsm.c | 9 ++-- security/tomoyo/tomoyo.c | 12 ++--- 20 files changed, 202 insertions(+), 244 deletions(-) Link: https://lkml.kernel.org/r/[email protected] Acked-by: Linus Torvalds <[email protected]> Signed-off-by: "Eric W. Biederman" <[email protected]>
2 parents b127c16 + bc2bf33 commit 9d9488d

File tree

20 files changed

+202
-244
lines changed

20 files changed

+202
-244
lines changed

arch/alpha/kernel/binfmt_loader.c

Lines changed: 2 additions & 9 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,12 +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-
retval = prepare_binprm(bprm);
39-
if (retval < 0)
40-
return retval;
41-
return search_binary_handler(bprm);
34+
return 0;
4235
}
4336

4437
static struct linux_binfmt loader_format = {

fs/binfmt_elf.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,8 +273,8 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec,
273273
NEW_AUX_ENT(AT_BASE_PLATFORM,
274274
(elf_addr_t)(unsigned long)u_base_platform);
275275
}
276-
if (bprm->interp_flags & BINPRM_FLAGS_EXECFD) {
277-
NEW_AUX_ENT(AT_EXECFD, bprm->interp_data);
276+
if (bprm->have_execfd) {
277+
NEW_AUX_ENT(AT_EXECFD, bprm->execfd);
278278
}
279279
#undef NEW_AUX_ENT
280280
/* AT_NULL is zero; clear the rest too */

fs/binfmt_elf_fdpic.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -628,10 +628,10 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
628628
(elf_addr_t) (unsigned long) u_base_platform);
629629
}
630630

631-
if (bprm->interp_flags & BINPRM_FLAGS_EXECFD) {
631+
if (bprm->have_execfd) {
632632
nr = 0;
633633
csp -= 2 * sizeof(unsigned long);
634-
NEW_AUX_ENT(AT_EXECFD, bprm->interp_data);
634+
NEW_AUX_ENT(AT_EXECFD, bprm->execfd);
635635
}
636636

637637
nr = 0;

fs/binfmt_em86.c

Lines changed: 2 additions & 11 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,13 +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-
retval = prepare_binprm(bprm);
95-
if (retval < 0)
96-
return retval;
97-
98-
return search_binary_handler(bprm);
88+
bprm->interpreter = file;
89+
return 0;
9990
}
10091

10192
static struct linux_binfmt em86_format = {

fs/binfmt_misc.c

Lines changed: 10 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,6 @@ static int load_misc_binary(struct linux_binprm *bprm)
134134
Node *fmt;
135135
struct file *interp_file = NULL;
136136
int retval;
137-
int fd_binary = -1;
138137

139138
retval = -ENOEXEC;
140139
if (!enabled)
@@ -160,51 +159,25 @@ static int load_misc_binary(struct linux_binprm *bprm)
160159
goto ret;
161160
}
162161

163-
if (fmt->flags & MISC_FMT_OPEN_BINARY) {
162+
if (fmt->flags & MISC_FMT_OPEN_BINARY)
163+
bprm->have_execfd = 1;
164164

165-
/* if the binary should be opened on behalf of the
166-
* interpreter than keep it open and assign descriptor
167-
* to it
168-
*/
169-
fd_binary = get_unused_fd_flags(0);
170-
if (fd_binary < 0) {
171-
retval = fd_binary;
172-
goto ret;
173-
}
174-
fd_install(fd_binary, bprm->file);
175-
176-
/* if the binary is not readable than enforce mm->dumpable=0
177-
regardless of the interpreter's permissions */
178-
would_dump(bprm, bprm->file);
179-
180-
allow_write_access(bprm->file);
181-
bprm->file = NULL;
182-
183-
/* mark the bprm that fd should be passed to interp */
184-
bprm->interp_flags |= BINPRM_FLAGS_EXECFD;
185-
bprm->interp_data = fd_binary;
186-
187-
} else {
188-
allow_write_access(bprm->file);
189-
fput(bprm->file);
190-
bprm->file = NULL;
191-
}
192165
/* make argv[1] be the path to the binary */
193166
retval = copy_strings_kernel(1, &bprm->interp, bprm);
194167
if (retval < 0)
195-
goto error;
168+
goto ret;
196169
bprm->argc++;
197170

198171
/* add the interp as argv[0] */
199172
retval = copy_strings_kernel(1, &fmt->interpreter, bprm);
200173
if (retval < 0)
201-
goto error;
174+
goto ret;
202175
bprm->argc++;
203176

204177
/* Update interp in case binfmt_script needs it. */
205178
retval = bprm_change_interp(fmt->interpreter, bprm);
206179
if (retval < 0)
207-
goto error;
180+
goto ret;
208181

209182
if (fmt->flags & MISC_FMT_OPEN_FILE) {
210183
interp_file = file_clone_open(fmt->interp_file);
@@ -215,38 +188,16 @@ static int load_misc_binary(struct linux_binprm *bprm)
215188
}
216189
retval = PTR_ERR(interp_file);
217190
if (IS_ERR(interp_file))
218-
goto error;
219-
220-
bprm->file = interp_file;
221-
if (fmt->flags & MISC_FMT_CREDENTIALS) {
222-
loff_t pos = 0;
223-
224-
/*
225-
* No need to call prepare_binprm(), it's already been
226-
* done. bprm->buf is stale, update from interp_file.
227-
*/
228-
memset(bprm->buf, 0, BINPRM_BUF_SIZE);
229-
retval = kernel_read(bprm->file, bprm->buf, BINPRM_BUF_SIZE,
230-
&pos);
231-
} else
232-
retval = prepare_binprm(bprm);
233-
234-
if (retval < 0)
235-
goto error;
191+
goto ret;
236192

237-
retval = search_binary_handler(bprm);
238-
if (retval < 0)
239-
goto error;
193+
bprm->interpreter = interp_file;
194+
if (fmt->flags & MISC_FMT_CREDENTIALS)
195+
bprm->preserve_creds = 1;
240196

197+
retval = 0;
241198
ret:
242199
dput(fmt->dentry);
243200
return retval;
244-
error:
245-
if (fd_binary > 0)
246-
ksys_close(fd_binary);
247-
bprm->interp_flags = 0;
248-
bprm->interp_data = 0;
249-
goto ret;
250201
}
251202

252203
/* Command parsers */

fs/binfmt_script.c

Lines changed: 35 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@
1616
#include <linux/fs.h>
1717

1818
static inline bool spacetab(char c) { return c == ' ' || c == '\t'; }
19-
static inline char *next_non_spacetab(char *first, const char *last)
19+
static inline const char *next_non_spacetab(const char *first, const char *last)
2020
{
2121
for (; first <= last; first++)
2222
if (!spacetab(*first))
2323
return first;
2424
return NULL;
2525
}
26-
static inline char *next_terminator(char *first, const char *last)
26+
static inline const char *next_terminator(const char *first, const char *last)
2727
{
2828
for (; first <= last; first++)
2929
if (spacetab(*first) || !*first)
@@ -33,29 +33,14 @@ static inline char *next_terminator(char *first, const char *last)
3333

3434
static int load_script(struct linux_binprm *bprm)
3535
{
36-
const char *i_arg, *i_name;
37-
char *cp, *buf_end;
36+
const char *i_name, *i_sep, *i_arg, *i_end, *buf_end;
3837
struct file *file;
3938
int retval;
4039

4140
/* Not ours to exec if we don't start with "#!". */
4241
if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))
4342
return -ENOEXEC;
4443

45-
/*
46-
* If the script filename will be inaccessible after exec, typically
47-
* because it is a "/dev/fd/<fd>/.." path against an O_CLOEXEC fd, give
48-
* up now (on the assumption that the interpreter will want to load
49-
* this file).
50-
*/
51-
if (bprm->interp_flags & BINPRM_FLAGS_PATH_INACCESSIBLE)
52-
return -ENOENT;
53-
54-
/* Release since we are not mapping a binary into memory. */
55-
allow_write_access(bprm->file);
56-
fput(bprm->file);
57-
bprm->file = NULL;
58-
5944
/*
6045
* This section handles parsing the #! line into separate
6146
* interpreter path and argument strings. We must be careful
@@ -71,39 +56,43 @@ static int load_script(struct linux_binprm *bprm)
7156
* parse them on its own.
7257
*/
7358
buf_end = bprm->buf + sizeof(bprm->buf) - 1;
74-
cp = strnchr(bprm->buf, sizeof(bprm->buf), '\n');
75-
if (!cp) {
76-
cp = next_non_spacetab(bprm->buf + 2, buf_end);
77-
if (!cp)
59+
i_end = strnchr(bprm->buf, sizeof(bprm->buf), '\n');
60+
if (!i_end) {
61+
i_end = next_non_spacetab(bprm->buf + 2, buf_end);
62+
if (!i_end)
7863
return -ENOEXEC; /* Entire buf is spaces/tabs */
7964
/*
8065
* If there is no later space/tab/NUL we must assume the
8166
* interpreter path is truncated.
8267
*/
83-
if (!next_terminator(cp, buf_end))
68+
if (!next_terminator(i_end, buf_end))
8469
return -ENOEXEC;
85-
cp = buf_end;
70+
i_end = buf_end;
8671
}
87-
/* NUL-terminate the buffer and any trailing spaces/tabs. */
88-
*cp = '\0';
89-
while (cp > bprm->buf) {
90-
cp--;
91-
if ((*cp == ' ') || (*cp == '\t'))
92-
*cp = '\0';
93-
else
94-
break;
95-
}
96-
for (cp = bprm->buf+2; (*cp == ' ') || (*cp == '\t'); cp++);
97-
if (*cp == '\0')
72+
/* Trim any trailing spaces/tabs from i_end */
73+
while (spacetab(i_end[-1]))
74+
i_end--;
75+
76+
/* Skip over leading spaces/tabs */
77+
i_name = next_non_spacetab(bprm->buf+2, i_end);
78+
if (!i_name || (i_name == i_end))
9879
return -ENOEXEC; /* No interpreter name found */
99-
i_name = cp;
80+
81+
/* Is there an optional argument? */
10082
i_arg = NULL;
101-
for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++)
102-
/* nothing */ ;
103-
while ((*cp == ' ') || (*cp == '\t'))
104-
*cp++ = '\0';
105-
if (*cp)
106-
i_arg = cp;
83+
i_sep = next_terminator(i_name, i_end);
84+
if (i_sep && (*i_sep != '\0'))
85+
i_arg = next_non_spacetab(i_sep, i_end);
86+
87+
/*
88+
* If the script filename will be inaccessible after exec, typically
89+
* because it is a "/dev/fd/<fd>/.." path against an O_CLOEXEC fd, give
90+
* up now (on the assumption that the interpreter will want to load
91+
* this file).
92+
*/
93+
if (bprm->interp_flags & BINPRM_FLAGS_PATH_INACCESSIBLE)
94+
return -ENOENT;
95+
10796
/*
10897
* OK, we've parsed out the interpreter name and
10998
* (optional) argument.
@@ -121,7 +110,9 @@ static int load_script(struct linux_binprm *bprm)
121110
if (retval < 0)
122111
return retval;
123112
bprm->argc++;
113+
*((char *)i_end) = '\0';
124114
if (i_arg) {
115+
*((char *)i_sep) = '\0';
125116
retval = copy_strings_kernel(1, &i_arg, bprm);
126117
if (retval < 0)
127118
return retval;
@@ -142,11 +133,8 @@ static int load_script(struct linux_binprm *bprm)
142133
if (IS_ERR(file))
143134
return PTR_ERR(file);
144135

145-
bprm->file = file;
146-
retval = prepare_binprm(bprm);
147-
if (retval < 0)
148-
return retval;
149-
return search_binary_handler(bprm);
136+
bprm->interpreter = file;
137+
return 0;
150138
}
151139

152140
static struct linux_binfmt script_format = {

0 commit comments

Comments
 (0)