]>
git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/rexec.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
13 #include "file_utils.h"
15 #include "memory_utils.h"
16 #include "process_utils.h"
18 #include "string_utils.h"
19 #include "syscall_wrappers.h"
25 #define LXC_MEMFD_REXEC_SEALS \
26 (F_SEAL_SEAL | F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE)
28 static int push_vargs(char *data
, int data_length
, char ***output
)
36 *output
= must_realloc(NULL
, sizeof(**output
));
38 while (cur
< data
+ data_length
) {
40 *output
= must_realloc(*output
, (num
+ 1) * sizeof(**output
));
42 (*output
)[num
- 1] = cur
;
43 cur
+= strlen(cur
) + 1;
45 (*output
)[num
] = NULL
;
49 static int parse_argv(char ***argv
)
51 __do_free
char *cmdline
= NULL
;
55 cmdline
= file_to_buf("/proc/self/cmdline", &cmdline_size
);
59 ret
= push_vargs(cmdline
, cmdline_size
, argv
);
67 static int is_memfd(void)
69 __do_close
int fd
= -EBADF
;
72 fd
= open("/proc/self/exe", O_RDONLY
| O_CLOEXEC
);
74 return -ENOTRECOVERABLE
;
76 seals
= fcntl(fd
, F_GET_SEALS
);
80 if (fstat(fd
, &s
) == 0)
81 return (s
.st_nlink
== 0);
86 return seals
== LXC_MEMFD_REXEC_SEALS
;
89 static void lxc_rexec_as_memfd(char **argv
, char **envp
, const char *memfd_name
)
91 __do_close
int execfd
= -EBADF
, fd
= -EBADF
, memfd
= -EBADF
,
94 ssize_t bytes_sent
= 0;
97 memfd
= memfd_create(memfd_name
, MFD_ALLOW_SEALING
| MFD_CLOEXEC
);
99 char template[PATH_MAX
];
101 ret
= strnprintf(template, sizeof(template),
102 P_tmpdir
"/.%s_XXXXXX", memfd_name
);
106 tmpfd
= lxc_make_tmpfile(template, true);
110 ret
= fchmod(tmpfd
, 0700);
115 fd
= open("/proc/self/exe", O_RDONLY
| O_CLOEXEC
);
119 /* sendfile() handles up to 2GB. */
120 ret
= fstat(fd
, &st
);
124 while (bytes_sent
< st
.st_size
) {
127 sent
= lxc_sendfile_nointr(memfd
>= 0 ? memfd
: tmpfd
, fd
, NULL
,
128 st
.st_size
- bytes_sent
);
131 * Fallback to shoveling data between kernel- and
134 if (lseek(fd
, 0, SEEK_SET
) == (off_t
) -1)
135 fprintf(stderr
, "Failed to seek to beginning of file");
137 if (fd_to_fd(fd
, memfd
>= 0 ? memfd
: tmpfd
))
144 close_prot_errno_disarm(fd
);
147 if (fcntl(memfd
, F_ADD_SEALS
, LXC_MEMFD_REXEC_SEALS
))
150 execfd
= move_fd(memfd
);
152 char procfd
[LXC_PROC_PID_FD_LEN
];
154 ret
= strnprintf(procfd
, sizeof(procfd
), "/proc/self/fd/%d", tmpfd
);
158 execfd
= open(procfd
, O_PATH
| O_CLOEXEC
);
159 close_prot_errno_disarm(tmpfd
);
165 fexecve(execfd
, argv
, envp
);
169 * Get cheap access to the environment. This must be declared by the user as
170 * mandated by POSIX. The definition is located in unistd.h.
172 extern char **environ
;
174 int lxc_rexec(const char *memfd_name
)
176 __do_free_string_list
char **argv
= NULL
;
180 if (ret
< 0 && ret
== -ENOTRECOVERABLE
) {
181 fprintf(stderr
, "%s - Failed to determine whether this is a memfd\n",
184 } else if (ret
> 0) {
188 ret
= parse_argv(&argv
);
190 fprintf(stderr
, "%s - Failed to parse command line parameters\n",
195 lxc_rexec_as_memfd(argv
, environ
, memfd_name
);
196 fprintf(stderr
, "%s - Failed to rexec as memfd\n", strerror(errno
));
201 * This function will copy any binary that calls liblxc into a memory file and
202 * will use the memfd to rexecute the binary. This is done to prevent attacks
203 * through the /proc/self/exe symlink to corrupt the host binary when host and
204 * container are in the same user namespace or have set up an identity id
205 * mapping: CVE-2019-5736.
207 __attribute__((constructor
)) static void liblxc_rexec(void)
209 if (getenv("LXC_MEMFD_REXEC") && lxc_rexec("liblxc")) {
210 fprintf(stderr
, "Failed to re-execute liblxc via memory file descriptor\n");