#include "../tests.h" #include #include #include #include #include #include static void test_await(void) { /* creates 16 child processes, each returning a different value. then checks * if await() returns every value exactly once */ int ret; int counts[16] = {0}; for (int i = 0; i < 16; i++) if (!fork()) exit(i); while ((ret = _syscall_await()) != ~0) { test(0 <= ret && ret < 16); counts[ret]++; } for (int i = 0; i < 16; i++) test(counts[i] == 1); } static void test_pipe(void) { const char *pipe_msgs[2] = {"hello", "world"}; handle_t ends[2]; char buf[16]; /* test regular reads / writes */ test(_syscall_pipe(ends, 0) >= 0); if (!fork()) { /* those repeated asserts ensure that you can't read/write to the wrong ends */ test(_syscall_read(ends[1], buf, 16, 0) < 0); test(_syscall_write(ends[0], buf, 16, 0, 0) < 0); test(5 == _syscall_write(ends[1], pipe_msgs[0], 5, -1, 0)); test(_syscall_read(ends[1], buf, 16, 0) < 0); test(_syscall_write(ends[0], buf, 16, 0, 0) < 0); test(5 == _syscall_write(ends[1], pipe_msgs[1], 5, -1, 0)); exit(0); } else { test(_syscall_read(ends[1], buf, 16, 0) < 0); test(_syscall_write(ends[0], buf, 16, 0, 0) < 0); test(5 == _syscall_read(ends[0], buf, 16, 0)); test(!memcmp(buf, pipe_msgs[0], 5)); test(_syscall_read(ends[1], buf, 16, 0) < 0); test(_syscall_write(ends[0], buf, 16, 0, 0) < 0); test(5 == _syscall_read(ends[0], buf, 16, 0)); test(!memcmp(buf, pipe_msgs[1], 5)); _syscall_await(); } close(ends[0]); close(ends[1]); /* writing to pipes with one end closed */ test(_syscall_pipe(ends, 0) >= 0); for (int i = 0; i < 16; i++) { if (!fork()) { close(ends[1]); test(_syscall_read(ends[0], buf, 16, 0) < 0); exit(0); } } close(ends[1]); for (int i = 0; i < 16; i++) _syscall_await(); close(ends[0]); test(_syscall_pipe(ends, 0) >= 0); for (int i = 0; i < 16; i++) { if (!fork()) { close(ends[0]); test(_syscall_write(ends[1], buf, 16, 0, 0) < 0); exit(0); } } close(ends[0]); for (int i = 0; i < 16; i++) _syscall_await(); close(ends[1]); /* queueing */ test(_syscall_pipe(ends, 0) >= 0); for (int i = 0; i < 16; i++) { if (!fork()) { test(_syscall_write(ends[1], pipe_msgs[0], 5, -1, 0) == 5); exit(0); } } close(ends[1]); for (int i = 0; i < 16; i++) { test(_syscall_read(ends[0], buf, sizeof buf, 0) == 5); _syscall_await(); } test(_syscall_read(ends[0], buf, sizeof buf, 0) < 0); close(ends[0]); test(_syscall_pipe(ends, 0) >= 0); for (int i = 0; i < 16; i++) { if (!fork()) { memset(buf, 0, sizeof buf); test(_syscall_read(ends[0], buf, 5, -1) == 5); test(!memcmp(buf, pipe_msgs[1], 5)); exit(0); } } close(ends[0]); for (int i = 0; i < 16; i++) { test(_syscall_write(ends[1], pipe_msgs[1], 5, -1, 0) == 5); _syscall_await(); } test(_syscall_write(ends[1], pipe_msgs[1], 5, -1, 0) < 0); close(ends[1]); // not a to.do detect when all processes that can read are stuck on writing to the pipe and vice versa // it seems like linux just lets the process hang endlessly. } static void test_memflag(void) { void *page = (void*)0x77777000; _syscall_memflag(page, 4096, MEMFLAG_PRESENT); // allocate page memset(page, 77, 4096); // write to it _syscall_memflag(page, 4096, 0); // free it if (!fork()) { memset(page, 11, 4096); // should segfault exit(0); } else { test(_syscall_await() != 0); // test if the process crashed } char *pages[4]; for (size_t i = 0; i < 4; i++) { pages[i] = _syscall_memflag(NULL, 4096, MEMFLAG_FINDFREE); test(pages[i] == _syscall_memflag(NULL, 4096, MEMFLAG_FINDFREE | MEMFLAG_PRESENT)); if (i > 0) test(pages[i] != pages[i-1]); *pages[i] = 0x77; } for (size_t i = 0; i < 4; i++) _syscall_memflag(pages, 4096, MEMFLAG_PRESENT); } static void test_dup(void) { handle_t pipe[2]; handle_t h1, h2; test(_syscall_pipe(pipe, 0) >= 0); if (!fork()) { close(pipe[0]); h1 = _syscall_dup(pipe[1], -1, 0); test(h1 >= 0); test(h1 != pipe[1]); h2 = _syscall_dup(h1, -1, 0); test(h2 >= 0); test(h2 != pipe[1] && h2 != h1); _syscall_write(pipe[1], "og", 2, 0, 0); _syscall_write(h1, "h1", 2, 0, 0); _syscall_write(h2, "h2", 2, 0, 0); close(pipe[1]); _syscall_write(h1, "h1", 2, 0, 0); _syscall_write(h2, "h2", 2, 0, 0); test(_syscall_dup(h1, pipe[1], 0) == pipe[1]); test(_syscall_dup(h2, pipe[1], 0) == pipe[1]); test(_syscall_dup(h1, pipe[1], 0) == pipe[1]); test(_syscall_dup(h2, pipe[1], 0) == pipe[1]); close(h1); close(h2); test(_syscall_dup(pipe[1], h2, 0) == h2); _syscall_write(h2, "h2", 2, 0, 0); close(h2); test(_syscall_dup(pipe[1], h1, 0) == h1); _syscall_write(h1, "h1", 2, 0, 0); close(h1); exit(0); } else { char buf[16]; size_t count = 0; close(pipe[1]); while (_syscall_read(pipe[0], buf, sizeof buf, 0) >= 0) count++; test(count == 7); _syscall_await(); } close(pipe[0]); } static void test_execbuf(void) { // not really a test, TODO // also TODO check returning last syscall value const char str1[] = "test_execbuf: string 1\n"; const char str2[] = "test_execbuf: and 2\n"; uint64_t buf[] = { EXECBUF_SYSCALL, _SYSCALL_WRITE, 1, (uintptr_t)str1, sizeof(str1) - 1, -1, 0, EXECBUF_SYSCALL, _SYSCALL_WRITE, 1, (uintptr_t)str2, sizeof(str2) - 1, -1, 0, EXECBUF_SYSCALL, _SYSCALL_EXIT, 0, 0, 0, 0, 0, }; _syscall_execbuf(buf, sizeof buf); test_fail(); } static void test_sleep(void) { // TODO yet another of those fake tests that you have to verify by hand if (!fork()) { if (!fork()) { _syscall_sleep(100); printf("1\n"); _syscall_sleep(200); printf("3\n"); _syscall_sleep(200); printf("5\n"); _syscall_exit(0); } if (!fork()) { printf("0\n"); _syscall_sleep(200); printf("2\n"); _syscall_sleep(200); printf("4\n"); /* get killed while asleep * a peaceful death, i suppose. */ for (;;) _syscall_sleep(1000000000); } _syscall_await(); _syscall_exit(0); } /* this part checks if, after killing an asleep process, other processes can still wake up */ _syscall_sleep(600); printf("6\n"); } void r_k_miscsyscall(void) { run_test(test_await); run_test(test_pipe); run_test(test_memflag); run_test(test_dup); run_test(test_execbuf); run_test(test_sleep); }