diff --git a/user/pidfdtest.c b/user/pidfdtest.c new file mode 100644 index 0000000..dc2083a --- /dev/null +++ b/user/pidfdtest.c @@ -0,0 +1,87 @@ +#include "ulib.h" + +/* + * Exercises pidfd.h end to end through the real syscall path + * (sysPidfdOpen/sysPidfdSend/sysPidfdWait) -- the fd-table-wrapped, + * generation-checked handle sched.c's Thread.generation and pidfd.c add + * as an ADDITIVE alternative to a raw pid, mirroring real Linux's own + * pidfd_open(2) (see pidfd.h's own file comment for the full design). + * Reuses killvictim.c (blocks forever in a real, syscall-level pipe read + * with no writer ever attaching, see killtest.c) as its target -- the + * same already-proven "genuinely stuck, only killable from outside" + * shape, no new victim binary needed. Fully synchronous like + * nicetest.c/date.c/timetest.c: no injected keystrokes, no shared shell + * channel, so there is no scheduling-timing race to flake on regardless + * of host load. + */ + +int main(void) +{ + print("pidfdtest: start, pid="); + printDec(userGetpid()); + print("\n"); + + if (userPidfdOpen(0) != -1) { + print("pidfdtest: FAIL pidfd on pid 0 should be rejected\n"); + return 1; + } + if (userPidfdOpen(999999) != -1) { + print("pidfdtest: FAIL pidfd on bogus pid should be rejected\n"); + return 1; + } + print("pidfdtest: bad-target rejected\n"); + + long victim = userSpawn("/bin/killvictim", 0); + if (victim < 0) { + print("pidfdtest: FAIL spawn killvictim\n"); + return 1; + } + + long pfd = userPidfdOpen(victim); + if (pfd < 0) { + print("pidfdtest: FAIL pidfd open\n"); + return 1; + } + print("pidfdtest: pidfd-open-ok\n"); + + /* Give the victim time to actually attach and reach its blocking read + -- same reasoning as killtest.c's own identical sleep. */ + userSleep(100); + + if (userPidfdSend(pfd, sigKill) != 0) { + print("pidfdtest: FAIL pidfd send sigKill\n"); + return 1; + } + print("pidfdtest: pidfd-send-ok\n"); + + long status = userPidfdWait(pfd); + print("pidfdtest: victim exit status="); + printDec(status); + print("\n"); + if (status != 9) { + print("pidfdtest: FAIL expected exit status 9\n"); + return 1; + } + print("pidfdtest: pidfd-wait-ok\n"); + + /* Already reaped by the wait above: a further send/wait on the SAME + handle must cleanly report the target as gone, not silently act on + whichever thread (if any) now occupies that exact numeric slot -- + the whole reason pidfd.h exists (see its own file comment) over + caching victim's raw pid and calling userSigSend(victim, ...)/ + userWait(victim) again, which killtest.c's own comment already + documents as unsafe once a slot is recycled. */ + if (userPidfdSend(pfd, sigUsr1) != -1) { + print("pidfdtest: FAIL send on reaped pidfd should fail\n"); + return 1; + } + if (userPidfdWait(pfd) != -1) { + print("pidfdtest: FAIL wait on reaped pidfd should fail\n"); + return 1; + } + print("pidfdtest: reaped-target-detected-ok\n"); + + userClose((int)pfd); + print("pidfdtest: PASS\n"); + return 0; +} diff --git a/user/pipetest.c b/user/pipetest.c new file mode 100644 index 0000000..f2580d5 --- /dev/null +++ b/user/pipetest.c @@ -0,0 +1,79 @@ +#include "ulib.h" + +static void printLong(const char* label, long value) +{ + print(label); + printDec(value); + print("\n"); +} + +int main(void) +{ + print("pipetest: start, pid="); + printDec(userGetpid()); + print("\n"); + + int fds[2]; + long rc = userPipe(fds); + printLong("pipetest: userPipe rc=", rc); + if (rc < 0) { + print("pipetest: pipe() failed\n"); + return 1; + } + int readFd = fds[0]; + int writeFd = fds[1]; + + /* nothing written yet: a read must report "would block" (-1), not EOF */ + char probe[4]; + long earlyRead = userRead(readFd, probe, sizeof(probe)); + printLong("pipetest: read before write rc=", earlyRead); + if (earlyRead != -1) { + print("pipetest: FAIL expected -1 on empty open pipe\n"); + return 1; + } + + const char* payload = "hello through the pipe\n"; + long written = userWrite(writeFd, payload, stringLength(payload)); + printLong("pipetest: wrote bytes=", written); + if (written != (long)stringLength(payload)) { + print("pipetest: FAIL short write\n"); + return 1; + } + + char buffer[64]; + long got = userRead(readFd, buffer, sizeof(buffer) - 1); + if (got < 0) { + print("pipetest: FAIL read after write blocked\n"); + return 1; + } + buffer[got] = 0; + print("pipetest: read back: "); + print(buffer); + + userClose(writeFd); + + /* buffer is now empty and the write end is closed: true EOF (0) */ + long afterClose = userRead(readFd, probe, sizeof(probe)); + printLong("pipetest: read after writer closed rc=", afterClose); + if (afterClose != 0) { + print("pipetest: FAIL expected EOF (0) after writer closed\n"); + return 1; + } + + userClose(readFd); + + /* a second independent pipe proves broken-pipe (write with no reader) */ + int fds2[2]; + userPipe(fds2); + userClose(fds2[0]); + long brokenWrite = userWrite(fds2[1], "x", 1); + printLong("pipetest: write after reader closed rc=", brokenWrite); + if (brokenWrite != -1) { + print("pipetest: FAIL expected broken pipe (-1)\n"); + return 1; + } + userClose(fds2[1]); + + print("pipetest: done\n"); + return 0; +}