Skip to content

Commit

Permalink
added pidns_tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ChibiDenDen authored and davidsaOpenu committed Jun 23, 2019
1 parent 094e4a8 commit 43794a7
Show file tree
Hide file tree
Showing 2 changed files with 363 additions and 1 deletion.
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ UPROGS=\
_sh\
_stressfs\
_usertests\
_pidns_tests\
_wc\
_zombie\

Expand Down Expand Up @@ -259,7 +260,7 @@ qemu-nox-gdb: fs.img xv6.img .gdbinit

EXTRA=\
mkfs.c ulib.c user.h cat.c echo.c forktest.c grep.c kill.c\
ln.c ls.c mkdir.c mounttest.c rm.c stressfs.c usertests.c wc.c zombie.c\
ln.c ls.c mkdir.c mounttest.c rm.c stressfs.c usertests.c pidns_tests.c wc.c zombie.c\
printf.c umalloc.c\
README dot-bochsrc *.pl toc.* runoff runoff1 runoff.list\
.gdbinit.tmpl gdbutil\
Expand Down
361 changes: 361 additions & 0 deletions pidns_tests.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,361 @@
#define _GNU_SOURCE
#include "syscall.h"
#include "types.h"
#include "user.h"
#include "wstatus.h"

#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))

#define CLONE_NEWPID 0x20000000
#define NULL 0

typedef signed int pid_t;
typedef signed int size_t;

int stderr = 2;

int check(int r, const char *msg) {
if (r < 0) {
printf(stderr, "%s\n", (char *)msg);
exit(1);
}

return r;
}

void assert_msg(int r, const char *msg) {
if (r) {
return;
}
printf(stderr, "%s\n", (char *)msg);
}

static int child_exit_status(int pid) {
int changed_pid = -1;
int wstatus;
do {
changed_pid = check(wait(&wstatus), "failed to waitpid");
} while (changed_pid != pid);

// TODO: there is no exit status in xv6
return WEXITSTATUS(wstatus);
}

static void write_all(int fd, unsigned char *buf, size_t len) {
size_t bytes_read = 0;

while (bytes_read < len) {
size_t ret =
check(write(fd, &buf[bytes_read], len - bytes_read), "failed to write");
bytes_read += ret;
}
}

static void read_all(int fd, unsigned char *buf, size_t len) {
size_t bytes_read = 0;

while (bytes_read < len) {
size_t ret =
check(read(fd, &buf[bytes_read], len - bytes_read), "failed to read");
bytes_read += ret;
}
}

/* static void wait_for(int fd, char *s) { */
/* char buf[0x80]; */
/* size_t len = strlen(s); */

/* if (len + 1 >= sizeof(buf)) { */
/* exit(1); */
/* } */
/* int ret = check(read(fd, buf, len + 1), "read"); */
/* if (ret != len + 1) { */
/* exit(1); */
/* } */
/* if (strcmp(s, buf) != 0) */
/* exit(1); */
/* } */

typedef int (*test_func_t)(void);

int test_simple_pidns_fork() {
check(unshare(CLONE_NEWPID), "failed to unshare");

int ret = check(fork(), "failed to fork");
// child
if (ret == 0) {
assert_msg(getpid() == 1, "pid not equal to 1");

ret = check(fork(), "failed to fork 2");
// child
if (ret == 0) {
assert_msg(getpid() == 2, "pid not equal to 2");
exit(0);
}

int status = child_exit_status(ret);
assert_msg(status == 0, "child process failed");
exit(0);
}

// flaky test because pid can recycle. However strictly speaking pid should be
// increasing
assert_msg(getpid() < ret, "wrong pid");

int status = child_exit_status(ret);
assert_msg(status == 0, "child process failed");
return 0;
}

int test_simple_pidns() {
check(unshare(CLONE_NEWPID), "failed to unshare");

int ret = check(fork(), "failed to fork");
// child
if (ret == 0) {
assert_msg(getpid() == 1, "pid not equal to 1");
exit(0);
}

// flaky test because pid can recycle. However strictly speaking pid should be
// increasing
assert_msg(getpid() < ret, "wrong pid");

int status = child_exit_status(ret);
assert_msg(status == 0, "child process failed");
return 0;
}

int test_nested_pidns_create() {
check(unshare(CLONE_NEWPID), "failed to unshare");

int ret = check(fork(), "failed to fork");
if (ret == 0) {
assert_msg(getpid() == 1, "pid not equal to 1");
exit(test_simple_pidns_fork());
}

int status = child_exit_status(ret);
assert_msg(status == 0, "child process failed");
return 0;
}

void create_children(int n, pid_t *child_pids, test_func_t func) {
for (int i = 0; i < n; i++) {
int ret = check(fork(), "failed to fork in create_children");
if (ret == 0) {
// child
exit(func());
}
if (child_pids) {
child_pids[i] = ret;
}
}
}

void reap_children(int n, pid_t *child_pids) {
int count = 0;
int wstatus = 0;
while (count < n) {
int pid = check(wait(&wstatus), "failed to wait()");

// if we don't verify the child pids just count them
if (!child_pids) {
count++;
continue;
}

for (int i = 0; i < n; i++) {
if (child_pids[i] == pid) {
count++;
assert_msg(WEXITSTATUS(wstatus) == 0, "exit code is not 0");
break;
}
}
}
}

int loop_forever() {
while (1) {
}
return 0;
}

int sleep_1s() {
// TODO: find a better way to sync the destruction
sleep(1);
return 0;
}

int test_children_reaped_by_nspid1() {
check(unshare(CLONE_NEWPID), "failed to unshare");
int ret = check(fork(), "failed to fork");
if (ret == 0) {
pid_t child_pids[2];
int fd[2];

check(pipe(fd), "failed to create pipes");

// child is pid 1
assert_msg(getpid() == 1, "pid not equal to 1");

// create pid 2
int ret = check(fork(), "failed to fork2");
if (ret == 0) {
create_children(ARRAY_SIZE(child_pids), child_pids, sleep_1s);
write_all(fd[1], (void *)child_pids, sizeof(child_pids));

// cleanup fds
close(fd[0]);
close(fd[1]);

// kill pid 2
exit(0);
}

read_all(fd[0], (void *)child_pids, sizeof(child_pids));

// cleanup fds
close(fd[0]);
close(fd[1]);

// reap child pids
reap_children(ARRAY_SIZE(child_pids), child_pids);
exit(0);
}

int status = child_exit_status(ret);
assert_msg(status == 0, "child process failed");
return 0;
}

int test_all_children_kill_when_nspid1_dies() {
check(unshare(CLONE_NEWPID), "failed to unshare");

int ret = check(fork(), "failed to fork");
if (ret == 0) {

// child is pid 1
assert_msg(getpid() == 1, "pid not equal to 1");

create_children(2, NULL, loop_forever);

// pid 1 exits
exit(0);
}

// Note: I'm supposed to only wait on one process sincew the other proccesses
// are reaped by the kernel (which is inconsistent and wierd).

// This test will fail on an infinite loop
int status = child_exit_status(ret);
assert_msg(status == 0, "child process failed");
return 0;
}

int test_calling_fork_after_nspid1_dies_fails() {
check(unshare(CLONE_NEWPID), "failed to unshare");

int ret = check(fork(), "failed to fork");
if (ret == 0) {

// child is pid 1
assert_msg(getpid() == 1, "pid not equal to 1");

// pid 1 exits
exit(0);
}

// make sure it's dead
int status = child_exit_status(ret);
assert_msg(status == 0, "child process failed");

ret = fork();
assert_msg(ret < 0, "fork didn't fail as expected");

return 0;
}

int MAX_RECURSION = 32;

int _test_unshare_recrusive_limit(int count) {
if (count == 0) {
assert_msg(unshare(CLONE_NEWPID) < 0, "unshare didn't fail as expected");
return 0;
}

check(unshare(CLONE_NEWPID), "failed to unshare");

int ret = check(fork(), "failed to fork");
if (ret == 0) {

// child is pid 1
assert_msg(getpid() == 1, "pid not equal to 1");

_test_unshare_recrusive_limit(count - 1);

// pid 1 exits
exit(0);
}

// make sure it's dead
int status = child_exit_status(ret);
assert_msg(status == 0, "child process failed");

return 0;
}

int test_unshare_recrusive_limit() {
_test_unshare_recrusive_limit(MAX_RECURSION);
return 0;
}

int unshare_twice() {
// first call should succeed
int ret = unshare(CLONE_NEWPID);
if (ret < 0) {
return 1;
}

// second call should fail
ret = unshare(CLONE_NEWPID);
if (ret < 0) {
return 0;
}

return 1;
}

int run_test(test_func_t func, const char *name) {
int status = 0;
int pid = -1;

printf(stderr, "running test - '%s'\n", name);
int ret = check(fork(), "fork failed");
if (ret == 0) {
exit(func());
}

pid = ret;
if (child_exit_status(pid) != 0) {
printf(stderr, "failed test - '%s'\n", name);
}

return status;
}

int main() {
run_test(unshare_twice, "unshare_twice");
run_test(test_simple_pidns, "test_simple_pidns");
run_test(test_simple_pidns_fork, "test_simple_pidns_fork");
run_test(test_nested_pidns_create, "test_nested_pidns_create");
run_test(test_children_reaped_by_nspid1, "test_children_reaped_by_nspid1");
run_test(test_all_children_kill_when_nspid1_dies,
"test_all_children_kill_when_nspid1_dies");
run_test(test_calling_fork_after_nspid1_dies_fails, "test_calling_fork_after_nspid1_dies_fails");
// run_test(test_calling_fork_recursive_after_nspid1_dies_fails, "test_calling_fork_recursive_after_nspid1_dies_fails");

run_test(test_unshare_recrusive_limit, "test_unshare_recrusive_limit");

return 0;
}

0 comments on commit 43794a7

Please sign in to comment.