Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ endif
BIN = nsjail
LIBS = kafel/libkafel.a
TEST_BINS = tests/nstun_buffer_budget_test tests/nstun_policy_test tests/nstun_ip_test tests/nstun_udp_peer_test
PID_INIT_INJECT = tests/pid_init_failure_inject.so

# If PASTA_BIN_PATH is not provided in env, dynamically search for it if EMBED_PASTA is requested
# or fallback to it naturally.
Expand Down Expand Up @@ -89,6 +90,9 @@ endif
%.o: %.cc | $(SRCS_PB_H)
$(CXX) $(CXXFLAGS) $< -o $@

$(PID_INIT_INJECT): tests/pid_init_failure_inject.cc
$(CXX) -shared -fPIC -std=c++20 -Wall -Wextra -Werror $< -o $@ -ldl

# Protobuf Generation
# We only define the recipe for the .cc file to prevent race conditions.
$(SRCS_PB_CXX): $(SRCS_PROTO)
Expand All @@ -110,7 +114,7 @@ kafel/libkafel.a: kafel_init

# Utilities
clean:
$(RM) core Makefile.bak $(OBJS) $(SRCS_PB_CXX) $(SRCS_PB_H) $(SRCS_PB_O) $(BIN) $(TEST_BINS)
$(RM) core Makefile.bak $(OBJS) $(SRCS_PB_CXX) $(SRCS_PB_H) $(SRCS_PB_O) $(BIN) $(TEST_BINS) $(PID_INIT_INJECT)
ifneq ("$(wildcard kafel/Makefile)","")
+$(MAKE) -C kafel clean
endif
Expand Down Expand Up @@ -160,7 +164,7 @@ test-cmdline: $(BIN)
$(call run_test, ./nsjail --time_limit 0x10 --cgroup_mem_swap_max=-1 --user 1000:1000:1 --help > /dev/null, 0)

.PHONY: test
test: $(BIN) $(TEST_BINS) test-cmdline
test: $(BIN) $(TEST_BINS) $(PID_INIT_INJECT) test-cmdline
$(call run_test, ./tests/nstun_buffer_budget_test, 0)
$(call run_test, ./tests/nstun_policy_test, 0)
$(call run_test, ./tests/nstun_ip_test, 0)
Expand All @@ -174,6 +178,9 @@ ifeq ($(UID),0)
$(call run_test, setpriv --reuid 1000 --regid 1000 --clear-groups -- ./nsjail -q -Mo --user 65534 --group 65534 --disable_clone_newnet --disable_clone_newcgroup --disable_clone_newns --disable_clone_newpid --disable_clone_newipc --disable_clone_newuts --disable_proc -- /bin/true, 0)
$(call run_test, setsid --fork --wait ./nsjail -q -Me --disable_clone_newuser --disable_clone_newnet --disable_clone_newcgroup --disable_clone_newns --disable_clone_newpid --disable_clone_newipc --disable_clone_newuts --disable_proc --chroot / -- /bin/true, 0)
$(call run_test, strace -f -qq -e inject=setsid:error=EPERM ./nsjail -q -Me --disable_clone_newuser --disable_clone_newnet --disable_clone_newcgroup --disable_clone_newns --disable_clone_newpid --disable_clone_newipc --disable_clone_newuts --disable_proc --chroot / -- /bin/true, 255)
$(call run_test, NSJAIL_TEST_FAIL_INIT=pdeathsig LD_PRELOAD=$(CURDIR)/$(PID_INIT_INJECT) ./nsjail -q -Me --disable_clone_newuser --disable_clone_newnet --disable_clone_newcgroup --disable_clone_newns --disable_clone_newipc --disable_clone_newuts --disable_proc --chroot / -- /bin/true, 255)
$(call run_test, NSJAIL_TEST_FAIL_INIT=dumpable LD_PRELOAD=$(CURDIR)/$(PID_INIT_INJECT) ./nsjail -q -Me --disable_clone_newuser --disable_clone_newnet --disable_clone_newcgroup --disable_clone_newns --disable_clone_newipc --disable_clone_newuts --disable_proc --chroot / -- /bin/true, 255)
$(call run_test, NSJAIL_TEST_FAIL_INIT=sigaction LD_PRELOAD=$(CURDIR)/$(PID_INIT_INJECT) ./nsjail -q -Me --disable_clone_newuser --disable_clone_newnet --disable_clone_newcgroup --disable_clone_newns --disable_clone_newipc --disable_clone_newuts --disable_proc --chroot / -- /bin/true, 255)
endif
$(call run_test, ./nsjail -q -Mo --chroot / --user 99999 --group 99999 -- /bin/true < /tmp, 255)
$(call run_test, ./nsjail -q -Mo --chroot / --user 99999 --group 99999 --pass_fd 0 -- /bin/true < /tmp, 0)
Expand Down
41 changes: 37 additions & 4 deletions pid.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,18 @@

#include "pid.h"

#include <fcntl.h>
#include <linux/sched.h>
#include <sched.h>
#include <signal.h>
#include <stddef.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <unistd.h>

#include "logs.h"
#include "subproc.h"
#include "util.h"

namespace pid {

Expand All @@ -42,6 +45,11 @@ bool initNs(nsj_t* nsj) {
}

LOG_D("Creating a dummy 'init' process");
int status_pipe[2];
if (pipe2(status_pipe, O_CLOEXEC) == -1) {
PLOG_E("Couldn't create status pipe for dummy init process");
return false;
}

/*
* If -Me is used then we need to create permanent init inside PID ns, otherwise only the
Expand All @@ -50,22 +58,40 @@ bool initNs(nsj_t* nsj) {
*/
pid_t pid = subproc::cloneProc(CLONE_FS, 0);
if (pid == -1) {
close(status_pipe[0]);
close(status_pipe[1]);
PLOG_E("Couldn't create a dummy init process");
return false;
}
if (pid > 0) {
close(status_pipe[1]);
char status;
const bool initialized =
util::readFromFd(status_pipe[0], &status, sizeof(status)) == sizeof(status) &&
status == 'I';
close(status_pipe[0]);
if (!initialized) {
LOG_E("Dummy init process failed to initialize");
kill(pid, SIGKILL);
while (waitpid(pid, nullptr, __WALL) == -1 && errno == EINTR) {
}
return false;
}
LOG_D("Created a dummy 'init' process with PID=%d", pid);
return true;
}
close(status_pipe[0]);

if (prctl(PR_SET_PDEATHSIG, SIGKILL, 0UL, 0UL, 0UL) == -1) {
PLOG_W("(prctl(PR_SET_PDEATHSIG, SIGKILL) failed");
PLOG_E("prctl(PR_SET_PDEATHSIG, SIGKILL) failed");
_exit(EXIT_FAILURE);
}
if (prctl(PR_SET_NAME, "ns-init", 0UL, 0UL, 0UL) == -1) {
PLOG_W("(prctl(PR_SET_NAME, 'init') failed");
PLOG_W("prctl(PR_SET_NAME, 'init') failed");
}
if (prctl(PR_SET_DUMPABLE, 0UL, 0UL, 0UL, 0UL) == -1) {
PLOG_W("(prctl(PR_SET_DUMPABLE, 0) failed");
PLOG_E("prctl(PR_SET_DUMPABLE, 0) failed");
_exit(EXIT_FAILURE);
}

/* Act sort-a like a init by reaping zombie processes */
Expand All @@ -76,8 +102,15 @@ bool initNs(nsj_t* nsj) {
sigemptyset(&sa.sa_mask);

if (sigaction(SIGCHLD, &sa, NULL) == -1) {
PLOG_W("Couldn't set sighandler for SIGCHLD");
PLOG_E("Couldn't set sighandler for SIGCHLD");
_exit(EXIT_FAILURE);
}

const char initialized = 'I';
if (!util::writeToFd(status_pipe[1], &initialized, sizeof(initialized))) {
_exit(EXIT_FAILURE);
}
close(status_pipe[1]);

for (;;) {
pause();
Expand Down
49 changes: 49 additions & 0 deletions tests/pid_init_failure_inject.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#include <dlfcn.h>
#include <errno.h>
#include <linux/prctl.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>

namespace {

pid_t original_pid;

__attribute__((constructor)) void rememberOriginalPid() {
original_pid = getpid();
}

} // namespace

extern "C" int prctl(
int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) {
const char* failure = getenv("NSJAIL_TEST_FAIL_INIT");
if (getpid() != original_pid && failure != nullptr &&
((option == PR_SET_PDEATHSIG && strcmp(failure, "pdeathsig") == 0) ||
(option == PR_SET_DUMPABLE && strcmp(failure, "dumpable") == 0))) {
errno = EPERM;
return -1;
}
return syscall(SYS_prctl, option, arg2, arg3, arg4, arg5);
}

extern "C" int sigaction(int signum, const struct sigaction* act, struct sigaction* oldact) {
const char* failure = getenv("NSJAIL_TEST_FAIL_INIT");
if (getpid() != original_pid && signum == SIGCHLD && failure != nullptr &&
strcmp(failure, "sigaction") == 0) {
errno = EPERM;
return -1;
}

using Sigaction = int (*)(int, const struct sigaction*, struct sigaction*);
static const auto real_sigaction =
reinterpret_cast<Sigaction>(dlsym(RTLD_NEXT, "sigaction"));
if (real_sigaction == nullptr) {
errno = ENOSYS;
return -1;
}
return real_sigaction(signum, act, oldact);
}
Loading