From 412e4fd9a1f94c12979258bc38277577b30fb3a5 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Wed, 9 Sep 2026 10:47:28 +0200 Subject: [PATCH] feat: add streq --- user/mmapexecvictim.c | 40 +++++++++++++++++++++++++++++ user/mmapprotvictim.c | 40 +++++++++++++++++++++++++++++ user/namedreader.c | 60 +++++++++++++++++++++++++++++++++++++++++++ user/namedwriter.c | 42 ++++++++++++++++++++++++++++++ 4 files changed, 182 insertions(+) create mode 100644 user/mmapexecvictim.c create mode 100644 user/mmapprotvictim.c create mode 100644 user/namedreader.c create mode 100644 user/namedwriter.c diff --git a/user/mmapexecvictim.c b/user/mmapexecvictim.c new file mode 100644 index 0000000..f00663a --- /dev/null +++ b/user/mmapexecvictim.c @@ -0,0 +1,40 @@ +#include "ulib.h" + +/* + * Spawned by mmaptest.c to prove that a real jump into a mapping that never + * asked for mmapProtExec turns into a genuine SIGSEGV (isr.c's tryResolve- + * UserPageFault I/D-bit short-circuit, not an infinite fault loop and not + * a kernel-wide panic) rather than silently letting the CPU execute + * whatever bytes happen to sit there -- the exact NX enforcement gap + * fs.h's own mmapProtExec doc comment used to name as unimplemented. This + * process is expected to die, not return 0; its own final "FAIL" print/ + * return only runs if that protection was somehow NOT enforced. + */ + +typedef void (*victimFn)(void); + +int main(void) +{ + print("mmapexecvictim: start\n"); + + void* addr = userMmap(0, 4096, mmapProtRead | mmapProtWrite, mmapAnonymous, -1, 0); + if (addr == userMmapFailed) { + print("mmapexecvictim: FAIL mmap\n"); + return 1; + } + + /* A real `ret` instruction (0xC3) -- harmless if it ever actually runs, + since the point is that it must never get the chance to. */ + volatile unsigned char* code = (volatile unsigned char*)addr; + code[0] = 0xC3; + print("mmapexecvictim: write-ok\n"); + + /* PROT_READ|PROT_WRITE only -- no PROT_EXEC -- so this call's own + instruction fetch must be refused by the CPU's own NX bit and turn + into a real SIGSEGV delivered against THIS process alone. */ + victimFn fn = (victimFn)addr; + fn(); + + print("mmapexecvictim: FAIL exec should have segfaulted\n"); + return 1; +} diff --git a/user/mmapprotvictim.c b/user/mmapprotvictim.c new file mode 100644 index 0000000..6defdf2 --- /dev/null +++ b/user/mmapprotvictim.c @@ -0,0 +1,40 @@ +#include "ulib.h" + +/* + * Spawned by mmaptest.c to prove that a real write against a PROT_READ-only + * mapping turns into a genuine SIGSEGV (isr.c's #PF handler) rather than a + * kernel-wide panic -- this process is expected to die, not return 0. Its + * own final "FAIL" print/return only runs if that protection was somehow + * NOT enforced. + */ + +int main(void) +{ + print("mmapprotvictim: start\n"); + + int fd = userOpen("/etc/motd.txt", oReadOnly); + if (fd < 0) { + print("mmapprotvictim: FAIL open\n"); + return 1; + } + + void* addr = userMmap(0, 4096, mmapProtRead, 0, fd, 0); + if (addr == userMmapFailed) { + print("mmapprotvictim: FAIL mmap\n"); + return 1; + } + + volatile char* p = (volatile char*)addr; + if (p[0] != 'f') { + print("mmapprotvictim: FAIL unexpected read content\n"); + return 1; + } + print("mmapprotvictim: read-ok\n"); + + /* PROT_READ only -- this write must be refused by mmapFaultHandler and + turn into a real SIGSEGV delivered against THIS process alone. */ + p[0] = 'X'; + + print("mmapprotvictim: FAIL write should have segfaulted\n"); + return 1; +} diff --git a/user/namedreader.c b/user/namedreader.c new file mode 100644 index 0000000..4319fbe --- /dev/null +++ b/user/namedreader.c @@ -0,0 +1,60 @@ +#include "ulib.h" + +/* + * Boot-spawned independently of namedwriter.c (see kmain.c's spawnUser + * calls) -- unlike fiforeader.c/fifowriter.c, which fifomaster.c spawns as + * its own children and hands a shared pipe.h handle to via argv, this + * process and namedwriter.c have no relationship at all beyond both being + * launched at boot: neither one's pid, argv, or fd table ever mentions the + * other. The only thing they share is agreeing on the path "/tmp/rendezvous" + * (mkfifo'd by kmain.c before either is spawned) -- proving vfsMkfifo/ + * vfsOpen (vfs.h) really do let two unrelated processes rendezvous purely + * through the filesystem namespace, the gap FALA 11 explicitly left open. + * Opens for read immediately, well before namedwriter.c's deliberate sleep + * elapses, so the blocking read() below has to genuinely park across many + * timer ticks and be woken later -- not just win a race against data that + * was already sitting in the pipe. + */ + +#define namedPipePath "/tmp/rendezvous" + +static int streq(const char* a, const char* b) +{ + while (*a && *a == *b) { + a++; + b++; + } + return *a == *b; +} + +int main(void) +{ + print("namedreader: start, pid="); + printDec(userGetpid()); + print("\n"); + + long fd = userOpen(namedPipePath, oReadOnly); + if (fd < 0) { + print("namedreader: FAIL open\n"); + return 1; + } + + print("namedreader: opened, blocking on read\n"); + char buffer[64]; + long got = userRead((int)fd, buffer, sizeof(buffer) - 1); + if (got <= 0) { + print("namedreader: FAIL read\n"); + return 1; + } + buffer[got] = 0; + + print("namedreader: got: "); + print(buffer); + print("\n"); + + int ok = streq(buffer, "hello via named pipe"); + print(ok ? "namedreader: payload-ok\n" : "namedreader: payload-FAIL\n"); + + userClose((int)fd); + return ok ? 0 : 1; +} diff --git a/user/namedwriter.c b/user/namedwriter.c new file mode 100644 index 0000000..f9116ab --- /dev/null +++ b/user/namedwriter.c @@ -0,0 +1,42 @@ +#include "ulib.h" + +/* + * Sibling of namedreader.c -- see its header comment for the full picture. + * Sleeps well past when namedreader.c (spawned moments earlier, at boot) + * will already be parked in a blocking read() on the still-empty named + * pipe, so the write below has to actually wake it back up (schedWakeChannel + * via pipeWrite, pipe.c) instead of the reader simply finding data already + * waiting -- same proof pattern fifowriter.c uses for the handle-based + * shared pipe, now for a path-based one neither side had to be told about + * by a common parent. + */ + +#define namedPipePath "/tmp/rendezvous" +#define namedWriterDelayMs 200 + +static const char* payload = "hello via named pipe"; + +int main(void) +{ + print("namedwriter: start, pid="); + printDec(userGetpid()); + print("\n"); + + userSleep(namedWriterDelayMs); + + long fd = userOpen(namedPipePath, oWriteOnly); + if (fd < 0) { + print("namedwriter: FAIL open\n"); + return 1; + } + + long written = userWrite((int)fd, payload, stringLength(payload)); + if (written != (long)stringLength(payload)) { + print("namedwriter: FAIL short/failed write\n"); + return 1; + } + print("namedwriter: wrote payload\n"); + + userClose((int)fd); + return 0; +}