feat: add eintrtarget and envprint
This commit is contained in:
1 parent
607eaff366
commit
6184cf43be
4 files changed
+179
No files matched your search
@@ -0,0 +1,61 @@
|
||||
#include "ulib.h"
|
||||
|
||||
/*
|
||||
* Only ever spawned by eintrtest.c. Attaches as the READ end of its own,
|
||||
* self-contained pipe (userPipeCreate/userPipeAttach) that no writer will
|
||||
* ever attach to -- the exact same "blocked forever" setup killtest.c's
|
||||
* killvictim.c already uses for threadKill -- but the way out here is an
|
||||
* ORDINARY signal (sigTerm, default disposition Terminate) going through
|
||||
* the FULL sigRaise -> pending-bit -> early-wake -> deliverPendingSignals
|
||||
* pipeline (signal.h/sched.c's threadWakeForSignal), not threadKill's
|
||||
* direct ThreadState bypass. Before that early-wake existed, sigRaise only
|
||||
* ever set a pending bit and left a Blocked thread exactly where it was --
|
||||
* delivery only happens at a return-to-ring3 this process would otherwise
|
||||
* never naturally reach again, so sigTerm sent here would have done
|
||||
* nothing observable, ever: the read below is never naturally satisfied,
|
||||
* by construction.
|
||||
*/
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
print("eintrtarget: start, pid=");
|
||||
printDec(userGetpid());
|
||||
print("\n");
|
||||
|
||||
if (argc != 2) {
|
||||
print("eintrtarget: FAIL expected one argv (ready handle)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
long victimHandle = userPipeCreate();
|
||||
if (victimHandle < 0) {
|
||||
print("eintrtarget: FAIL create victim pipe\n");
|
||||
return 1;
|
||||
}
|
||||
long victimFd = userPipeAttach(victimHandle, 0);
|
||||
if (victimFd < 0) {
|
||||
print("eintrtarget: FAIL attach victim pipe\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
long readyHandle = parseDec(argv[1]);
|
||||
long readyFd = userPipeAttach(readyHandle, 1);
|
||||
if (readyFd < 0) {
|
||||
print("eintrtarget: FAIL attach ready pipe\n");
|
||||
return 1;
|
||||
}
|
||||
if (userWrite((int)readyFd, "r", 1) != 1) {
|
||||
print("eintrtarget: FAIL write ready byte\n");
|
||||
return 1;
|
||||
}
|
||||
userClose((int)readyFd);
|
||||
|
||||
print("eintrtarget: blocking on read forever\n");
|
||||
char buffer[4];
|
||||
userRead((int)victimFd, buffer, sizeof(buffer));
|
||||
|
||||
/* Only reached if userRead somehow returned on its own -- eintrtest.c
|
||||
never writes to the victim pipe, so that would itself be a bug. */
|
||||
print("eintrtarget: FAIL read returned without being signaled\n");
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
#include "ulib.h"
|
||||
|
||||
/*
|
||||
* Live ring-3 proof for the real environment-variable subsystem (sched.h's
|
||||
* Thread.env, syscall.c's sysGetEnv/sysSetEnv/sysUnsetEnv, shell.c's
|
||||
* `export`/`env`/`unset` builtins and `$NAME` expansion) -- loaded on demand by
|
||||
* the real interactive shell (kmain.c's envDriverThread types
|
||||
* "export GREETING=frostbyte" then "envprint" at it, the same injected-
|
||||
* keystroke mechanism every shell-driven demo above already uses), never
|
||||
* boot-spawned, the same "no kmain.c spawnUser call, only /bin/NAME
|
||||
* resolved by the shell's own fallback" shape jobctlfg.c/hcat.c/pipecat.c
|
||||
* already established.
|
||||
*
|
||||
* Proves inheritance first (this process never called userSetEnv itself
|
||||
* before the first check -- whatever it sees came only from
|
||||
* threadCreateUser's own initialEnv copying the shell's table at spawn
|
||||
* time), then proves this process's OWN later userSetEnv is visible to
|
||||
* itself immediately, then proves userUnsetEnv actually removes the entry
|
||||
* (a following userGetEnv on the same name fails) and that unsetting a
|
||||
* name that was never set is still a no-op success, not an error. The
|
||||
* other half of the isolation guarantee -- that
|
||||
* this process's own mutation can never reach back into the PARENT
|
||||
* shell's copy -- is proven by envDriverThread itself: it types a plain
|
||||
* "env" at the shell AFTER this process has already exited and checks
|
||||
* that the shell still reports the ORIGINAL "frostbyte" value, not
|
||||
* "child-mutated" (mirrors cwdtest.c's own post-userWait
|
||||
* parent-cwd-unaffected check, just driven through the shell instead of a
|
||||
* direct userWait).
|
||||
*/
|
||||
|
||||
int main(void)
|
||||
{
|
||||
print("envprint: start, pid=");
|
||||
printDec(userGetpid());
|
||||
print("\n");
|
||||
|
||||
char value[32];
|
||||
long len = userGetEnv("GREETING", value, sizeof(value));
|
||||
if (len < 0) {
|
||||
print("envprint: FAIL missing inherited GREETING\n");
|
||||
return 1;
|
||||
}
|
||||
print("envprint: inherited-ok GREETING=");
|
||||
print(value);
|
||||
print("\n");
|
||||
|
||||
if (userGetEnv("NO_SUCH_VAR", value, sizeof(value)) >= 0) {
|
||||
print("envprint: FAIL unset variable returned a value\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (userSetEnv("GREETING", "child-mutated") != 0) {
|
||||
print("envprint: FAIL setenv\n");
|
||||
return 1;
|
||||
}
|
||||
len = userGetEnv("GREETING", value, sizeof(value));
|
||||
if (len < 0) {
|
||||
print("envprint: FAIL read back own setenv\n");
|
||||
return 1;
|
||||
}
|
||||
print("envprint: self-set-ok GREETING=");
|
||||
print(value);
|
||||
print("\n");
|
||||
|
||||
if (userUnsetEnv("GREETING") != 0) {
|
||||
print("envprint: FAIL unset\n");
|
||||
return 1;
|
||||
}
|
||||
if (userGetEnv("GREETING", value, sizeof(value)) >= 0) {
|
||||
print("envprint: FAIL unset did not remove variable\n");
|
||||
return 1;
|
||||
}
|
||||
print("envprint: unset-ok\n");
|
||||
|
||||
/* Real unsetenv(3) is a no-op success on a name that was never set --
|
||||
this must NOT be confused with a "not found" error. */
|
||||
if (userUnsetEnv("NO_SUCH_VAR_EVER") != 0) {
|
||||
print("envprint: FAIL unset-missing should be a no-op success\n");
|
||||
return 1;
|
||||
}
|
||||
print("envprint: unset-missing-ok\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#include "ulib.h"
|
||||
|
||||
/*
|
||||
* Never spawned directly by kmain.c or by proctest.c's userSpawn -- the
|
||||
* only way this binary's "exectarget: running" line can ever appear in the
|
||||
* boot log is if proctest.c's userExec("/bin/exectarget") call actually
|
||||
* replaced proctest's own image with this one (see proctest.c). Its pid is
|
||||
* therefore the SAME pid proctest was given at spawnUser time in kmain.c,
|
||||
* which is what kmain.c's execReaperThread checks via threadJoin to prove
|
||||
* exec() preserves process identity, not just that some program ran.
|
||||
*/
|
||||
|
||||
int main(void)
|
||||
{
|
||||
print("exectarget: running, pid=");
|
||||
printDec(userGetpid());
|
||||
print("\n");
|
||||
return 7;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#include "ulib.h"
|
||||
|
||||
/*
|
||||
* Real POSIX false(1): does nothing, always exits 1. Exists purely so
|
||||
* shell.c's new `&&`/`||` conditional chaining (execute()/executeSingle,
|
||||
* shellSplitAndOr in shell.h) has a genuinely SPAWNED process (real
|
||||
* fork-less spawn + real userExit, not just a builtin's own int return
|
||||
* like doExport/changeDir) whose nonzero exit status drives a real `&&`
|
||||
* skip followed by a real `||` fallback -- kmain.c's andOrDriverThread
|
||||
* types `false && echo x || echo ao2` at the live shell to prove it.
|
||||
*/
|
||||
int main(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
Reference in new issue
Block a user