From: "Theo de Raadt" Subject: Re: FD state after close(2) error? To: Martijn Dekker Cc: Tech-OpenBSD Date: Fri, 07 Aug 2026 13:03:19 -0600 Martijn Dekker wrote: > 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. It is not ambigious. It is accurate text. > The ERRORS section details the conditions > under which close(2) fails, but it does not define what it means to > fail. Let's try your approach by looking at the open(2) manual page. DESCRIPTION The file name specified by path is opened for reading and/or writing as specified by the argument flags and the file descriptor returned to the calling process. The flags argument may indicate the file is to be So shall we interpret that if we pass O_CREAT|O_WRONLY and then an error happens the file *MAY* have been created in the filesystem, but an error is returned? Your opinion on ambiguity here is not correct. > 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. Sure if you read it wrong, you could also interpret that calling close(-7) will deliver you a free pony. The close() system call behaviour has not significantly changed in over 30 years. The text in the manual page is ACCURATE and very likely older than you. > 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. And let me see, to open(2) we should add this text: In case of any error, the requested file is not created. Right? I think that's bullshit. The first sentence was clear. Unfortunately you cannot write code to that spec, because POSIX says: If close() is interrupted by a signal that is to be caught, it shall return -1 with errno set to [EINTR] and the state of fildes is unspecified. If an I/O error occurred while reading from or writing to the file system during close(), it may return -1 with errno set to [EIO]; if this error is returned, the state of fildes is unspecified. I recommend you take up concerns with the POSIX people. > 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. I've never seen any code which is broken by entirely ignoring the return value from close(2), and assuming that it works as originally intended. The base of userland code does commonly check for error, and it should not be checking. > In the meantime, I much appreciate your on-list clarification, which I > can use as a reference. You should not use my comments. You should refer to code. > > 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.) Why are you asking people? You should be reading the pretty simple kernel code, and if you look back over decades of repository, it will be fine. > 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. They say that POSIX says it is indeterminate, but why have you not checked the actual Solaris code to verify? Meaning, the Solaris manual page is telling you there may be systems (perhaps not AIX and Solaris/illumos) where something weird could happen, and it is following POSIX guidance to tell people that they may need to be wary if writing portable code. THIS is the problem: https://pubs.opengroup.org/onlinepubs/009604299/functions/close.html I think there are situations where operating system manual pages for functions should warn that there are dangerous divergences on other systems, but there are also other cases where they should not say a word about it because it doesn't matter. We do not want people submitting hundreds of pointless diffs to add return value checks for all the close() functions in our tree (with no suitable resolution when that happens), and the best way to do that is to not admit that there are potentially broken systems. Those broken operating systems will hopefully expire, and we can ignore that useless lawyer speak in POSIX. > 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. POSIX has been turning into a joke for a while The rule of thumb was that they can only document existing behaviour. That is not existing behaviour. > However, this change is > likely to incorrectly trigger failure handling in existing code. > https://pubs.opengroup.org/onlinepubs/9799919799/functions/close.html PoC||GTFO And now I've hit my limit on interest in this area.