Skip to content

Commit b8a61c9

Browse files
committed
exec: Generic execfd support
Most of the support for passing the file descriptor of an executable to an interpreter already lives in the generic code and in binfmt_elf. Rework the fields in binfmt_elf that deal with executable file descriptor passing to make executable file descriptor passing a first class concept. Move the fd_install from binfmt_misc into begin_new_exec after the new creds have been installed. This means that accessing the file through /proc/<pid>/fd/N is able to see the creds for the new executable before allowing access to the new executables files. Performing the install of the executables file descriptor after the point of no return also means that nothing special needs to be done on error. The exiting of the process will close all of it's open files. Move the would_dump from binfmt_misc into begin_new_exec right after would_dump is called on the bprm->file. This makes it obvious this case exists and that no nesting of bprm->file is currently supported. In binfmt_misc the movement of fd_install into generic code means that it's special error exit path is no longer needed. 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 ccbb18b commit b8a61c9

File tree

5 files changed

+32
-41
lines changed

5 files changed

+32
-41
lines changed

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_misc.c

Lines changed: 8 additions & 32 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)
@@ -161,29 +160,12 @@ static int load_misc_binary(struct linux_binprm *bprm)
161160
}
162161

163162
if (fmt->flags & MISC_FMT_OPEN_BINARY) {
164-
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);
163+
/* Pass the open binary to the interpreter */
164+
bprm->have_execfd = 1;
165+
bprm->executable = bprm->file;
179166

180167
allow_write_access(bprm->file);
181168
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-
187169
} else {
188170
allow_write_access(bprm->file);
189171
fput(bprm->file);
@@ -192,19 +174,19 @@ static int load_misc_binary(struct linux_binprm *bprm)
192174
/* make argv[1] be the path to the binary */
193175
retval = copy_strings_kernel(1, &bprm->interp, bprm);
194176
if (retval < 0)
195-
goto error;
177+
goto ret;
196178
bprm->argc++;
197179

198180
/* add the interp as argv[0] */
199181
retval = copy_strings_kernel(1, &fmt->interpreter, bprm);
200182
if (retval < 0)
201-
goto error;
183+
goto ret;
202184
bprm->argc++;
203185

204186
/* Update interp in case binfmt_script needs it. */
205187
retval = bprm_change_interp(fmt->interpreter, bprm);
206188
if (retval < 0)
207-
goto error;
189+
goto ret;
208190

209191
if (fmt->flags & MISC_FMT_OPEN_FILE) {
210192
interp_file = file_clone_open(fmt->interp_file);
@@ -215,25 +197,19 @@ static int load_misc_binary(struct linux_binprm *bprm)
215197
}
216198
retval = PTR_ERR(interp_file);
217199
if (IS_ERR(interp_file))
218-
goto error;
200+
goto ret;
219201

220202
bprm->file = interp_file;
221203
if (fmt->flags & MISC_FMT_CREDENTIALS)
222204
bprm->preserve_creds = 1;
223205

224206
retval = search_binary_handler(bprm);
225207
if (retval < 0)
226-
goto error;
208+
goto ret;
227209

228210
ret:
229211
dput(fmt->dentry);
230212
return retval;
231-
error:
232-
if (fd_binary > 0)
233-
ksys_close(fd_binary);
234-
bprm->interp_flags = 0;
235-
bprm->interp_data = 0;
236-
goto ret;
237213
}
238214

239215
/* Command parsers */

fs/exec.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,7 +1323,10 @@ int begin_new_exec(struct linux_binprm * bprm)
13231323
*/
13241324
set_mm_exe_file(bprm->mm, bprm->file);
13251325

1326+
/* If the binary is not readable then enforce mm->dumpable=0 */
13261327
would_dump(bprm, bprm->file);
1328+
if (bprm->have_execfd)
1329+
would_dump(bprm, bprm->executable);
13271330

13281331
/*
13291332
* Release all of the old mmap stuff
@@ -1427,6 +1430,16 @@ int begin_new_exec(struct linux_binprm * bprm)
14271430
* credentials; any time after this it may be unlocked.
14281431
*/
14291432
security_bprm_committed_creds(bprm);
1433+
1434+
/* Pass the opened binary to the interpreter. */
1435+
if (bprm->have_execfd) {
1436+
retval = get_unused_fd_flags(0);
1437+
if (retval < 0)
1438+
goto out_unlock;
1439+
fd_install(retval, bprm->executable);
1440+
bprm->executable = NULL;
1441+
bprm->execfd = retval;
1442+
}
14301443
return 0;
14311444

14321445
out_unlock:
@@ -1516,6 +1529,8 @@ static void free_bprm(struct linux_binprm *bprm)
15161529
allow_write_access(bprm->file);
15171530
fput(bprm->file);
15181531
}
1532+
if (bprm->executable)
1533+
fput(bprm->executable);
15191534
/* If a binfmt changed the interp, free it. */
15201535
if (bprm->interp != bprm->filename)
15211536
kfree(bprm->interp);

include/linux/binfmts.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ struct linux_binprm {
2626
unsigned long p; /* current top of mem */
2727
unsigned long argmin; /* rlimit marker for copy_strings() */
2828
unsigned int
29+
/* Should an execfd be passed to userspace? */
30+
have_execfd:1,
31+
2932
/* It is safe to use the creds of a script (see binfmt_misc) */
3033
preserve_creds:1,
3134
/*
@@ -48,6 +51,7 @@ struct linux_binprm {
4851
unsigned int taso:1;
4952
#endif
5053
unsigned int recursion_depth; /* only for search_binary_handler() */
54+
struct file * executable; /* Executable to pass to the interpreter */
5155
struct file * file;
5256
struct cred *cred; /* new credentials */
5357
int unsafe; /* how unsafe this exec is (mask of LSM_UNSAFE_*) */
@@ -58,7 +62,7 @@ struct linux_binprm {
5862
of the time same as filename, but could be
5963
different for binfmt_{misc,script} */
6064
unsigned interp_flags;
61-
unsigned interp_data;
65+
int execfd; /* File descriptor of the executable */
6266
unsigned long loader, exec;
6367

6468
struct rlimit rlim_stack; /* Saved RLIMIT_STACK used during exec. */
@@ -69,10 +73,6 @@ struct linux_binprm {
6973
#define BINPRM_FLAGS_ENFORCE_NONDUMP_BIT 0
7074
#define BINPRM_FLAGS_ENFORCE_NONDUMP (1 << BINPRM_FLAGS_ENFORCE_NONDUMP_BIT)
7175

72-
/* fd of the binary should be passed to the interpreter */
73-
#define BINPRM_FLAGS_EXECFD_BIT 1
74-
#define BINPRM_FLAGS_EXECFD (1 << BINPRM_FLAGS_EXECFD_BIT)
75-
7676
/* filename of the binary will be inaccessible after exec */
7777
#define BINPRM_FLAGS_PATH_INACCESSIBLE_BIT 2
7878
#define BINPRM_FLAGS_PATH_INACCESSIBLE (1 << BINPRM_FLAGS_PATH_INACCESSIBLE_BIT)

0 commit comments

Comments
 (0)