]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/rexec.c
Merge pull request #3235 from xinhua9569/master
[mirror_lxc.git] / src / lxc / rexec.c
CommitLineData
cc73685d 1/* SPDX-License-Identifier: LGPL-2.1+ */
6400238d
CB
2
3#ifndef _GNU_SOURCE
4#define _GNU_SOURCE 1
5#endif
6#include <errno.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
cee55b59 10#include <unistd.h>
6400238d
CB
11
12#include "config.h"
13#include "file_utils.h"
cee55b59
CB
14#include "macro.h"
15#include "memory_utils.h"
6400238d
CB
16#include "raw_syscalls.h"
17#include "string_utils.h"
18#include "syscall_wrappers.h"
19
9d361e0f
CB
20#if IS_BIONIC
21#include "../include/fexecve.h"
22#endif
23
6400238d
CB
24#define LXC_MEMFD_REXEC_SEALS \
25 (F_SEAL_SEAL | F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE)
26
7a80606d
CB
27static int push_vargs(char *data, int data_length, char ***output)
28{
29 int num = 0;
30 char *cur = data;
31
32 if (!data || *output)
33 return -1;
34
35 *output = must_realloc(NULL, sizeof(**output));
36
37 while (cur < data + data_length) {
38 num++;
39 *output = must_realloc(*output, (num + 1) * sizeof(**output));
40
41 (*output)[num - 1] = cur;
42 cur += strlen(cur) + 1;
43 }
44 (*output)[num] = NULL;
45 return num;
46}
47
cee55b59 48static int parse_argv(char ***argv)
7a80606d 49{
cee55b59 50 __do_free char *cmdline = NULL;
7a80606d 51 int ret;
cee55b59 52 size_t cmdline_size;
7a80606d
CB
53
54 cmdline = file_to_buf("/proc/self/cmdline", &cmdline_size);
55 if (!cmdline)
cee55b59 56 return -1;
7a80606d
CB
57
58 ret = push_vargs(cmdline, cmdline_size, argv);
59 if (ret <= 0)
cee55b59 60 return -1;
7a80606d 61
e4edf5d7 62 move_ptr(cmdline);
7a80606d 63 return 0;
7a80606d
CB
64}
65
6400238d
CB
66static int is_memfd(void)
67{
fed8112d 68 __do_close_prot_errno int fd = -EBADF;
4aa90f60 69 int seals;
6400238d
CB
70
71 fd = open("/proc/self/exe", O_RDONLY | O_CLOEXEC);
72 if (fd < 0)
73 return -ENOTRECOVERABLE;
74
75 seals = fcntl(fd, F_GET_SEALS);
4aa90f60
CB
76 if (seals < 0) {
77 struct stat s = {0};
78
79 if (fstat(fd, &s) == 0)
80 return (s.st_nlink == 0);
81
6400238d 82 return -EINVAL;
4aa90f60 83 }
6400238d
CB
84
85 return seals == LXC_MEMFD_REXEC_SEALS;
86}
87
88static void lxc_rexec_as_memfd(char **argv, char **envp, const char *memfd_name)
89{
5e6e38bd
CB
90 __do_close_prot_errno int execfd = -EBADF, fd = -EBADF, memfd = -EBADF,
91 tmpfd = -EBADF;
4aa90f60 92 int ret;
5e6e38bd
CB
93 ssize_t bytes_sent = 0;
94 struct stat st = {0};
6400238d
CB
95
96 memfd = memfd_create(memfd_name, MFD_ALLOW_SEALING | MFD_CLOEXEC);
4aa90f60
CB
97 if (memfd < 0) {
98 char template[PATH_MAX];
99
100 ret = snprintf(template, sizeof(template),
101 P_tmpdir "/.%s_XXXXXX", memfd_name);
102 if (ret < 0 || (size_t)ret >= sizeof(template))
103 return;
104
105 tmpfd = lxc_make_tmpfile(template, true);
106 if (tmpfd < 0)
107 return;
108
109 ret = fchmod(tmpfd, 0700);
110 if (ret)
111 return;
112 }
6400238d
CB
113
114 fd = open("/proc/self/exe", O_RDONLY | O_CLOEXEC);
115 if (fd < 0)
fed8112d 116 return;
6400238d
CB
117
118 /* sendfile() handles up to 2GB. */
5e6e38bd
CB
119 ret = fstat(fd, &st);
120 if (ret)
121 return;
4aa90f60 122
5e6e38bd
CB
123 while (bytes_sent < st.st_size) {
124 ssize_t sent;
4aa90f60 125
5e6e38bd
CB
126 sent = lxc_sendfile_nointr(memfd >= 0 ? memfd : tmpfd, fd, NULL,
127 st.st_size - bytes_sent);
128 if (sent < 0) {
129 /* Fallback to shoveling data between kernel- and
130 * userspace.
131 */
132 lseek(fd, 0, SEEK_SET);
133 if (fd_to_fd(fd, memfd >= 0 ? memfd : tmpfd))
134 break;
135
136 return;
4aa90f60 137 }
5e6e38bd 138 bytes_sent += sent;
4aa90f60 139 }
4aa90f60
CB
140 close_prot_errno_disarm(fd);
141
4aa90f60 142 if (memfd >= 0) {
5e6e38bd
CB
143 if (fcntl(memfd, F_ADD_SEALS, LXC_MEMFD_REXEC_SEALS))
144 return;
145
146 execfd = memfd;
4aa90f60 147 } else {
4aa90f60
CB
148 char procfd[LXC_PROC_PID_FD_LEN];
149
150 ret = snprintf(procfd, sizeof(procfd), "/proc/self/fd/%d", tmpfd);
151 if (ret < 0 || (size_t)ret >= sizeof(procfd))
152 return;
153
154 execfd = open(procfd, O_PATH | O_CLOEXEC);
155 close_prot_errno_disarm(tmpfd);
4aa90f60 156
4aa90f60 157 }
5e6e38bd
CB
158 if (execfd < 0)
159 return;
160
161 fexecve(execfd, argv, envp);
6400238d
CB
162}
163
cee55b59
CB
164/*
165 * Get cheap access to the environment. This must be declared by the user as
166 * mandated by POSIX. The definition is located in unistd.h.
167 */
168extern char **environ;
169
7a80606d 170int lxc_rexec(const char *memfd_name)
6400238d
CB
171{
172 int ret;
cee55b59 173 char **argv = NULL;
6400238d
CB
174
175 ret = is_memfd();
176 if (ret < 0 && ret == -ENOTRECOVERABLE) {
177 fprintf(stderr,
178 "%s - Failed to determine whether this is a memfd\n",
179 strerror(errno));
180 return -1;
181 } else if (ret > 0) {
182 return 0;
183 }
184
cee55b59 185 ret = parse_argv(&argv);
7a80606d
CB
186 if (ret < 0) {
187 fprintf(stderr,
188 "%s - Failed to parse command line parameters\n",
189 strerror(errno));
190 return -1;
191 }
192
cee55b59 193 lxc_rexec_as_memfd(argv, environ, memfd_name);
6400238d
CB
194 fprintf(stderr, "%s - Failed to rexec as memfd\n", strerror(errno));
195 return -1;
196}
197
198/**
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.
204 */
7a80606d 205__attribute__((constructor)) static void liblxc_rexec(void)
6400238d 206{
7a80606d 207 if (getenv("LXC_MEMFD_REXEC") && lxc_rexec("liblxc")) {
6400238d
CB
208 fprintf(stderr, "Failed to re-execute liblxc via memory file descriptor\n");
209 _exit(EXIT_FAILURE);
210 }
211}