From: Martijn Dekker Subject: Re: FD state after close(2) error? To: Theo de Raadt Cc: Tech-OpenBSD Date: Fri, 7 Aug 2026 19:09:52 +0100 Op 07-08-2026 om 16:39 schreef Theo de Raadt: > Martijn Dekker wrote: > >> Is there any scenario where a file descriptor passed to close(2) >> remains, or might remain, open if EINTR or EIO occurs? The manual page >> does not document this. > > It does document it, in the first sentence: > > DESCRIPTION > The close() call deletes a descriptor d from the per-process object > reference table. > > Nothing later on disagrees with this sentence. That's ambiguous at best. The ERRORS section details the conditions under which close(2) fails, but it does not define what it means to fail. A reasonable default interpretation would be that the call failed to do its specified job, which begins with deleting the descriptor d from the per-process object reference table. I would suggest adding, to the end of the ERRORS section, this sentence from FreeBSD's close(2) manual page: In case of any error except EBADF, the supplied file descriptor is deallocated and therefore is no longer valid. Given the incredible mess around close(2) breakage on various systems over the last few decades, it would seem prudent to be quite explicit about this. In the meantime, I much appreciate your on-list clarification, which I can use as a reference. > The addition of error return values to close() by POSIX has been > contentious since the start, because it makes this system call operation > non-ACID (even when returning an error, it has performed a step). This > mainly arrived because of a weird behaviour in NFS. But many believe the > specification was a mistake, because no program is prepared or capable of > taking corrective action when these errors occur, because sufficient > information about the issue isn't reported AND because the descriptor > which would be needed for resolution has been deleted (see the first sentence). I agree that avoiding a change of state if an error occurs would make the most sense. Unfortunately, HP-UX is the only extant system that acts like this. OpenBSD, FreeBSD and Linux are at least consistent: it's always closed, full stop. I'm awaiting confirmation from NetBSD that they do the same thing, but at this point I suspect so. (Their close(2) man page is equally unclear on the matter.) AIX and Solaris/illumos have the worst of both worlds: their manual pages say that the state of the file descriptor is indeterminate if close(2) fails. So retrying close(2) is necessary to avoid file descriptor leaks. Good luck safely and effectively closing a file in a multithreaded application. In the meantime, POSIX-1.2024 has added a new complication: if close(2) and the new posix_close(2) are interrupted but return with the file descriptor closed, they're now required to return -1 and set errno to EINPROGRESS. Since the FD is closed just fine and the kernel will take care of the rest, that's not actually a failure, and there's nothing the caller can or should do about it. However, this change is likely to incorrectly trigger failure handling in existing code. https://pubs.opengroup.org/onlinepubs/9799919799/functions/close.html -- || modernish -- harness the shell || https://github.com/modernish/modernish || || KornShell lives! || https://github.com/ksh93/ksh