Compare commits
8 Commits
main
...
assertions
Author | SHA1 | Date | |
---|---|---|---|
56b4deeb64 | |||
2c5d354bcd | |||
7b3f3a475a | |||
2c820c1dd7 | |||
c213d4eda2 | |||
3498e9b26a | |||
4277d58f26 | |||
6568720c6d |
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "extern/Unity"]
|
||||||
|
path = extern/Unity
|
||||||
|
url = https://github.com/ThrowTheSwitch/Unity.git
|
3
Makefile
Normal file
3
Makefile
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.PHONY: assertions
|
||||||
|
assertions:
|
||||||
|
$(MAKE) -C assertions/
|
2
assertions/.gitignore
vendored
Normal file
2
assertions/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
target/
|
||||||
|
test_runners/
|
57
assertions/Makefile
Normal file
57
assertions/Makefile
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
CLEANUP = rm -f
|
||||||
|
|
||||||
|
C_COMPILER=clang
|
||||||
|
|
||||||
|
UNITY_ROOT=../extern/Unity
|
||||||
|
|
||||||
|
CFLAGS = -std=c17
|
||||||
|
|
||||||
|
INC_DIRS=-I$(UNITY_ROOT)/src
|
||||||
|
|
||||||
|
SRC_NAMESPACES_FS_UNSHARE=$(UNITY_ROOT)/src/unity.c namespaces/fs/TestUnshare.c test_runners/namespaces_fs_TestUnshare_Runner.c
|
||||||
|
TARGET_NAMESPACES_FS_UNSHARE=target/test_namespaces_fs_unshare
|
||||||
|
|
||||||
|
SRC_NAMESPACES_FS_CLONE=$(UNITY_ROOT)/src/unity.c namespaces/fs/TestClone.c test_runners/namespaces_fs_TestClone_Runner.c
|
||||||
|
TARGET_NAMESPACES_FS_CLONE=target/test_namespaces_fs_clone
|
||||||
|
|
||||||
|
SRC_NAMESPACES_FILES_CLONE=$(UNITY_ROOT)/src/unity.c namespaces/files/TestClone.c test_runners/namespaces_files_TestClone_Runner.c
|
||||||
|
TARGET_NAMESPACES_FILES_CLONE=target/test_namespaces_files_clone
|
||||||
|
|
||||||
|
SRC_NAMESPACES_MOUNT_CLONE=$(UNITY_ROOT)/src/unity.c namespaces/mount/TestClone.c test_runners/namespaces_mount_TestClone_Runner.c
|
||||||
|
TARGET_NAMESPACES_MOUNT_CLONE=target/test_namespaces_mount_clone
|
||||||
|
|
||||||
|
|
||||||
|
all: clean default
|
||||||
|
|
||||||
|
default: $(SRC_NAMESPACES_FS_UNSHARE) $(SRC_NAMESPACES_FS_CLONE) $(SRC_NAMESPACES_FILES_CLONE) $(SRC_NAMESPACES_MOUNT_CLONE)
|
||||||
|
$(C_COMPILER) $(CFLAGS) $(INC_DIRS) $(SYMBOLS) $(SRC_NAMESPACES_FS_UNSHARE) -o $(TARGET_NAMESPACES_FS_UNSHARE)
|
||||||
|
$(C_COMPILER) $(CFLAGS) $(INC_DIRS) $(SYMBOLS) $(SRC_NAMESPACES_FS_CLONE) -o $(TARGET_NAMESPACES_FS_CLONE)
|
||||||
|
$(C_COMPILER) $(CFLAGS) $(INC_DIRS) $(SYMBOLS) $(SRC_NAMESPACES_FILES_CLONE) -o $(TARGET_NAMESPACES_FILES_CLONE)
|
||||||
|
$(C_COMPILER) $(CFLAGS) $(INC_DIRS) $(SYMBOLS) $(SRC_NAMESPACES_MOUNT_CLONE) -o $(TARGET_NAMESPACES_MOUNT_CLONE)
|
||||||
|
|
||||||
|
@echo
|
||||||
|
@echo Finished preparing tests, running now.
|
||||||
|
@echo
|
||||||
|
|
||||||
|
sudo setcap cap_sys_admin+eip $(TARGET_NAMESPACES_MOUNT_CLONE)
|
||||||
|
|
||||||
|
- ./$(TARGET_NAMESPACES_FS_UNSHARE)
|
||||||
|
- ./$(TARGET_NAMESPACES_FS_CLONE)
|
||||||
|
- ./$(TARGET_NAMESPACES_FILES_CLONE)
|
||||||
|
- ./$(TARGET_NAMESPACES_MOUNT_CLONE)
|
||||||
|
|
||||||
|
test_runners/namespaces_fs_TestUnshare_Runner.c: namespaces/fs/TestUnshare.c
|
||||||
|
ruby $(UNITY_ROOT)/auto/generate_test_runner.rb namespaces/fs/TestUnshare.c test_runners/namespaces_fs_TestUnshare_Runner.c
|
||||||
|
|
||||||
|
test_runners/namespaces_fs_TestClone_Runner.c: namespaces/fs/TestClone.c
|
||||||
|
ruby $(UNITY_ROOT)/auto/generate_test_runner.rb namespaces/fs/TestClone.c test_runners/namespaces_fs_TestClone_Runner.c
|
||||||
|
|
||||||
|
test_runners/namespaces_files_TestClone_Runner.c: namespaces/files/TestClone.c
|
||||||
|
ruby $(UNITY_ROOT)/auto/generate_test_runner.rb namespaces/files/TestClone.c test_runners/namespaces_files_TestClone_Runner.c
|
||||||
|
|
||||||
|
test_runners/namespaces_mount_TestClone_Runner.c: namespaces/mount/TestClone.c
|
||||||
|
ruby $(UNITY_ROOT)/auto/generate_test_runner.rb namespaces/mount/TestClone.c test_runners/namespaces_mount_TestClone_Runner.c
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(CLEANUP) $(TARGET_NAMESPACES_FS_UNSHARE) $(TARGET_NAMESPACES_FS_CLONE) $(TARGET_NAMESPACES_FILES_CLONE)
|
||||||
|
mkdir -p test_runners/ target/
|
199
assertions/namespaces/files/TestClone.c
Normal file
199
assertions/namespaces/files/TestClone.c
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
#include "unity.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <linux/wait.h>
|
||||||
|
|
||||||
|
#define STACK_SIZE (1024 * 1024)
|
||||||
|
|
||||||
|
static char *TMP_DIR = NULL;
|
||||||
|
|
||||||
|
void removeDirectory(char *dir) {
|
||||||
|
char cmd[256];
|
||||||
|
sprintf(cmd, "rm -r %s", dir);
|
||||||
|
TEST_ASSERT_EQUAL(0, system(cmd));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setUp(void) {
|
||||||
|
const char tmp_dir[] = "/tmp/tmpdir-XXXXXX";
|
||||||
|
|
||||||
|
TMP_DIR = malloc(sizeof(tmp_dir));
|
||||||
|
strcpy(TMP_DIR, tmp_dir);
|
||||||
|
|
||||||
|
TEST_ASSERT_NOT_EQUAL_MESSAGE(NULL, mkdtemp(TMP_DIR), "tmpdir failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
void tearDown(void) {
|
||||||
|
removeDirectory(TMP_DIR);
|
||||||
|
free(TMP_DIR);
|
||||||
|
TMP_DIR = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
long clone3(struct clone_args *cl_args) {
|
||||||
|
return syscall(SYS_clone3, cl_args, sizeof(struct clone_args));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_cloneNoFiles_writeToExistingFd_succeeds(void) {
|
||||||
|
// PREPARE
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
pid_t forkedChildPid;
|
||||||
|
|
||||||
|
if ((forkedChildPid = fork()) == 0) {
|
||||||
|
// child process - act but do not assert
|
||||||
|
// all assertions will be on the return code
|
||||||
|
|
||||||
|
// prepare fd
|
||||||
|
int dirfd = open(TMP_DIR, O_DIRECTORY);
|
||||||
|
if (dirfd < 0) {
|
||||||
|
exit(1); // open dir failed
|
||||||
|
}
|
||||||
|
int filefd = openat(dirfd, "write", O_WRONLY | O_CREAT, 0700);
|
||||||
|
if (filefd < 0) {
|
||||||
|
exit(1); // open file failed
|
||||||
|
}
|
||||||
|
if (close(dirfd) != 0) {
|
||||||
|
exit(7);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clone
|
||||||
|
int clonedChildPidFd;
|
||||||
|
|
||||||
|
struct clone_args cl_args = {
|
||||||
|
.flags = CLONE_PIDFD,
|
||||||
|
.pidfd = (uint64_t)(&clonedChildPidFd),
|
||||||
|
.child_tid = 0,
|
||||||
|
.parent_tid = 0,
|
||||||
|
.exit_signal = SIGCHLD,
|
||||||
|
.stack = 0,
|
||||||
|
.stack_size = 0,
|
||||||
|
.tls = 0,
|
||||||
|
.set_tid = 0,
|
||||||
|
.set_tid_size = 0,
|
||||||
|
.cgroup = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
long cloneResult = clone3(&cl_args);
|
||||||
|
if (cloneResult == 0) {
|
||||||
|
char buf[] = "hello world\n";
|
||||||
|
if (write(filefd, buf, 13) == -1) {
|
||||||
|
exit(9);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
} else if (cloneResult == -1) {
|
||||||
|
exit(2); // clone failed
|
||||||
|
}
|
||||||
|
|
||||||
|
siginfo_t status;
|
||||||
|
if (waitid(P_PIDFD, clonedChildPidFd, &status, WEXITED) == -1) {
|
||||||
|
exit(3); // wait failed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.si_status != 0) {
|
||||||
|
exit(status.si_status); // return status
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
TEST_ASSERT_GREATER_THAN_MESSAGE(0, forkedChildPid, "fork failed");
|
||||||
|
|
||||||
|
int status = 0;
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(forkedChildPid, waitpid(forkedChildPid, &status, 0),
|
||||||
|
"wait failed");
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(0, WEXITSTATUS(status), "return status non-zero");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_cloneFiles_writeToExistingFd_succeeds(void) {
|
||||||
|
// PREPARE
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
pid_t forkedChildPid;
|
||||||
|
|
||||||
|
if ((forkedChildPid = fork()) == 0) {
|
||||||
|
// child process - act but do not assert
|
||||||
|
// all assertions will be on the return code
|
||||||
|
|
||||||
|
// prepare fd
|
||||||
|
int dirfd = open(TMP_DIR, O_DIRECTORY);
|
||||||
|
if (dirfd < 0) {
|
||||||
|
exit(1); // open dir failed
|
||||||
|
}
|
||||||
|
int filefd = openat(dirfd, "write", O_WRONLY | O_CREAT, 0700);
|
||||||
|
if (filefd < 0) {
|
||||||
|
exit(1); // open file failed
|
||||||
|
}
|
||||||
|
if (close(dirfd) != 0) {
|
||||||
|
exit(7);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clone
|
||||||
|
int clonedChildPidFd;
|
||||||
|
|
||||||
|
struct clone_args cl_args = {
|
||||||
|
.flags = CLONE_PIDFD | CLONE_FILES,
|
||||||
|
.pidfd = (uint64_t)(&clonedChildPidFd),
|
||||||
|
.child_tid = 0,
|
||||||
|
.parent_tid = 0,
|
||||||
|
.exit_signal = SIGCHLD,
|
||||||
|
.stack = 0,
|
||||||
|
.stack_size = 0,
|
||||||
|
.tls = 0,
|
||||||
|
.set_tid = 0,
|
||||||
|
.set_tid_size = 0,
|
||||||
|
.cgroup = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
long cloneResult = clone3(&cl_args);
|
||||||
|
if (cloneResult == 0) {
|
||||||
|
char buf[] = "hello world\n";
|
||||||
|
if (write(filefd, buf, 13) == -1) {
|
||||||
|
exit(9);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
} else if (cloneResult == -1) {
|
||||||
|
exit(2); // clone failed
|
||||||
|
}
|
||||||
|
|
||||||
|
siginfo_t status;
|
||||||
|
if (waitid(P_PIDFD, clonedChildPidFd, &status, WEXITED) == -1) {
|
||||||
|
exit(3); // wait failed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.si_status != 0) {
|
||||||
|
exit(status.si_status); // return status
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
TEST_ASSERT_GREATER_THAN_MESSAGE(0, forkedChildPid, "fork failed");
|
||||||
|
|
||||||
|
int status = 0;
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(forkedChildPid, waitpid(forkedChildPid, &status, 0),
|
||||||
|
"wait failed");
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(0, WEXITSTATUS(status), "return status non-zero");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_cloneNoFiles_writeToNewFd_fails(void) { TEST_IGNORE_MESSAGE("TODO"); }
|
||||||
|
|
||||||
|
void test_cloneFiles_writeToNewFd_succeeds(void) {
|
||||||
|
TEST_IGNORE_MESSAGE("TODO");
|
||||||
|
}
|
185
assertions/namespaces/fs/TestClone.c
Normal file
185
assertions/namespaces/fs/TestClone.c
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
#include "unity.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <linux/wait.h>
|
||||||
|
|
||||||
|
static char *INITIAL_DIR = NULL;
|
||||||
|
static char *TMP_DIR = NULL;
|
||||||
|
|
||||||
|
void removeDirectory(char *dir) {
|
||||||
|
char cmd[256];
|
||||||
|
sprintf(cmd, "rm -r %s", dir);
|
||||||
|
TEST_ASSERT_EQUAL(0, system(cmd));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setUp(void) {
|
||||||
|
INITIAL_DIR = get_current_dir_name();
|
||||||
|
TEST_ASSERT_NOT_EQUAL_MESSAGE(NULL, INITIAL_DIR,
|
||||||
|
"get working directory failed");
|
||||||
|
|
||||||
|
const char tmp_dir[] = "/tmp/tmpdir-XXXXXX";
|
||||||
|
|
||||||
|
TMP_DIR = malloc(sizeof(tmp_dir));
|
||||||
|
strcpy(TMP_DIR, tmp_dir);
|
||||||
|
|
||||||
|
TEST_ASSERT_NOT_EQUAL_MESSAGE(NULL, mkdtemp(TMP_DIR), "tmpdir failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
void tearDown(void) {
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(0, chdir(INITIAL_DIR),
|
||||||
|
"returning to working directory failed");
|
||||||
|
free(INITIAL_DIR);
|
||||||
|
INITIAL_DIR = NULL;
|
||||||
|
|
||||||
|
removeDirectory(TMP_DIR);
|
||||||
|
free(TMP_DIR);
|
||||||
|
TMP_DIR = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
long clone3(struct clone_args *cl_args) {
|
||||||
|
return syscall(SYS_clone3, cl_args, sizeof(struct clone_args));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_cloneFs_chdir_doesPropagate(void) {
|
||||||
|
// PREPARE
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
pid_t forkedChildPid;
|
||||||
|
|
||||||
|
if ((forkedChildPid = fork()) == 0) {
|
||||||
|
// child process - act but do not assert
|
||||||
|
// all assertions will be on the return code
|
||||||
|
int clonedChildPidFd;
|
||||||
|
|
||||||
|
struct clone_args cl_args = {
|
||||||
|
.flags = CLONE_PIDFD | CLONE_FS,
|
||||||
|
.pidfd = (uint64_t)(&clonedChildPidFd),
|
||||||
|
.child_tid = 0,
|
||||||
|
.parent_tid = 0,
|
||||||
|
.exit_signal = SIGCHLD,
|
||||||
|
.stack = 0,
|
||||||
|
.stack_size = 0,
|
||||||
|
.tls = 0,
|
||||||
|
.set_tid = 0,
|
||||||
|
.set_tid_size = 0,
|
||||||
|
.cgroup = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
long cloneResult = clone3(&cl_args);
|
||||||
|
if (cloneResult == 0) {
|
||||||
|
if (chdir(TMP_DIR) != 0) {
|
||||||
|
exit(1); // chdir failed
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
} else if (cloneResult == -1) {
|
||||||
|
exit(2); // clone failed
|
||||||
|
}
|
||||||
|
|
||||||
|
siginfo_t status;
|
||||||
|
if (waitid(P_PIDFD, clonedChildPidFd, &status, WEXITED) == -1) {
|
||||||
|
exit(3); // wait failed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.si_status != 0) {
|
||||||
|
exit(status.si_status); // return status
|
||||||
|
}
|
||||||
|
|
||||||
|
char *cwd = get_current_dir_name();
|
||||||
|
int directory_moved = strcmp(cwd, TMP_DIR);
|
||||||
|
free(cwd);
|
||||||
|
|
||||||
|
if (directory_moved != 0) {
|
||||||
|
exit(4); // chdir did not propagate
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
TEST_ASSERT_GREATER_THAN_MESSAGE(0, forkedChildPid, "fork failed");
|
||||||
|
|
||||||
|
int status = 0;
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(forkedChildPid, waitpid(forkedChildPid, &status, 0),
|
||||||
|
"wait failed");
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(0, WEXITSTATUS(status), "return status non-zero");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_cloneNoFs_chdir_doesNotPropagate(void) {
|
||||||
|
// PREPARE
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
pid_t forkedChildPid;
|
||||||
|
|
||||||
|
if ((forkedChildPid = fork()) == 0) {
|
||||||
|
// child process - act but do not assert
|
||||||
|
// all assertions will be on the return code
|
||||||
|
int clonedChildPidFd;
|
||||||
|
|
||||||
|
struct clone_args cl_args = {
|
||||||
|
.flags = CLONE_PIDFD,
|
||||||
|
.pidfd = (uint64_t)(&clonedChildPidFd),
|
||||||
|
.child_tid = 0,
|
||||||
|
.parent_tid = 0,
|
||||||
|
.exit_signal = SIGCHLD,
|
||||||
|
.stack = 0,
|
||||||
|
.stack_size = 0,
|
||||||
|
.tls = 0,
|
||||||
|
.set_tid = 0,
|
||||||
|
.set_tid_size = 0,
|
||||||
|
.cgroup = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
long cloneResult = clone3(&cl_args);
|
||||||
|
if (cloneResult == 0) {
|
||||||
|
if (chdir(TMP_DIR) != 0) {
|
||||||
|
exit(1); // chdir failed
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
} else if (cloneResult == -1) {
|
||||||
|
exit(2); // clone failed
|
||||||
|
}
|
||||||
|
|
||||||
|
siginfo_t status;
|
||||||
|
if (waitid(P_PIDFD, clonedChildPidFd, &status, WEXITED) == -1) {
|
||||||
|
exit(3); // wait failed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.si_status != 0) {
|
||||||
|
exit(status.si_status); // return status
|
||||||
|
}
|
||||||
|
|
||||||
|
char *cwd = get_current_dir_name();
|
||||||
|
int directory_moved = strcmp(cwd, TMP_DIR);
|
||||||
|
free(cwd);
|
||||||
|
|
||||||
|
if (directory_moved == 0) {
|
||||||
|
exit(4); // chdir did propagate
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
TEST_ASSERT_GREATER_THAN_MESSAGE(0, forkedChildPid, "fork failed");
|
||||||
|
|
||||||
|
int status = 0;
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(forkedChildPid, waitpid(forkedChildPid, &status, 0),
|
||||||
|
"wait failed");
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(0, WEXITSTATUS(status), "return status non-zero");
|
||||||
|
}
|
323
assertions/namespaces/fs/TestUnshare.c
Normal file
323
assertions/namespaces/fs/TestUnshare.c
Normal file
@ -0,0 +1,323 @@
|
|||||||
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
#include "unity.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <linux/wait.h>
|
||||||
|
|
||||||
|
static char *INITIAL_DIR = NULL;
|
||||||
|
static char *TMP_DIR = NULL;
|
||||||
|
|
||||||
|
void removeDirectory(char *dir) {
|
||||||
|
char cmd[256];
|
||||||
|
sprintf(cmd, "rm -r %s", dir);
|
||||||
|
TEST_ASSERT_EQUAL(0, system(cmd));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setUp(void) {
|
||||||
|
INITIAL_DIR = get_current_dir_name();
|
||||||
|
TEST_ASSERT_NOT_EQUAL_MESSAGE(NULL, INITIAL_DIR,
|
||||||
|
"get working directory failed");
|
||||||
|
|
||||||
|
const char tmp_dir[] = "/tmp/tmpdir-XXXXXX";
|
||||||
|
|
||||||
|
TMP_DIR = malloc(sizeof(tmp_dir));
|
||||||
|
strcpy(TMP_DIR, tmp_dir);
|
||||||
|
|
||||||
|
TEST_ASSERT_NOT_EQUAL_MESSAGE(NULL, mkdtemp(TMP_DIR), "tmpdir failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
void tearDown(void) {
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(0, chdir(INITIAL_DIR),
|
||||||
|
"returning to working directory failed");
|
||||||
|
free(INITIAL_DIR);
|
||||||
|
INITIAL_DIR = NULL;
|
||||||
|
|
||||||
|
removeDirectory(TMP_DIR);
|
||||||
|
free(TMP_DIR);
|
||||||
|
TMP_DIR = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
long clone3(struct clone_args *cl_args) {
|
||||||
|
return syscall(SYS_clone3, cl_args, sizeof(struct clone_args));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_unshareNoFsFromFork_chdir_doesNotPropagate(void) {
|
||||||
|
// PREPARE
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
pid_t forkedChildPid;
|
||||||
|
|
||||||
|
if ((forkedChildPid = fork()) == 0) {
|
||||||
|
// child process - act but do not assert
|
||||||
|
// all assertions will be on the return code
|
||||||
|
pid_t clonedChildPid;
|
||||||
|
|
||||||
|
if ((clonedChildPid = fork()) == 0) {
|
||||||
|
if (unshare(0) == -1) {
|
||||||
|
exit(1); // unshare failed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chdir(TMP_DIR) != 0) {
|
||||||
|
exit(2); // chdir failed
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
} else if (clonedChildPid == -1) {
|
||||||
|
exit(3); // fork failed
|
||||||
|
}
|
||||||
|
|
||||||
|
siginfo_t status;
|
||||||
|
if (waitid(P_PID, clonedChildPid, &status, WEXITED) == -1) {
|
||||||
|
exit(4); // wait failed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.si_status != 0) {
|
||||||
|
exit(status.si_status); // return status
|
||||||
|
}
|
||||||
|
|
||||||
|
char *cwd = get_current_dir_name();
|
||||||
|
int directory_moved = strcmp(cwd, TMP_DIR);
|
||||||
|
free(cwd);
|
||||||
|
|
||||||
|
if (directory_moved == 0) {
|
||||||
|
exit(5); // chdir did propagate
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
TEST_ASSERT_GREATER_THAN_MESSAGE(0, forkedChildPid, "fork failed");
|
||||||
|
|
||||||
|
int status = 0;
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(forkedChildPid, waitpid(forkedChildPid, &status, 0),
|
||||||
|
"wait failed");
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(0, WEXITSTATUS(status), "return status non-zero");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_unshareFsFromFork_chdir_doesNotPropagate(void) {
|
||||||
|
// PREPARE
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
pid_t forkedChildPid;
|
||||||
|
|
||||||
|
if ((forkedChildPid = fork()) == 0) {
|
||||||
|
// child process - act but do not assert
|
||||||
|
// all assertions will be on the return code
|
||||||
|
pid_t clonedChildPid;
|
||||||
|
|
||||||
|
if ((clonedChildPid = fork()) == 0) {
|
||||||
|
if (unshare(CLONE_FS) == -1) {
|
||||||
|
exit(1); // unshare failed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chdir(TMP_DIR) != 0) {
|
||||||
|
exit(2); // chdir failed
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
} else if (clonedChildPid == -1) {
|
||||||
|
exit(3); // fork failed
|
||||||
|
}
|
||||||
|
|
||||||
|
siginfo_t status;
|
||||||
|
if (waitid(P_PID, clonedChildPid, &status, WEXITED) == -1) {
|
||||||
|
exit(4); // wait failed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.si_status != 0) {
|
||||||
|
exit(status.si_status); // return status
|
||||||
|
}
|
||||||
|
|
||||||
|
char *cwd = get_current_dir_name();
|
||||||
|
int directory_moved = strcmp(cwd, TMP_DIR);
|
||||||
|
free(cwd);
|
||||||
|
|
||||||
|
if (directory_moved == 0) {
|
||||||
|
exit(5); // chdir did propagate
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
TEST_ASSERT_GREATER_THAN_MESSAGE(0, forkedChildPid, "fork failed");
|
||||||
|
|
||||||
|
int status = 0;
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(forkedChildPid, waitpid(forkedChildPid, &status, 0),
|
||||||
|
"wait failed");
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(0, WEXITSTATUS(status), "return status non-zero");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_unshareNoFsFromClone_chdir_doesPropagate(void) {
|
||||||
|
// PREPARE
|
||||||
|
char subDirA[21];
|
||||||
|
TEST_ASSERT_GREATER_THAN(0, sprintf(subDirA, "%s/a", TMP_DIR));
|
||||||
|
TEST_ASSERT_EQUAL(0, mkdir(subDirA, 0700));
|
||||||
|
|
||||||
|
char subDirB[21];
|
||||||
|
TEST_ASSERT_GREATER_THAN(0, sprintf(subDirB, "%s/b", TMP_DIR));
|
||||||
|
TEST_ASSERT_EQUAL(0, mkdir(subDirB, 0700));
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
pid_t forkedChildPid;
|
||||||
|
|
||||||
|
if ((forkedChildPid = fork()) == 0) {
|
||||||
|
// child process - act but do not assert
|
||||||
|
// all assertions will be on the return code
|
||||||
|
int clonedChildPidFd;
|
||||||
|
|
||||||
|
struct clone_args cl_args = {
|
||||||
|
.flags = CLONE_PIDFD | CLONE_FS,
|
||||||
|
.pidfd = (uint64_t)(&clonedChildPidFd),
|
||||||
|
.child_tid = 0,
|
||||||
|
.parent_tid = 0,
|
||||||
|
.exit_signal = SIGCHLD,
|
||||||
|
.stack = 0,
|
||||||
|
.stack_size = 0,
|
||||||
|
.tls = 0,
|
||||||
|
.set_tid = 0,
|
||||||
|
.set_tid_size = 0,
|
||||||
|
.cgroup = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
long cloneResult = clone3(&cl_args);
|
||||||
|
if (cloneResult == 0) {
|
||||||
|
if (chdir(subDirA) != 0) {
|
||||||
|
exit(1); // chdir failed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unshare(0) == -1) {
|
||||||
|
exit(1); // unshare failed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chdir(subDirB) != 0) {
|
||||||
|
exit(1); // chdir failed
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
} else if (cloneResult == -1) {
|
||||||
|
exit(2); // clone failed
|
||||||
|
}
|
||||||
|
|
||||||
|
siginfo_t status;
|
||||||
|
if (waitid(P_PIDFD, clonedChildPidFd, &status, WEXITED) == -1) {
|
||||||
|
exit(3); // wait failed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.si_status != 0) {
|
||||||
|
exit(status.si_status); // return status
|
||||||
|
}
|
||||||
|
|
||||||
|
char *cwd = get_current_dir_name();
|
||||||
|
int directory_moved = strcmp(cwd, subDirB);
|
||||||
|
free(cwd);
|
||||||
|
|
||||||
|
if (directory_moved != 0) {
|
||||||
|
exit(4); // chdir propagated unexpectedly
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
TEST_ASSERT_GREATER_THAN_MESSAGE(0, forkedChildPid, "fork failed");
|
||||||
|
|
||||||
|
int status = 0;
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(forkedChildPid, waitpid(forkedChildPid, &status, 0),
|
||||||
|
"wait failed");
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(0, WEXITSTATUS(status), "return status non-zero");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_unshareFsFromClone_chdir_doesNotPropagate(void) {
|
||||||
|
// PREPARE
|
||||||
|
char subDirA[21];
|
||||||
|
TEST_ASSERT_GREATER_THAN(0, sprintf(subDirA, "%s/a", TMP_DIR));
|
||||||
|
TEST_ASSERT_EQUAL(0, mkdir(subDirA, 0700));
|
||||||
|
|
||||||
|
char subDirB[21];
|
||||||
|
TEST_ASSERT_GREATER_THAN(0, sprintf(subDirB, "%s/b", TMP_DIR));
|
||||||
|
TEST_ASSERT_EQUAL(0, mkdir(subDirB, 0700));
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
pid_t forkedChildPid;
|
||||||
|
|
||||||
|
if ((forkedChildPid = fork()) == 0) {
|
||||||
|
// child process - act but do not assert
|
||||||
|
// all assertions will be on the return code
|
||||||
|
int clonedChildPidFd;
|
||||||
|
|
||||||
|
struct clone_args cl_args = {
|
||||||
|
.flags = CLONE_PIDFD | CLONE_FS,
|
||||||
|
.pidfd = (uint64_t)(&clonedChildPidFd),
|
||||||
|
.child_tid = 0,
|
||||||
|
.parent_tid = 0,
|
||||||
|
.exit_signal = SIGCHLD,
|
||||||
|
.stack = 0,
|
||||||
|
.stack_size = 0,
|
||||||
|
.tls = 0,
|
||||||
|
.set_tid = 0,
|
||||||
|
.set_tid_size = 0,
|
||||||
|
.cgroup = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
long cloneResult = clone3(&cl_args);
|
||||||
|
if (cloneResult == 0) {
|
||||||
|
if (chdir(subDirA) != 0) {
|
||||||
|
exit(1); // chdir failed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unshare(CLONE_FS) == -1) {
|
||||||
|
exit(1); // unshare failed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chdir(subDirB) != 0) {
|
||||||
|
exit(1); // chdir failed
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
} else if (cloneResult == -1) {
|
||||||
|
exit(2); // clone failed
|
||||||
|
}
|
||||||
|
|
||||||
|
siginfo_t status;
|
||||||
|
if (waitid(P_PIDFD, clonedChildPidFd, &status, WEXITED) == -1) {
|
||||||
|
exit(3); // wait failed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.si_status != 0) {
|
||||||
|
exit(status.si_status); // return status
|
||||||
|
}
|
||||||
|
|
||||||
|
char *cwd = get_current_dir_name();
|
||||||
|
int directory_moved = strcmp(cwd, subDirA);
|
||||||
|
free(cwd);
|
||||||
|
|
||||||
|
if (directory_moved != 0) {
|
||||||
|
exit(4); // chdir propagated unexpectedly
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
TEST_ASSERT_GREATER_THAN_MESSAGE(0, forkedChildPid, "fork failed");
|
||||||
|
|
||||||
|
int status = 0;
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(forkedChildPid, waitpid(forkedChildPid, &status, 0),
|
||||||
|
"wait failed");
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(0, WEXITSTATUS(status), "return status non-zero");
|
||||||
|
}
|
140
assertions/namespaces/mount/TestClone.c
Normal file
140
assertions/namespaces/mount/TestClone.c
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
#include "unity.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <linux/wait.h>
|
||||||
|
|
||||||
|
static char *TMP_DIR = NULL;
|
||||||
|
|
||||||
|
void removeDirectory(char *dir) {
|
||||||
|
char cmd[256];
|
||||||
|
sprintf(cmd, "rm -r %s", dir);
|
||||||
|
TEST_ASSERT_EQUAL(0, system(cmd));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setUp(void) {
|
||||||
|
const char tmp_dir[] = "/var/tmp/tmpdir-XXXXXX";
|
||||||
|
|
||||||
|
TMP_DIR = malloc(sizeof(tmp_dir));
|
||||||
|
strcpy(TMP_DIR, tmp_dir);
|
||||||
|
|
||||||
|
TEST_ASSERT_NOT_EQUAL_MESSAGE(NULL, mkdtemp(TMP_DIR), "tmpdir failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
void tearDown(void) {
|
||||||
|
removeDirectory(TMP_DIR);
|
||||||
|
free(TMP_DIR);
|
||||||
|
TMP_DIR = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
long clone3(struct clone_args *cl_args) {
|
||||||
|
return syscall(SYS_clone3, cl_args, sizeof(struct clone_args));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_cloneMount_mount_doesNotPropagate(void) {
|
||||||
|
// PREPARE
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
pid_t forkedChildPid;
|
||||||
|
|
||||||
|
if ((forkedChildPid = fork()) == 0) {
|
||||||
|
// child process - act but do not assert
|
||||||
|
// all assertions will be on the return code
|
||||||
|
if (mount(TMP_DIR, TMP_DIR, NULL, MS_BIND | MS_PRIVATE, NULL) != 0) {
|
||||||
|
exit(12); // bind mount failed
|
||||||
|
}
|
||||||
|
|
||||||
|
char *tmpDirMount = malloc(64);
|
||||||
|
|
||||||
|
int clonedChildPidFd;
|
||||||
|
|
||||||
|
struct clone_args cl_args = {
|
||||||
|
.flags = CLONE_PIDFD | CLONE_NEWNS,
|
||||||
|
.pidfd = (uint64_t)(&clonedChildPidFd),
|
||||||
|
.child_tid = 0,
|
||||||
|
.parent_tid = 0,
|
||||||
|
.exit_signal = SIGCHLD,
|
||||||
|
.stack = 0,
|
||||||
|
.stack_size = 0,
|
||||||
|
.tls = 0,
|
||||||
|
.set_tid = 0,
|
||||||
|
.set_tid_size = 0,
|
||||||
|
.cgroup = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
long cloneResult = clone3(&cl_args);
|
||||||
|
if (cloneResult == 0) {
|
||||||
|
if (mount(NULL, TMP_DIR, "tmpfs", 0, NULL) != 0) {
|
||||||
|
exit(1); // mount failed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mount(NULL, TMP_DIR, NULL, MS_PRIVATE, NULL) != 0) {
|
||||||
|
exit(10); // mount permission change failed
|
||||||
|
}
|
||||||
|
|
||||||
|
int dirfd;
|
||||||
|
if ((dirfd = open(TMP_DIR, O_DIRECTORY)) < 0) {
|
||||||
|
exit(1); // dir open failed
|
||||||
|
}
|
||||||
|
|
||||||
|
int filefd;
|
||||||
|
if ((filefd = openat(dirfd, "touched", O_WRONLY | O_CREAT, 0700)) < 0) {
|
||||||
|
exit(1); // file open failed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (close(filefd) != 0 || close(dirfd) != 0) {
|
||||||
|
exit(1); // close failed
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
} else if (cloneResult == -1) {
|
||||||
|
exit(2); // clone failed
|
||||||
|
}
|
||||||
|
|
||||||
|
siginfo_t status;
|
||||||
|
if (waitid(P_PIDFD, clonedChildPidFd, &status, WEXITED) == -1) {
|
||||||
|
exit(3); // wait failed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.si_status != 0) {
|
||||||
|
exit(status.si_status); // return status
|
||||||
|
}
|
||||||
|
|
||||||
|
int dirfd;
|
||||||
|
if ((dirfd = open(TMP_DIR, O_DIRECTORY)) < 0) {
|
||||||
|
exit(1); // dir open failed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (faccessat(dirfd, "touched", F_OK, 0) == 0) {
|
||||||
|
exit(9); // file in foreign namespace mount exists
|
||||||
|
}
|
||||||
|
|
||||||
|
if (umount(TMP_DIR) != 0) {
|
||||||
|
exit(11); // unmount failed
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
TEST_ASSERT_GREATER_THAN_MESSAGE(0, forkedChildPid, "fork failed");
|
||||||
|
|
||||||
|
int status = 0;
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(forkedChildPid, waitpid(forkedChildPid, &status, 0),
|
||||||
|
"wait failed");
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(0, WEXITSTATUS(status), "return status non-zero");
|
||||||
|
}
|
@ -95,10 +95,10 @@ int main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// all necessary files opened, drop filesystem
|
// all necessary files opened, drop filesystem
|
||||||
// if (unshare(CLONE_FS) == -1) {
|
if (unshare(CLONE_FS) == -1) {
|
||||||
// perror("unshare");
|
perror("unshare");
|
||||||
// return -1;
|
return -1;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// fork and copy in a second process
|
// fork and copy in a second process
|
||||||
pid_t child_pid = fork();
|
pid_t child_pid = fork();
|
||||||
|
1
extern/Unity
vendored
Submodule
1
extern/Unity
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 8ba01386008196a92ef4fdbdb0b00f2434c79563
|
Reference in New Issue
Block a user