/* * vlibc — sys/resource.h test (todo 31). * * Exercises the whole surface end to end: * * 1. getrlimit(RLIMIT_NOFILE): returns 0 with a finite soft limit in * (0, max] — the kernel's own NOFILE ceiling, so cur is never * RLIM_INFINITY. getrlimit(RLIMIT_STACK) is the QA happy shape and * must also report a positive soft limit. * 2. setrlimit round-trip: re-installing the just-read RLIMIT_NOFILE * pair must succeed without privilege (identical values never raise * the hard limit), and a following getrlimit must reflect it. * 3. getrlimit64/setrlimit64 parity: the LFS aliases return the same * values and accept the same writes as the base functions. * 4. getrusage(RUSAGE_SELF) and RUSAGE_THREAD: ru_utime is a sane * timeval and ru_maxrss is non-negative. * 5. getpriority(PRIO_PROCESS, 0) lands in PRIO_MIN..PRIO_MAX, and * setpriority to the same value round-trips (equal values are never * a denied raise). * 6. nice(0) returns the current nice value: no change, no error. * 7. Constant sanity: RLIM_INFINITY is ~0UL, RLIMIT_NLIMITS is 16 and * PRIO_MIN/PRIO_MAX bound the range [-20, 19]. * * The -f mode runs the failure shapes — setrlimit/getrusage/getpriority/ * setpriority with a bogus resource kind or `which` — asserting return * values only (errno is never read: vlibc's errno slot collides with the * host TCB). It leaves via a raw SYS_exit_group so no host cleanup runs * after the library's errno writes (house pattern, tests/test_systime.c). * * No host libc headers are included (the -Iinclude path would shadow * GCC's internal headers); diagnostics go through raw SYS_write. The whole * body mirrors the header's gate: is entirely level 2, so * at level 1 this TU compiles to a no-op runner. * * Standalone build (list the src/resource/ files individually, or the * shell glob would trip -Wcomment in this header block): * gcc -Iinclude -DVLIBC_LEVEL=2 -std=c23 -Wall -Wextra -pedantic -O2 \ * -o /tmp/t31 tests/test_resource.c \ * src/resource/getrlimit.c src/resource/setrlimit.c \ * src/resource/getrusage.c src/resource/getpriority.c \ * src/resource/setpriority.c src/resource/nice.c \ * src/internal/errno.c src/internal/syscall_ret.c */ #include #include "../src/internal/syscall.h" /* Write a NUL-terminated string to fd via the raw syscall layer. The * optimize attribute keeps GCC from lowering the length loop into a * strlen call, which would leave a vlibc-owned symbol undefined in this * host-linked standalone binary (house idiom, see src/string). */ static __attribute__((optimize("no-tree-loop-distribute-patterns"))) void say(int fd, const char *s) { long n = 0; while (s[n] != '\0') { n++; } __syscall3(SYS_write, fd, (long)s, n); } #if VLIBC_LEVEL_GE(2) static int failures; static void check(int ok, const char *msg) { if (ok) { say(1, "ok "); } else { say(1, "FAIL "); failures++; } say(1, msg); say(1, "\n"); } /* 1. getrlimit reads sane, finite NOFILE and STACK soft limits. */ static void rlimit_read_scenario(void) { struct rlimit rl; check(getrlimit(RLIMIT_NOFILE, &rl) == 0, "getrlimit(RLIMIT_NOFILE) returns 0"); check(rl.rlim_cur > 0, "getrlimit NOFILE rlim_cur is positive"); check(rl.rlim_max > 0 && rl.rlim_max >= rl.rlim_cur, "getrlimit NOFILE rlim_max is positive and at least rlim_cur"); check(getrlimit(RLIMIT_STACK, &rl) == 0, "getrlimit(RLIMIT_STACK) returns 0"); check(rl.rlim_cur > 0, "getrlimit STACK rlim_cur is positive"); } /* 2. Re-installing the same limit pair succeeds and round-trips. */ static void setrlimit_roundtrip_scenario(void) { struct rlimit before; struct rlimit after; if (getrlimit(RLIMIT_NOFILE, &before) != 0) { check(0, "getrlimit(RLIMIT_NOFILE) for the round-trip returns 0"); return; } check(setrlimit(RLIMIT_NOFILE, &before) == 0, "setrlimit of identical NOFILE pair returns 0"); check(getrlimit(RLIMIT_NOFILE, &after) == 0, "getrlimit after setrlimit returns 0"); check(after.rlim_cur == before.rlim_cur && after.rlim_max == before.rlim_max, "getrlimit reflects the re-installed NOFILE pair"); } /* 3. The LFS aliases read and write exactly like the base functions. */ static void rlimit64_alias_scenario(void) { struct rlimit a; struct rlimit b; check(getrlimit64(RLIMIT_NOFILE, &a) == 0, "getrlimit64(RLIMIT_NOFILE) returns 0"); check(getrlimit(RLIMIT_NOFILE, &b) == 0, "getrlimit(RLIMIT_NOFILE) returns 0"); check(a.rlim_cur == b.rlim_cur && a.rlim_max == b.rlim_max, "getrlimit64 agrees with getrlimit"); check(setrlimit64(RLIMIT_NOFILE, &a) == 0, "setrlimit64 of identical pair returns 0"); check(getrlimit(RLIMIT_NOFILE, &b) == 0, "getrlimit after setrlimit64 returns 0"); check(b.rlim_cur == a.rlim_cur && b.rlim_max == a.rlim_max, "setrlimit64 write is visible through getrlimit"); } /* 4. getrusage reports sane accounting for the process and the thread. */ static void getrusage_scenario(void) { struct rusage ru; check(getrusage(RUSAGE_SELF, &ru) == 0, "getrusage(RUSAGE_SELF) returns 0"); check(ru.ru_utime.tv_sec >= 0, "getrusage ru_utime.tv_sec is non-negative"); check(ru.ru_utime.tv_usec >= 0 && ru.ru_utime.tv_usec < 1000000, "getrusage ru_utime.tv_usec is in [0, 1e6)"); check(ru.ru_maxrss >= 0, "getrusage ru_maxrss is non-negative"); check(getrusage(RUSAGE_THREAD, &ru) == 0, "getrusage(RUSAGE_THREAD) returns 0"); check(ru.ru_utime.tv_sec >= 0, "getrusage THREAD ru_utime.tv_sec is non-negative"); } /* 5. getpriority/setpriority round-trip on the current process. */ static void priority_roundtrip_scenario(void) { int p; p = getpriority(PRIO_PROCESS, 0); check(p >= PRIO_MIN && p <= PRIO_MAX, "getpriority(PRIO_PROCESS, 0) is in [PRIO_MIN, PRIO_MAX]"); check(setpriority(PRIO_PROCESS, 0, p) == 0, "setpriority to the current value returns 0"); check(getpriority(PRIO_PROCESS, 0) == p, "getpriority round-trips through setpriority"); } /* 6. nice(0) reports the current nice value without changing it. */ static void nice_scenario(void) { int cur; int n; cur = getpriority(PRIO_PROCESS, 0); check(cur >= PRIO_MIN && cur <= PRIO_MAX, "nice baseline is in [PRIO_MIN, PRIO_MAX]"); n = nice(0); check(n == cur, "nice(0) returns the current nice value"); check(getpriority(PRIO_PROCESS, 0) == cur, "nice(0) left the nice value unchanged"); } /* 7. Constant sanity. */ static void constant_scenario(void) { check(RLIM_INFINITY == ~0UL, "RLIM_INFINITY is ~0UL"); check(RLIMIT_NLIMITS == 16, "RLIMIT_NLIMITS is 16"); check(PRIO_MIN == -20 && PRIO_MAX == 19, "PRIO_MIN..PRIO_MAX is -20..19"); check(RUSAGE_SELF == 0 && RUSAGE_CHILDREN == -1 && RUSAGE_THREAD == 1, "RUSAGE_SELF/CHILDREN/THREAD are 0/-1/1"); } /* The failure shapes; return values only, never errno. Exit via a raw * SYS_exit_group: the library's errno writes on these paths corrupt glibc's * private dtv slot at %fs:0+8, so host cleanup must never run (house * pattern, tests/test_systime.c). */ static void failure_scenarios(void) { struct rlimit rl; struct rusage ru; rl.rlim_cur = 0; rl.rlim_max = 0; check(setrlimit(9999, &rl) == -1, "-f setrlimit(9999) returns -1"); check(getrlimit(9999, &rl) == -1, "-f getrlimit(9999) returns -1"); check(getrusage(999, &ru) == -1, "-f getrusage(999) returns -1"); check(getpriority(999, 0) == -1, "-f getpriority(999, 0) returns -1"); check(setpriority(999, 0, 0) == -1, "-f setpriority(999, 0, 0) returns -1"); if (failures == 0) { say(1, "all resource failure scenarios passed\n"); } __syscall1(SYS_exit_group, failures == 0 ? 0 : 1); /* not reached */ } int main(int argc, char **argv) { if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'f' && argv[1][2] == '\0') { failure_scenarios(); } rlimit_read_scenario(); setrlimit_roundtrip_scenario(); rlimit64_alias_scenario(); getrusage_scenario(); priority_roundtrip_scenario(); nice_scenario(); constant_scenario(); if (failures == 0) { say(1, "all resource tests passed\n"); } else { say(1, "FAILURES\n"); } return failures == 0 ? 0 : 1; } #else /* !VLIBC_LEVEL_GE(2) */ /* * Level 1: every symbol is gated at level 2, so there is * nothing to run. Keep the TU compilable at any configured profile. */ int main(void) { say(1, "SKIP: is level 2, not available here\n"); return 0; } #endif /* VLIBC_LEVEL_GE(2) */