166 lines
5.5 KiB
C
166 lines
5.5 KiB
C
// User-mode smoke test for POSIX process/thread runtime state and public headers.
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <pthread.h>
|
|
#include <dlfcn.h>
|
|
#include <pwd.h>
|
|
#include <grp.h>
|
|
#include <ntlite/syscall.h>
|
|
|
|
static volatile int gWorkerErrno;
|
|
static volatile uintptr_t gWorkerErrnoAddress;
|
|
static volatile int gWorkerDlErrorOk;
|
|
static volatile pid_t gWorkerPid;
|
|
|
|
// Writes one NUL-terminated string to stdout.
|
|
static void print(const char* text) {
|
|
(void)write(1, text, strlen(text));
|
|
}
|
|
|
|
// Terminates the smoke test with one diagnostic.
|
|
static void fail(const char* message) {
|
|
print("posix_runtime_smoke: ");
|
|
print(message);
|
|
print("\n");
|
|
exit(1);
|
|
}
|
|
|
|
// Mutates errno and dlerror in a second POSIX thread.
|
|
static void* worker(void* argument) {
|
|
(void)argument;
|
|
|
|
if (errno != 0)
|
|
return (void*)1;
|
|
|
|
gWorkerErrnoAddress = (uintptr_t)__ntl_posix_errno_location();
|
|
gWorkerPid = getpid();
|
|
errno = 222;
|
|
|
|
if (dlsym((void*)0, "worker_missing_symbol") != (void*)0)
|
|
return (void*)2;
|
|
char* error = dlerror();
|
|
if (!error || !error[0] || dlerror() != (char*)0)
|
|
return (void*)3;
|
|
|
|
gWorkerErrno = errno;
|
|
gWorkerDlErrorOk = 1;
|
|
return (void*)0x1234;
|
|
}
|
|
|
|
// Runs the POSIX runtime foundation smoke test.
|
|
int main(void) {
|
|
print("posix_runtime_smoke: start\n");
|
|
|
|
if (_POSIX_VERSION != 199009L)
|
|
fail("wrong POSIX profile version");
|
|
|
|
uintptr_t mainErrnoAddress = (uintptr_t)__ntl_posix_errno_location();
|
|
if (!mainErrnoAddress)
|
|
fail("main errno address is null");
|
|
|
|
pid_t stablePid = getpid();
|
|
if (stablePid <= 0)
|
|
fail("stable POSIX pid was invalid");
|
|
if ((uint32_t)stablePid == SysGetCurrentPid())
|
|
fail("getpid leaked native pid");
|
|
|
|
errno = 77;
|
|
if (dlsym((void*)0, "main_missing_symbol") != (void*)0)
|
|
fail("main dlsym unexpectedly succeeded");
|
|
if (!dlerror())
|
|
fail("main dlerror was not set");
|
|
|
|
// Re-arm one main-thread loader error so the worker can prove isolation.
|
|
if (dlsym((void*)0, "main_missing_symbol_2") != (void*)0)
|
|
fail("second main dlsym unexpectedly succeeded");
|
|
errno = 77;
|
|
|
|
pthread_t thread;
|
|
int createStatus = pthread_create(&thread, (const pthread_attr_t*)0, worker, (void*)0);
|
|
if (createStatus != 0)
|
|
fail("pthread_create failed");
|
|
|
|
void* result = (void*)0;
|
|
int joinStatus = pthread_join(thread, &result);
|
|
if (joinStatus != 0)
|
|
fail("pthread_join failed");
|
|
if (result != (void*)0x1234)
|
|
fail("worker returned wrong value");
|
|
|
|
if (gWorkerErrnoAddress == 0 || gWorkerErrnoAddress == mainErrnoAddress)
|
|
fail("errno storage was shared between threads");
|
|
if (gWorkerPid != stablePid)
|
|
fail("worker did not inherit logical POSIX process identity");
|
|
if (gWorkerErrno != EINVAL || !gWorkerDlErrorOk)
|
|
fail("worker thread-local state was wrong");
|
|
if (errno != 77)
|
|
fail("worker changed main errno");
|
|
|
|
char* mainError = dlerror();
|
|
if (!mainError || !mainError[0])
|
|
fail("worker consumed main dlerror");
|
|
if (dlerror() != (char*)0)
|
|
fail("main dlerror did not clear");
|
|
|
|
if (getuid() != 0 || geteuid() != 0 || getgid() != 0 || getegid() != 0)
|
|
fail("initial synthetic credentials were wrong");
|
|
if (getgroups(0, (gid_t*)0) != 0)
|
|
fail("initial supplementary group set was wrong");
|
|
char* login = getlogin();
|
|
if (!login || strcmp(login, "root") != 0)
|
|
fail("initial login identity was wrong");
|
|
|
|
struct passwd* root = getpwnam("root");
|
|
if (!root || root->pw_uid != 0 || root->pw_gid != 0 || strcmp(root->pw_name, "root") != 0)
|
|
fail("root passwd lookup failed");
|
|
struct passwd* user = getpwuid(1000);
|
|
if (!user || user->pw_uid != 1000 || user->pw_gid != 1000 || strcmp(user->pw_name, "user") != 0)
|
|
fail("user passwd lookup failed");
|
|
struct group* users = getgrnam("users");
|
|
if (!users || users->gr_gid != 1000 || strcmp(users->gr_name, "users") != 0 ||
|
|
!users->gr_mem || users->gr_mem[0] != (char*)0)
|
|
fail("group lookup failed");
|
|
if (getgrgid(0) == (struct group*)0)
|
|
fail("group id lookup failed");
|
|
|
|
if (getpgrp() != stablePid)
|
|
fail("initial process group was wrong");
|
|
errno = 0;
|
|
if (setsid() != (pid_t)-1 || errno != EPERM)
|
|
fail("session leader unexpectedly created another session");
|
|
errno = 0;
|
|
if (setpgid(0, 0) != -1 || errno != EPERM)
|
|
fail("session leader unexpectedly changed process group");
|
|
|
|
char originalCwd[256];
|
|
if (!getcwd(originalCwd, sizeof(originalCwd)))
|
|
fail("getcwd before chdir failed");
|
|
if (chdir("/") != 0)
|
|
fail("chdir root failed");
|
|
char rootCwd[16];
|
|
if (!getcwd(rootCwd, sizeof(rootCwd)) || strcmp(rootCwd, "/") != 0)
|
|
fail("chdir root did not update cwd");
|
|
if (chdir(originalCwd) != 0)
|
|
fail("cwd restore failed");
|
|
|
|
if (setgid(1000) != 0 || getgid() != 1000 || getegid() != 1000)
|
|
fail("root setgid failed");
|
|
if (setuid(1000) != 0 || getuid() != 1000 || geteuid() != 1000)
|
|
fail("root setuid failed");
|
|
errno = 0;
|
|
if (setgid(0) != -1 || errno != EPERM)
|
|
fail("dropped uid retained setgid authority");
|
|
errno = 0;
|
|
if (setuid(0) != -1 || errno != EPERM)
|
|
fail("dropped uid regained root");
|
|
if (!getlogin() || strcmp(getlogin(), "root") != 0)
|
|
fail("login identity changed with effective uid");
|
|
|
|
print("posix_runtime_smoke: PASS\n");
|
|
return 0;
|
|
}
|