/* * 632_w6l_manyflags — w6l -L/-l arrays must be sized by argc, not a * fixed 64-slot cap (drain F-C). The 65th -L used to write past the * allocation: `libdirs[nlibdirs] = ...` with no bound check, corrupting * the heap and dropping the flag. * * Behavioural discriminator: make a -L past slot 64 LOAD-BEARING. We * build libfoo.a in a real directory and reach it ONLY via the Nth -L, * preceded by N-1 junk dirs. w6l errors `cannot find -lfoo` unless it * can locate the archive, so a successful link (exit 0 + ET_EXEC) proves * the Nth -L was honoured. N is a table {1, 64, 65, 100, 128}; pre-fix, * N=65 (the slot exactly one past the 64-element array) reliably fails * to round-trip and the link errors — verified by rebuilding the pre-fix * w6l_ww. Both stages are driven for parity. */ #include #include #include #include #include #include #include #include "wwtestpkg.h" static const char * absbin(void) { const char *b = getenv("BIN"); if (!b) b = "out/bin"; if (b[0] == '/') return b; static char buf[2048]; char cwd[1024]; if (getcwd(cwd, sizeof cwd) == NULL) return NULL; snprintf(buf, sizeof buf, "%s/%s", cwd, b); return buf; } static int runwait(const char *cmd) { int rc = system(cmd); if (rc == -1) return -1; if (WIFEXITED(rc)) return WEXITSTATUS(rc); return 1; } /* link `obj` with linker `lnk`, resolving -lfoo through `libdir` placed * as the Nth (last) -L after nflags-1 junk dirs. Returns the linker's * exit code; on success also fails (-1) if the output isn't an ET_EXEC * ELF — proving the archive was actually located and linked. */ static int linkwith(const char *lnk, const char *obj, const char *libdir, int nflags) { char exe[128]; snprintf(exe, sizeof exe, "%s/wwfc_x", libdir); char junk[8192]; size_t off = 0; for (int i = 1; i < nflags; i++) off += snprintf(junk + off, sizeof junk - off, " -L/nonexist/wwfc_d%d", i); char cmd[16384]; snprintf(cmd, sizeof cmd, "%s -o %s %s%s -L%s -lfoo 2>/dev/null", lnk, exe, obj, junk, libdir); int rc = runwait(cmd); if (rc != 0) goto cleanup; FILE *f = fopen(exe, "rb"); if (!f) { rc = -1; goto cleanup; } unsigned char hdr[20]; int ok = fread(hdr, 1, sizeof hdr, f) == sizeof hdr; fclose(f); if (!ok || memcmp(hdr, "\x7f""ELF", 4) != 0) { rc = -1; goto cleanup; } unsigned short etype = (unsigned short)hdr[16] | ((unsigned short)hdr[17] << 8); if (etype != 2) rc = -1; /* ET_EXEC */ cleanup: if (unlink(exe) != 0 && errno != ENOENT) { perror(exe); if (rc == 0) rc = -1; } return rc; } int main(void) { const char *bin = absbin(); if (!bin) return 1; char src[128], asmf[128], obj[128]; char fsrc[128], fasm[128], fobj[128]; char libdir[64], lib[128], cmd[1024]; int pid = getpid(); snprintf(libdir, sizeof libdir, "/tmp/wwfc_%d_lib", pid); if (mkdir(libdir, 0755) != 0) { perror(libdir); return 1; } int result = 1; snprintf(src, sizeof src, "%s/m.ww", libdir); snprintf(asmf, sizeof asmf, "%s/m.s", libdir); snprintf(obj, sizeof obj, "%s/m.o", libdir); snprintf(fsrc, sizeof fsrc, "%s/f.ww", libdir); snprintf(fasm, sizeof fasm, "%s/f.s", libdir); snprintf(fobj, sizeof fobj, "%s/f.o", libdir); snprintf(lib, sizeof lib, "%s/libfoo.a", libdir); /* a standalone main.o (no undefs) + an unrelated archived foo.o. */ FILE *f = fopen(src, "wb"); if (!f) { perror(src); goto cleanup; } wwtest_fputs("fn main() i32 = { return 42; };", f); if (fclose(f) != 0) { perror(src); goto cleanup; } f = fopen(fsrc, "wb"); if (!f) { perror(fsrc); goto cleanup; } wwtest_fputs("export fn foo() i32 = { return 7; };", f); if (fclose(f) != 0) { perror(fsrc); goto cleanup; } snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s", bin, asmf, src); if (runwait(cmd) != 0) { fprintf(stderr, "w6c main failed\n"); goto cleanup; } snprintf(cmd, sizeof cmd, "%s/w6a -o %s %s", bin, obj, asmf); if (runwait(cmd) != 0) { fprintf(stderr, "w6a main failed\n"); goto cleanup; } snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s", bin, fasm, fsrc); if (runwait(cmd) != 0) { fprintf(stderr, "w6c foo failed\n"); goto cleanup; } snprintf(cmd, sizeof cmd, "%s/w6a -o %s %s", bin, fobj, fasm); if (runwait(cmd) != 0) { fprintf(stderr, "w6a foo failed\n"); goto cleanup; } snprintf(cmd, sizeof cmd, "ar rcs %s %s", lib, fobj); if (runwait(cmd) != 0) { fprintf(stderr, "ar failed\n"); goto cleanup; } int counts[] = { 1, 64, 65, 100, 128 }; int fail = 0; for (int i = 0; i < (int)(sizeof counts / sizeof counts[0]); i++) { int n = counts[i]; char wlnk[2048], clnk[2048]; snprintf(wlnk, sizeof wlnk, "%s/w6l_ww", bin); snprintf(clnk, sizeof clnk, "%s/w6l", bin); int wrc = linkwith(wlnk, obj, libdir, n); int crc = linkwith(clnk, obj, libdir, n); if (wrc != 0) { fprintf(stderr, "FAIL: w6l_ww nflags=%d link rc=%d " "(65th-style -L not honoured → heap overflow)\n", n, wrc); fail++; } if (crc != 0) { fprintf(stderr, "FAIL: w6l (cstage) nflags=%d link rc=%d\n", n, crc); fail++; } } if (fail) { fprintf(stderr, "w6l_manyflags: %d failure(s)\n", fail); goto cleanup; } result = 0; cleanup: { int cleanfail = 0; if (unlink(src) != 0 && errno != ENOENT) { perror(src); cleanfail = 1; } if (unlink(asmf) != 0 && errno != ENOENT) { perror(asmf); cleanfail = 1; } if (unlink(obj) != 0 && errno != ENOENT) { perror(obj); cleanfail = 1; } if (unlink(fsrc) != 0 && errno != ENOENT) { perror(fsrc); cleanfail = 1; } if (unlink(fasm) != 0 && errno != ENOENT) { perror(fasm); cleanfail = 1; } if (unlink(fobj) != 0 && errno != ENOENT) { perror(fobj); cleanfail = 1; } if (unlink(lib) != 0 && errno != ENOENT) { perror(lib); cleanfail = 1; } char exe[128]; snprintf(exe, sizeof exe, "%s/wwfc_x", libdir); if (unlink(exe) != 0 && errno != ENOENT) { perror(exe); cleanfail = 1; } if (rmdir(libdir) != 0) { perror(libdir); cleanfail = 1; } if (cleanfail && result == 0) result = 1; } if (result != 0) return result; printf("w6l_manyflags: -L past slot 64 honoured by both stages " "(1/64/65/100/128 flags)\n"); return result; }