Skip to content

Commit ccbb18b

Browse files
committed
exec/binfmt_script: Don't modify bprm->buf and then return -ENOEXEC
The return code -ENOEXEC serves to tell search_binary_handler that it should continue searching for the binfmt to handle a given file. This makes return -ENOEXEC with a bprm->buf that is needed to continue the search problematic. The current binfmt_script manages to escape problems as it closes and clears bprm->file before return -ENOEXEC with bprm->buf modified. This prevents search_binary_handler from looping as it explicitly handles a NULL bprm->file. I plan on moving all of the bprm->file managment into fs/exec.c and out of the binary handlers so this will become a problem. Move closing bprm->file and the test for BINPRM_PATH_INACCESSIBLE down below the last return of -ENOEXEC. Introduce i_sep and i_end to track the end of the first argument and the end of the parameters respectively. Using those, constification of all char * pointers, and the helpers next_terminator and next_non_spacetab guarantee the parameter parsing will not modify bprm->buf. Only modify bprm->buf to terminate the strings i_arg and i_name with '\0' for passing to copy_strings_kernel. When replacing loops with next_non_spacetab and next_terminator care has been take that the logic of the parsing code (short of replacing characters by '\0') remains the same. 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 8b72ca9 commit ccbb18b

File tree

1 file changed

+38
-42
lines changed

1 file changed

+38
-42
lines changed

fs/binfmt_script.c

Lines changed: 38 additions & 42 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,48 @@ 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+
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+
107101
/*
108102
* OK, we've parsed out the interpreter name and
109103
* (optional) argument.
@@ -121,7 +115,9 @@ static int load_script(struct linux_binprm *bprm)
121115
if (retval < 0)
122116
return retval;
123117
bprm->argc++;
118+
*((char *)i_end) = '\0';
124119
if (i_arg) {
120+
*((char *)i_sep) = '\0';
125121
retval = copy_strings_kernel(1, &i_arg, bprm);
126122
if (retval < 0)
127123
return retval;

0 commit comments

Comments
 (0)