175{
177
178 if (!fs::exists(worker_path) || !fs::is_regular_file(worker_path)) {
180 return result;
181 }
182
183 if (!fs::exists(input_path) || !fs::is_regular_file(input_path)) {
185 return result;
186 }
187
188 char tmp_template[] = "/tmp/bastionguard-archive-XXXXXX";
189 char* tmp_dir = ::mkdtemp(tmp_template);
190 if (!tmp_dir) {
192 return result;
193 }
194
195 const fs::path sandbox_root(tmp_dir);
196 const fs::path work_dir = sandbox_root / "work";
197 std::error_code ec;
198 fs::create_directories(work_dir, ec);
199 if (ec) {
201 fs::remove_all(sandbox_root, ec);
202 return result;
203 }
204
205 int out_pipe[2] = {-1, -1};
206 int err_pipe[2] = {-1, -1};
207
208 if (::pipe(out_pipe) != 0 || ::pipe(err_pipe) != 0) {
210 for (int fd : {out_pipe[0], out_pipe[1], err_pipe[0], err_pipe[1]}) {
211 if (fd >= 0) ::close(fd);
212 }
213 fs::remove_all(sandbox_root, ec);
214 return result;
215 }
216
217 const pid_t pid = ::fork();
218 if (pid < 0) {
220 ::close(out_pipe[0]);
221 ::close(out_pipe[1]);
222 ::close(err_pipe[0]);
223 ::close(err_pipe[1]);
224 fs::remove_all(sandbox_root, ec);
225 return result;
226 }
227
228 if (pid == 0) {
229 ::close(out_pipe[0]);
230 ::close(err_pipe[0]);
231
232 ::dup2(out_pipe[1], STDOUT_FILENO);
233 ::dup2(err_pipe[1], STDERR_FILENO);
234
235 ::close(out_pipe[1]);
236 ::close(err_pipe[1]);
237
239
240 std::vector<std::string> args = {
241 "bwrap",
242 "--die-with-parent",
243 "--new-session",
244 "--unshare-ipc",
245 "--unshare-pid",
246 "--unshare-uts",
247 "--unshare-cgroup-try",
248 "--proc", "/proc",
249 "--dev", "/dev",
250 "--ro-bind", "/usr", "/usr",
251 "--ro-bind", "/bin", "/bin",
252 "--ro-bind", "/lib", "/lib",
253 "--ro-bind", "/lib64", "/lib64",
254 "--tmpfs", "/tmp",
255 "--dir", "/work",
256 "--ro-bind", input_path, "/work/input.zip",
257 "--ro-bind", worker_path, "/worker",
258 "--chdir", "/work",
259 "--setenv", "HOME", "/tmp",
260 "--setenv", "TMPDIR", "/tmp",
261 "/worker",
262 "/work/input.zip"
263 };
264
265 std::vector<char*> argv;
266 argv.reserve(args.size() + 1);
267 for (auto& s : args) {
268 argv.push_back(s.data());
269 }
270 argv.push_back(nullptr);
271
272 ::execvp("bwrap", argv.data());
273 _exit(127);
274 }
275
277
278 ::close(out_pipe[1]);
279 ::close(err_pipe[1]);
280
281
282
283
284
287
288 const auto start = std::chrono::steady_clock::now();
289 bool child_done = false;
290 int status = 0;
291
292 bool out_eof = false;
293 bool err_eof = false;
294
295 const int effective_timeout = std::max(1, timeout_seconds);
296 while (true) {
297
298
299 if (!child_done) {
300 const pid_t w = ::waitpid(pid, &status, WNOHANG);
301 if (w == pid) {
302 child_done = true;
303 } else if (w < 0 && errno != EINTR) {
305
306 ::kill(pid, SIGKILL);
307 ::waitpid(pid, &status, 0);
308 break;
309 }
310 }
311
312
313 const auto now = std::chrono::steady_clock::now();
314 const auto elapsed =
315 std::chrono::duration_cast<std::chrono::seconds>(now - start).count();
316 if (elapsed >= effective_timeout && (!child_done || !out_eof || !err_eof)) {
317
318
320 if (!child_done) {
321 ::kill(pid, SIGKILL);
322 ::waitpid(pid, &status, 0);
323 child_done = true;
324 }
325 break;
326 }
327
328
329 struct pollfd fds[2];
330 int nfds = 0;
331 if (!out_eof) { fds[nfds].fd = out_pipe[0]; fds[nfds].events = POLLIN; ++nfds; }
332 if (!err_eof) { fds[nfds].fd = err_pipe[0]; fds[nfds].events = POLLIN; ++nfds; }
333
334 if (nfds > 0) {
335
336
337
338 const int poll_timeout_ms = child_done ? 100 : 50;
339 const int pr = ::poll(fds, nfds, poll_timeout_ms);
340
341 if (pr > 0) {
342 for (int i = 0; i < nfds; ++i) {
343 if (!(fds[i].revents & (POLLIN | POLLHUP | POLLERR))) continue;
344
345 bool eof_here = false;
346 if (fds[i].fd == out_pipe[0]) {
348 if (eof_here) out_eof = true;
349 } else if (fds[i].fd == err_pipe[0]) {
351 if (eof_here) err_eof = true;
352 }
353 }
354 }
355
356 }
357
358
359 if (child_done && (out_eof || nfds == 0) && (err_eof || nfds == 0)) {
360 break;
361 }
362
363
364
365 if (!child_done && nfds == 0) {
366 std::this_thread::sleep_for(std::chrono::milliseconds(50));
367 }
368 }
369
370
373
374 ::close(out_pipe[0]);
375 ::close(err_pipe[0]);
376
377 if (child_done) {
379 if (WIFEXITED(status)) {
381 } else if (WIFSIGNALED(status)) {
382 result.
exit_code = 128 + WTERMSIG(status);
383 }
384 }
385
386 fs::remove_all(sandbox_root, ec);
387 return result;
388}
void set_worker_rlimits()
bool drain_fd_nonblocking(int fd, std::string &out, bool &got_eof, bool &truncated)
bool set_nonblocking(int fd)