Skip to content

Commit 978ffcb

Browse files
committed
execve: open the executable file before doing anything else
No point in allocating a new mm, counting arguments and environment variables etc if we're just going to return ENOENT. This patch does expose the fact that 'do_filp_open()' that execve() uses is still unnecessarily expensive in the failure case, because it allocates the 'struct file *' early, even if the path lookup (which is heavily optimized) fails. So that remains an unnecessary cost in the "no such executable" case, but it's a separate issue. Regardless, I do not want to do _both_ a filename_lookup() and a later do_filp_open() like the origin patch by Josh Triplett did in [1]. Reported-by: Josh Triplett <josh@joshtriplett.org> Cc: Kees Cook <keescook@chromium.org> Cc: Mateusz Guzik <mjguzik@gmail.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Link: https://lore.kernel.org/lkml/5c7333ea4bec2fad1b47a8fa2db7c31e4ffc4f14.1663334978.git.josh@joshtriplett.org/ [1] Link: https://lore.kernel.org/lkml/202209161637.9EDAF6B18@keescook/ Link: https://lore.kernel.org/lkml/CAHk-=wgznerM-xs+x+krDfE7eVBiy_HOam35rbsFMMOwvYuEKQ@mail.gmail.com/ Link: https://lore.kernel.org/lkml/CAHk-=whf9qLO8ipps4QhmS0BkM8mtWJhvnuDSdtw5gFjhzvKNA@mail.gmail.com/ Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent e5075d8 commit 978ffcb

File tree

1 file changed

+35
-34
lines changed

1 file changed

+35
-34
lines changed

fs/exec.c

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1508,12 +1508,24 @@ static void free_bprm(struct linux_binprm *bprm)
15081508
kfree(bprm);
15091509
}
15101510

1511-
static struct linux_binprm *alloc_bprm(int fd, struct filename *filename)
1511+
static struct linux_binprm *alloc_bprm(int fd, struct filename *filename, int flags)
15121512
{
1513-
struct linux_binprm *bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
1513+
struct linux_binprm *bprm;
1514+
struct file *file;
15141515
int retval = -ENOMEM;
1515-
if (!bprm)
1516-
goto out;
1516+
1517+
file = do_open_execat(fd, filename, flags);
1518+
if (IS_ERR(file))
1519+
return ERR_CAST(file);
1520+
1521+
bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
1522+
if (!bprm) {
1523+
allow_write_access(file);
1524+
fput(file);
1525+
return ERR_PTR(-ENOMEM);
1526+
}
1527+
1528+
bprm->file = file;
15171529

15181530
if (fd == AT_FDCWD || filename->name[0] == '/') {
15191531
bprm->filename = filename->name;
@@ -1526,18 +1538,28 @@ static struct linux_binprm *alloc_bprm(int fd, struct filename *filename)
15261538
if (!bprm->fdpath)
15271539
goto out_free;
15281540

1541+
/*
1542+
* Record that a name derived from an O_CLOEXEC fd will be
1543+
* inaccessible after exec. This allows the code in exec to
1544+
* choose to fail when the executable is not mmaped into the
1545+
* interpreter and an open file descriptor is not passed to
1546+
* the interpreter. This makes for a better user experience
1547+
* than having the interpreter start and then immediately fail
1548+
* when it finds the executable is inaccessible.
1549+
*/
1550+
if (get_close_on_exec(fd))
1551+
bprm->interp_flags |= BINPRM_FLAGS_PATH_INACCESSIBLE;
1552+
15291553
bprm->filename = bprm->fdpath;
15301554
}
15311555
bprm->interp = bprm->filename;
15321556

15331557
retval = bprm_mm_init(bprm);
1534-
if (retval)
1535-
goto out_free;
1536-
return bprm;
1558+
if (!retval)
1559+
return bprm;
15371560

15381561
out_free:
15391562
free_bprm(bprm);
1540-
out:
15411563
return ERR_PTR(retval);
15421564
}
15431565

@@ -1807,10 +1829,8 @@ static int exec_binprm(struct linux_binprm *bprm)
18071829
/*
18081830
* sys_execve() executes a new program.
18091831
*/
1810-
static int bprm_execve(struct linux_binprm *bprm,
1811-
int fd, struct filename *filename, int flags)
1832+
static int bprm_execve(struct linux_binprm *bprm)
18121833
{
1813-
struct file *file;
18141834
int retval;
18151835

18161836
retval = prepare_bprm_creds(bprm);
@@ -1826,26 +1846,8 @@ static int bprm_execve(struct linux_binprm *bprm,
18261846
current->in_execve = 1;
18271847
sched_mm_cid_before_execve(current);
18281848

1829-
file = do_open_execat(fd, filename, flags);
1830-
retval = PTR_ERR(file);
1831-
if (IS_ERR(file))
1832-
goto out_unmark;
1833-
18341849
sched_exec();
18351850

1836-
bprm->file = file;
1837-
/*
1838-
* Record that a name derived from an O_CLOEXEC fd will be
1839-
* inaccessible after exec. This allows the code in exec to
1840-
* choose to fail when the executable is not mmaped into the
1841-
* interpreter and an open file descriptor is not passed to
1842-
* the interpreter. This makes for a better user experience
1843-
* than having the interpreter start and then immediately fail
1844-
* when it finds the executable is inaccessible.
1845-
*/
1846-
if (bprm->fdpath && get_close_on_exec(fd))
1847-
bprm->interp_flags |= BINPRM_FLAGS_PATH_INACCESSIBLE;
1848-
18491851
/* Set the unchanging part of bprm->cred */
18501852
retval = security_bprm_creds_for_exec(bprm);
18511853
if (retval)
@@ -1875,7 +1877,6 @@ static int bprm_execve(struct linux_binprm *bprm,
18751877
if (bprm->point_of_no_return && !fatal_signal_pending(current))
18761878
force_fatal_sig(SIGSEGV);
18771879

1878-
out_unmark:
18791880
sched_mm_cid_after_execve(current);
18801881
current->fs->in_exec = 0;
18811882
current->in_execve = 0;
@@ -1910,7 +1911,7 @@ static int do_execveat_common(int fd, struct filename *filename,
19101911
* further execve() calls fail. */
19111912
current->flags &= ~PF_NPROC_EXCEEDED;
19121913

1913-
bprm = alloc_bprm(fd, filename);
1914+
bprm = alloc_bprm(fd, filename, flags);
19141915
if (IS_ERR(bprm)) {
19151916
retval = PTR_ERR(bprm);
19161917
goto out_ret;
@@ -1959,7 +1960,7 @@ static int do_execveat_common(int fd, struct filename *filename,
19591960
bprm->argc = 1;
19601961
}
19611962

1962-
retval = bprm_execve(bprm, fd, filename, flags);
1963+
retval = bprm_execve(bprm);
19631964
out_free:
19641965
free_bprm(bprm);
19651966

@@ -1984,7 +1985,7 @@ int kernel_execve(const char *kernel_filename,
19841985
if (IS_ERR(filename))
19851986
return PTR_ERR(filename);
19861987

1987-
bprm = alloc_bprm(fd, filename);
1988+
bprm = alloc_bprm(fd, filename, 0);
19881989
if (IS_ERR(bprm)) {
19891990
retval = PTR_ERR(bprm);
19901991
goto out_ret;
@@ -2019,7 +2020,7 @@ int kernel_execve(const char *kernel_filename,
20192020
if (retval < 0)
20202021
goto out_free;
20212022

2022-
retval = bprm_execve(bprm, fd, filename, 0);
2023+
retval = bprm_execve(bprm);
20232024
out_free:
20242025
free_bprm(bprm);
20252026
out_ret:

0 commit comments

Comments
 (0)