Skip to content

Commit 60d9ad1

Browse files
committed
exec: Move initialization of bprm->filename into alloc_bprm
Currently it is necessary for the usermode helper code and the code that launches init to use set_fs so that pages coming from the kernel look like they are coming from userspace. To allow that usage of set_fs to be removed cleanly the argument copying from userspace needs to happen earlier. Move the computation of bprm->filename and possible allocation of a name in the case of execveat into alloc_bprm to make that possible. The exectuable name, the arguments, and the environment are copied into the new usermode stack which is stored in bprm until exec passes the point of no return. As the executable name is copied first onto the usermode stack it needs to be known. As there are no dependencies to computing the executable name, compute it early in alloc_bprm. As an implementation detail if the filename needs to be generated because it embeds a file descriptor store that filename in a new field bprm->fdpath, and free it in free_bprm. Previously this was done in an independent variable pathbuf. I have renamed pathbuf fdpath because fdpath is more suggestive of what kind of path is in the variable. I moved fdpath into struct linux_binprm because it is tightly tied to the other variables in struct linux_binprm, and as such is needed to allow the call alloc_binprm to move. Reviewed-by: Kees Cook <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]> Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: "Eric W. Biederman" <[email protected]>
1 parent 0a8f36e commit 60d9ad1

File tree

2 files changed

+34
-28
lines changed

2 files changed

+34
-28
lines changed

fs/exec.c

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1557,15 +1557,37 @@ static void free_bprm(struct linux_binprm *bprm)
15571557
/* If a binfmt changed the interp, free it. */
15581558
if (bprm->interp != bprm->filename)
15591559
kfree(bprm->interp);
1560+
kfree(bprm->fdpath);
15601561
kfree(bprm);
15611562
}
15621563

1563-
static struct linux_binprm *alloc_bprm(void)
1564+
static struct linux_binprm *alloc_bprm(int fd, struct filename *filename)
15641565
{
15651566
struct linux_binprm *bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
1567+
int retval = -ENOMEM;
15661568
if (!bprm)
1567-
return ERR_PTR(-ENOMEM);
1569+
goto out;
1570+
1571+
if (fd == AT_FDCWD || filename->name[0] == '/') {
1572+
bprm->filename = filename->name;
1573+
} else {
1574+
if (filename->name[0] == '\0')
1575+
bprm->fdpath = kasprintf(GFP_KERNEL, "/dev/fd/%d", fd);
1576+
else
1577+
bprm->fdpath = kasprintf(GFP_KERNEL, "/dev/fd/%d/%s",
1578+
fd, filename->name);
1579+
if (!bprm->fdpath)
1580+
goto out_free;
1581+
1582+
bprm->filename = bprm->fdpath;
1583+
}
1584+
bprm->interp = bprm->filename;
15681585
return bprm;
1586+
1587+
out_free:
1588+
free_bprm(bprm);
1589+
out:
1590+
return ERR_PTR(retval);
15691591
}
15701592

15711593
int bprm_change_interp(const char *interp, struct linux_binprm *bprm)
@@ -1831,7 +1853,6 @@ static int do_execveat_common(int fd, struct filename *filename,
18311853
struct user_arg_ptr envp,
18321854
int flags)
18331855
{
1834-
char *pathbuf = NULL;
18351856
struct linux_binprm *bprm;
18361857
struct file *file;
18371858
struct files_struct *displaced;
@@ -1856,7 +1877,7 @@ static int do_execveat_common(int fd, struct filename *filename,
18561877
* further execve() calls fail. */
18571878
current->flags &= ~PF_NPROC_EXCEEDED;
18581879

1859-
bprm = alloc_bprm();
1880+
bprm = alloc_bprm(fd, filename);
18601881
if (IS_ERR(bprm)) {
18611882
retval = PTR_ERR(bprm);
18621883
goto out_ret;
@@ -1881,28 +1902,14 @@ static int do_execveat_common(int fd, struct filename *filename,
18811902
sched_exec();
18821903

18831904
bprm->file = file;
1884-
if (fd == AT_FDCWD || filename->name[0] == '/') {
1885-
bprm->filename = filename->name;
1886-
} else {
1887-
if (filename->name[0] == '\0')
1888-
pathbuf = kasprintf(GFP_KERNEL, "/dev/fd/%d", fd);
1889-
else
1890-
pathbuf = kasprintf(GFP_KERNEL, "/dev/fd/%d/%s",
1891-
fd, filename->name);
1892-
if (!pathbuf) {
1893-
retval = -ENOMEM;
1894-
goto out_unmark;
1895-
}
1896-
/*
1897-
* Record that a name derived from an O_CLOEXEC fd will be
1898-
* inaccessible after exec. Relies on having exclusive access to
1899-
* current->files (due to unshare_files above).
1900-
*/
1901-
if (close_on_exec(fd, rcu_dereference_raw(current->files->fdt)))
1902-
bprm->interp_flags |= BINPRM_FLAGS_PATH_INACCESSIBLE;
1903-
bprm->filename = pathbuf;
1904-
}
1905-
bprm->interp = bprm->filename;
1905+
/*
1906+
* Record that a name derived from an O_CLOEXEC fd will be
1907+
* inaccessible after exec. Relies on having exclusive access to
1908+
* current->files (due to unshare_files above).
1909+
*/
1910+
if (bprm->fdpath &&
1911+
close_on_exec(fd, rcu_dereference_raw(current->files->fdt)))
1912+
bprm->interp_flags |= BINPRM_FLAGS_PATH_INACCESSIBLE;
19061913

19071914
retval = bprm_mm_init(bprm);
19081915
if (retval)
@@ -1941,7 +1948,6 @@ static int do_execveat_common(int fd, struct filename *filename,
19411948
acct_update_integrals(current);
19421949
task_numa_free(current, false);
19431950
free_bprm(bprm);
1944-
kfree(pathbuf);
19451951
putname(filename);
19461952
if (displaced)
19471953
put_files_struct(displaced);
@@ -1970,7 +1976,6 @@ static int do_execveat_common(int fd, struct filename *filename,
19701976
reset_files_struct(displaced);
19711977
out_free:
19721978
free_bprm(bprm);
1973-
kfree(pathbuf);
19741979

19751980
out_ret:
19761981
putname(filename);

include/linux/binfmts.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ struct linux_binprm {
5656
const char *interp; /* Name of the binary really executed. Most
5757
of the time same as filename, but could be
5858
different for binfmt_{misc,script} */
59+
const char *fdpath; /* generated filename for execveat */
5960
unsigned interp_flags;
6061
int execfd; /* File descriptor of the executable */
6162
unsigned long loader, exec;

0 commit comments

Comments
 (0)