/* * vlibc — strerror/strerror_r test (todo 2). * * Exercises the public end to end: * * 1. strerror(0) is "Success"; known values return the exact message and * are non-NULL and distinct; aliases (EWOULDBLOCK/EDEADLOCK) resolve to * their canonical entries; the last table value (EHWPOISON) is covered. * 2. Unknown errno values never return NULL and never crash: 9999 and the * ABI-gap value 41 format as "Unknown error ", negatives too. * 3. strerror_r (XSI): returns 0 and fills buf with the right text; an * unknown value still returns 0 with "Unknown error "; truncation * into a tiny buf stays NUL-terminated; buflen 0 writes nothing; a NULL * buf with buflen > 0 returns EINVAL. * * The test deliberately never touches errno itself: under the host libc the * TCB slot our errno macro addresses is glibc's private TLS state, and * writing it would corrupt the host (see tests/syscall_test.c). All coverage * here is through the errnum parameters. * * All diagnostics go through raw SYS_write (no stdio): under -Iinclude the * vlibc public headers shadow GCC's internal ones, so mixing in a host * breaks compilation (glibc's stdio.h pulls in our minimal * , which lacks __gnuc_va_list). below is vlibc's own * header, which is safe. * * Not part of the library proper; compiled manually for this todo (the * tests/ + make check wiring is owned by a later todo). */ #include #include #include "../src/internal/syscall.h" static int failures; /* Write a NUL-terminated string to fd via the raw syscall layer. */ static void say(int fd, const char *s) { long n = 0; while (s[n] != '\0') { n++; } __syscall3(SYS_write, fd, (long)s, n); } static void check(int cond, const char *what) { if (!cond) { say(2, "FAIL: "); say(2, what); say(2, "\n"); failures++; } } /* * Failure scenario (-f): an unknown errno must yield "Unknown error 9999", * non-NULL, without crashing. Prints the observed string; exits 0 only when * the failure behaved exactly as specified. */ static int failure_scenario(void) { const char *s = strerror(9999); if (s == NULL) { say(1, "strerror(9999)=NULL\n"); return 1; } say(1, "strerror(9999)="); say(1, s); say(1, "\n"); return strcmp(s, "Unknown error 9999") == 0 ? 0 : 1; } int main(int argc, char **argv) { const char *a; const char *b; char buf[64]; char tiny[8]; char untouched[4]; int r; if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'f') { return failure_scenario(); } /* 1. Known values: exact text, non-NULL, distinct. */ check(strcmp(strerror(0), "Success") == 0, "strerror(0) == \"Success\""); check(strcmp(strerror(2), "No such file or directory") == 0, "strerror(2) == \"No such file or directory\""); check(strcmp(strerror(EINVAL), "Invalid argument") == 0, "strerror(EINVAL) == \"Invalid argument\""); check(strcmp(strerror(EPERM), "Operation not permitted") == 0, "strerror(EPERM) == \"Operation not permitted\""); a = strerror(EINVAL); b = strerror(EPERM); check(a != NULL && b != NULL, "known values non-NULL"); check(a != b, "known values distinct"); /* Aliases resolve to their canonical entries. */ check(strerror(EWOULDBLOCK) == strerror(EAGAIN), "EWOULDBLOCK == EAGAIN"); check(strerror(EDEADLOCK) == strerror(EDEADLK), "EDEADLOCK == EDEADLK"); /* Table ends: last defined value and ABI gap values. */ check(strerror(EHWPOISON) != NULL, "EHWPOISON (last value) non-NULL"); check(strcmp(strerror(41), "Unknown error 41") == 0, "ABI gap 41 unknown"); check(strcmp(strerror(58), "Unknown error 58") == 0, "ABI gap 58 unknown"); /* 2. Unknown values: never NULL, never crash, formatted. */ a = strerror(9999); check(a != NULL, "strerror(9999) non-NULL"); check(strcmp(a, "Unknown error 9999") == 0, "strerror(9999) text"); a = strerror(-7); check(a != NULL, "strerror(-7) non-NULL"); check(strcmp(a, "Unknown error -7") == 0, "strerror(-7) text"); /* 3. strerror_r: XSI int return, exact fill. */ r = strerror_r(EDOM, buf, sizeof buf); check(r == 0, "strerror_r(EDOM) returns 0"); check(strcmp(buf, "Numerical argument out of domain") == 0, "strerror_r(EDOM) fills buf"); /* Unknown errno still succeeds and formats into buf (XSI). */ r = strerror_r(9999, buf, sizeof buf); check(r == 0, "strerror_r(9999) returns 0 (XSI)"); check(strcmp(buf, "Unknown error 9999") == 0, "strerror_r(9999) text"); /* Truncation into a tiny buf stays NUL-terminated. */ r = strerror_r(ENAMETOOLONG, tiny, sizeof tiny); check(r == 0, "strerror_r(ENAMETOOLONG, tiny) returns 0"); check(tiny[sizeof tiny - 1] == '\0', "tiny buf NUL-terminated"); check(strcmp(tiny, "File na") == 0, "tiny buf truncated prefix"); /* buflen 0: nothing written, still success. */ untouched[0] = 'X'; untouched[1] = 'X'; untouched[2] = 'X'; untouched[3] = '\0'; r = strerror_r(EDOM, untouched, 0); check(r == 0, "strerror_r(EDOM, buf, 0) returns 0"); check(untouched[0] == 'X', "buflen 0 writes nothing"); /* NULL buf with buflen > 0: EINVAL. */ r = strerror_r(EDOM, NULL, 16); check(r == EINVAL, "strerror_r(NULL buf) returns EINVAL"); if (failures > 0) { say(2, "FAILED\n"); } else { say(1, "strerror ok\n"); } return failures == 0 ? 0 : 1; }