]>
git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/rexec.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
11 #include "file_utils.h"
13 #include "memory_utils.h"
14 #include "process_utils.h"
16 #include "string_utils.h"
17 #include "syscall_wrappers.h"
19 #if IS_BIONIC && !HAVE_FEXECVE
23 #define LXC_MEMFD_REXEC_SEALS \
24 (F_SEAL_SEAL | F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE)
26 static int push_vargs(char *data
, int data_length
, char ***output
)
34 *output
= must_realloc(NULL
, sizeof(**output
));
36 while (cur
< data
+ data_length
) {
38 *output
= must_realloc(*output
, (num
+ 1) * sizeof(**output
));
40 (*output
)[num
- 1] = cur
;
41 cur
+= strlen(cur
) + 1;
43 (*output
)[num
] = NULL
;
47 static int parse_argv(char ***argv
)
49 __do_free
char *cmdline
= NULL
;
53 cmdline
= file_to_buf("/proc/self/cmdline", &cmdline_size
);
57 ret
= push_vargs(cmdline
, cmdline_size
, argv
);
65 static int is_memfd(void)
67 __do_close
int fd
= -EBADF
;
70 fd
= open("/proc/self/exe", O_RDONLY
| O_CLOEXEC
);
72 return -ENOTRECOVERABLE
;
74 seals
= fcntl(fd
, F_GET_SEALS
);
78 if (fstat(fd
, &s
) == 0)
79 return (s
.st_nlink
== 0);
84 return seals
== LXC_MEMFD_REXEC_SEALS
;
87 static void lxc_rexec_as_memfd(char **argv
, char **envp
, const char *memfd_name
)
89 __do_close
int execfd
= -EBADF
, fd
= -EBADF
, memfd
= -EBADF
,
92 ssize_t bytes_sent
= 0;
95 memfd
= memfd_create(memfd_name
, MFD_ALLOW_SEALING
| MFD_CLOEXEC
);
97 char template[PATH_MAX
];
99 ret
= strnprintf(template, sizeof(template),
100 P_tmpdir
"/.%s_XXXXXX", memfd_name
);
104 tmpfd
= lxc_make_tmpfile(template, true);
108 ret
= fchmod(tmpfd
, 0700);
113 fd
= open("/proc/self/exe", O_RDONLY
| O_CLOEXEC
);
117 /* sendfile() handles up to 2GB. */
118 ret
= fstat(fd
, &st
);
122 while (bytes_sent
< st
.st_size
) {
125 sent
= lxc_sendfile_nointr(memfd
>= 0 ? memfd
: tmpfd
, fd
, NULL
,
126 st
.st_size
- bytes_sent
);
129 * Fallback to shoveling data between kernel- and
132 if (lseek(fd
, 0, SEEK_SET
) == (off_t
) -1)
133 fprintf(stderr
, "Failed to seek to beginning of file");
135 if (fd_to_fd(fd
, memfd
>= 0 ? memfd
: tmpfd
))
142 close_prot_errno_disarm(fd
);
145 if (fcntl(memfd
, F_ADD_SEALS
, LXC_MEMFD_REXEC_SEALS
))
148 execfd
= move_fd(memfd
);
150 char procfd
[LXC_PROC_PID_FD_LEN
];
152 ret
= strnprintf(procfd
, sizeof(procfd
), "/proc/self/fd/%d", tmpfd
);
156 execfd
= open(procfd
, O_PATH
| O_CLOEXEC
);
157 close_prot_errno_disarm(tmpfd
);
163 fexecve(execfd
, argv
, envp
);
167 * Get cheap access to the environment. This must be declared by the user as
168 * mandated by POSIX. The definition is located in unistd.h.
170 extern char **environ
;
172 int lxc_rexec(const char *memfd_name
)
174 __do_free_string_list
char **argv
= NULL
;
178 if (ret
< 0 && ret
== -ENOTRECOVERABLE
) {
179 fprintf(stderr
, "%s - Failed to determine whether this is a memfd\n",
182 } else if (ret
> 0) {
186 ret
= parse_argv(&argv
);
188 fprintf(stderr
, "%s - Failed to parse command line parameters\n",
193 lxc_rexec_as_memfd(argv
, environ
, memfd_name
);
194 fprintf(stderr
, "%s - Failed to rexec as memfd\n", strerror(errno
));
199 * This function will copy any binary that calls liblxc into a memory file and
200 * will use the memfd to rexecute the binary. This is done to prevent attacks
201 * through the /proc/self/exe symlink to corrupt the host binary when host and
202 * container are in the same user namespace or have set up an identity id
203 * mapping: CVE-2019-5736.
205 __attribute__((constructor
)) static void liblxc_rexec(void)
207 if (getenv("LXC_MEMFD_REXEC") && lxc_rexec("liblxc")) {
208 fprintf(stderr
, "Failed to re-execute liblxc via memory file descriptor\n");