[RFC PATCH 1/1] fs: don't return EINTR from close()
From: Mikko Rantalainen
Date: Sun Sep 13 2026 - 15:45:27 EST
close() removes the file descriptor from the descriptor table before
calling filp_flush(). Consequently, once file_close_fd() succeeds,
the descriptor is closed regardless of the result returned by
filp_flush() and the same fd number may immediately be reused.
Without this patch an interruptible ->flush() can nevertheless cause
close() to return EINTR, either directly or after an internal -ERESTART*
error is translated to EINTR.
This is particularly problematic for close(). EINTR conventionally
indicates an interrupted operation that may need to be retried, but
retrying close() is unsafe: another thread may already have reused the
descriptor number, causing the retry to close an unrelated file.
There is also no recovery operation the caller can perform through the
original descriptor, since it has already been removed from the
descriptor table.
Treat interruption after descriptor removal as successful close instead.
Continue to report other errors from ->flush(), such as delayed I/O
errors.
This also makes this case compatible with POSIX.1-2024. POSIX permits
an interrupted close() that has closed the descriptor to return success,
whereas if close() reports EINTR, POSIX requires the descriptor to
remain open.
musl and Android bionic already normalize EINTR from Linux close() to
success in userspace, providing substantial deployed precedent for this
behavior.
Signed-off-by: Mikko Rantalainen <mikko.rantalainen@xxxxxxxx>
---
fs/open.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/fs/open.c b/fs/open.c
index 408925d7bd0b..81a43b6c5b6b 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -1513,12 +1513,17 @@ SYSCALL_DEFINE1(close, unsigned int, fd)
if (likely(retval == 0))
return 0;
- /* can't restart close syscall because file table entry was cleared */
- if (retval == -ERESTARTSYS ||
+ /*
+ * The file descriptor has already been closed, so an interrupted
+ * close cannot be restarted safely. Do not report EINTR after the
+ * descriptor has been detached.
+ */
+ if (retval == -EINTR ||
+ retval == -ERESTARTSYS ||
retval == -ERESTARTNOINTR ||
retval == -ERESTARTNOHAND ||
retval == -ERESTART_RESTARTBLOCK)
- retval = -EINTR;
+ retval = 0;
return retval;
}
--
2.43.0