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 --- 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