← board

crtl syscall veneers returned the raw negative errno

Symptom

errno = 0;
int fd = open("/tmp/no_such_dir/x", O_RDONLY);
printf("%d %d\n", fd, errno);
fd errno
gcc -1 2 (ENOENT)
pxx -2 0

open("/", O_WRONLY) returned -21 (EISDIR) the same way.

Eleven functions across four files did this: open, openat, creat, open64, fcntl, fcntl64 (fcntl.c), fsync, dup, dup2, chdir, symlink, link, pipe (unistd.c), mkdir, fchmod (sys/stat.c), utimes (sys/time.c).

Why it survived

The PAL deliberately returns the raw kernel convention — 0/positive, or a negative errno — and most of unistd.c already translates it inline (if (rc < 0) { errno = -rc; return -1; } appears a dozen times). The functions added later were written as one-line forwards and simply skipped the step.

It hid because if (rc < 0) catches both conventions. That is how almost all C code tests a syscall, so almost all C code kept working, and the failure only shows in the two places that read the detail:

So the program takes the error branch correctly and then reports the wrong reason, which is the plausible-wrong-value shape this codebase treats as worse than a crash.

isatty was the sharp one: it must answer 0 or 1, and a negative leaking out makes every if (isatty(fd)) interactivity check — colour, progress bars, prompting — read TRUE. It happens to return 0 for a regular file today, but nothing was stopping the EBADF path; it is now clamped.

Fix

A sysret() helper per file (matching the inline pattern already there), plus isatty clamped to 0/1 and openat's unsupported-dirfd path setting EBADF instead of returning a bare -1.

Regression cover

tools/gcc_diff_probe.sh case syscall-errno-convention checks return AND errno for ten failing syscalls against gcc, and isatty-not-negative checks the 0/1 contract. errno is read on its own statement in both — reading it in the argument list of the call that sets it is unspecified order and has burned this repo twice.

Gate

gcc_diff_probe clean on native/i386/arm32/aarch64, gate.sh lib green, c-testsuite 219/0/1.