From ba996acfcad1698adbf4a1834fe50e73b1c6cab9 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Sat, 19 Sep 2026 23:28:22 -0300 Subject: Rename programs: 9player -> 9ns, introspect -> 9proc, app -> web (9web) Directories, binaries, build options (-D9ns, -D9proc), step names, module name (9proc), thread and fs names, env var NINEPLAYER_MOUNT -> NINE_MOUNT, docs and test scripts. Browser assets move to web/static. Co-Authored-By: Claude Fable 5.1 --- 9proc/test/adv_9proc_hostile.py | 999 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 999 insertions(+) create mode 100755 9proc/test/adv_9proc_hostile.py (limited to '9proc/test/adv_9proc_hostile.py') diff --git a/9proc/test/adv_9proc_hostile.py b/9proc/test/adv_9proc_hostile.py new file mode 100755 index 0000000..934e757 --- /dev/null +++ b/9proc/test/adv_9proc_hostile.py @@ -0,0 +1,999 @@ +#!/usr/bin/env python3 +"""Hostile raw-9P2000 client for the 9proc-demo server (stdlib only). + +Usage: + adv_9proc_hostile.py --server zig-out/bin/9proc-demo # spawns it on a temp unix socket + adv_9proc_hostile.py --socket PATH # attacks a running server + +Every attack is followed by a "server still healthy" probe on a fresh connection. +Exit status is non-zero if any check fails, the server dies, or a probe hangs. +""" +import argparse +import os +import signal +import socket +import struct +import subprocess +import sys +import tempfile +import threading +import time + +NOTAG = 0xFFFF +NOFID = 0xFFFFFFFF +Tversion, Rversion, Tauth, Rauth, Tattach, Rattach, Rerror = 100, 101, 102, 103, 104, 105, 107 +Tflush, Rflush, Twalk, Rwalk, Topen, Ropen, Tcreate, Rcreate = 108, 109, 110, 111, 112, 113, 114, 115 +Tread, Rread, Twrite, Rwrite, Tclunk, Rclunk, Tremove, Rremove = 116, 117, 118, 119, 120, 121, 122, 123 +Tstat, Rstat, Twstat, Rwstat = 124, 125, 126, 127 +OREAD, OWRITE, ORDWR, OEXEC, OTRUNC, ORCLOSE = 0, 1, 2, 3, 0x10, 0x40 +DMDIR, DMAPPEND, DMEXCL = 0x80000000, 0x40000000, 0x20000000 +NAMES = {v: k for k, v in globals().items() if k[:1] in "TR" and isinstance(v, int) and 100 <= v <= 127} + +FAILS = [] +PASSES = 0 + + +def ok(name, cond, detail=""): + global PASSES + if cond: + PASSES += 1 + print(f"ok - {name}") + else: + FAILS.append(name) + print(f"FAIL - {name} {detail}") + + +def s16(b): + return struct.pack(" connection closed + c.raw(frame(Twalk, 1, struct.pack("0 + ok("walk to file", c.walk_ok(0, 1, [b"build", b"target"]) == 2) + ok("walk from file fails 'not a directory'", c.err(Twalk, struct.pack(" 0) + ok("read dir at bad offset", c.err(Tread, struct.pack(" 6: + return + if c.walk_ok(0, 9, names) != len(names): + ok("walk " + b"/".join(names).decode(), False) + return + rt, st = c.stat(9) + if st["mode"] & DMDIR: + c.open(9, OREAD) + d = c.read_all(9, 512) + c.clunk(9) + while d: + n, = struct.unpack_from(" 0: + ok("server process still running", proc.poll() is None, proc.poll()) + finally: + if proc is not None: + proc.send_signal(signal.SIGTERM) + try: + _, err = proc.communicate(timeout=5) + except subprocess.TimeoutExpired: + proc.kill() + _, err = proc.communicate() + lines = [ln for ln in err.decode("utf-8", "replace").splitlines() if "connection ended" not in ln and "read: " not in ln] + if lines: + print("# server stderr (filtered):") + for ln in lines[:40]: + print(" " + ln) + if tmp: + try: + os.unlink(path) + os.rmdir(tmp) + except OSError: + pass + print(f"# {PASSES} passed, {len(FAILS)} failed") + for f in FAILS: + print("# FAIL " + f) + sys.exit(1 if FAILS else 0) + + +if __name__ == "__main__": + main() -- cgit v1.3