[PATCH] Manual should discourage mixing TEMP_FAILURE_RETRY and close

Michael Kerrisk mtk.manpages@gmail.com
Mon Dec 5 14:49:00 GMT 2016


For what it's worth, well after the fact...

On Wed, Dec 4, 2013 at 1:08 AM, Ondřej Bílka <neleai@seznam.cz> wrote:
> On Tue, Dec 03, 2013 at 01:05:21PM -0500, Mark Mentovai wrote:
>> When the close system call is interrupted by a signal, the state of its
>> file descriptor argument is not specified[1]. On Linux, a close that
>> fails with EINTR must not be restarted because the file descriptor is
>> guaranteed to have been closed at that point[2]. Note that the kernel
>> itself never restarts a close when SA_RESTART is set or when no
>> user-space signal handler is present[3].
>>
> This is linux problem, POSIX now says
>
> http://austingroupbugs.net/view.php?id=529
>
> If close( ) is interrupted by a signal that is to be caught, then it
> is unspecified whether it returns -1 with errno set to [EINTR] with
> fildes remaining open, or returns -1 with errno set to [EINPROGRESS]
> with fildes being closed, or returns 0 to indicate successful
> completion; except that if POSIX_CLOSE_RESTART is defined as 0, then
> the option of returning -1 with errno set to [EINTR] and fildes
> remaining open shall not occur. If close() returns -1 with errno set
> to [EINTR], it is unspecified whether fildes can subsequently be
> passed to any function except close( ) or posix_close( ) without error.
> For all other error situations (except for [EBADF] where fildes was
> invalid), fildes shall be closed. If fildes was closed even though
> the close operation is incomplete, the close operation shall continue
> asynchronously and the process shall have no further ability to track
> the completion or final status of the close operation.

Yes, but POSIX never said that. That was a proposal that was
discussed, that subsequently got shot down... Older POSIX.1 was wrong.
It's worth reading that entire Austin bug for the subsequent
discussion.

POSIX.1-2016 says under close()

       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.

(The only three errors mandated in POSIX.1-2016 for close() are EINTR,
EIO, and EBADF.)

My takeaway from the above POSIX text is that it's perfectly
legitimate for an implementation to say that  retrying close() after
*any* error is the wrong thing to do. Linux does this. So does
FreeBSD, where the man page says:

     In case of any error except EBADF, the supplied file descriptor is
     deallocated and therefore is no longer valid.

And according to a claim in the thread here:
https://news.ycombinator.com/item?id=3363884 , ancient AIX also
explicitly documented that the FD was closed in an EINTR error was
returned. This looks to be confirmed in
http://publib16.boulder.ibm.com/doc_link/en_US/a_doc_lib/libs/basetrf1/close.htm

And from the look of http://austingroupbugs.net/view.php?id=529#c1200
, the plan in the future is that POSIX will allow even more
flexibility to an implementation in the next major release (who knows
when).

>> Because glibc is widely used on Linux, its documentation should state
>> affirmatively that the TEMP_FAILURE_RETRY, which restarts interrupted
>> system calls that fail with EINTR, is not to be used with close on
>> Linux. Examples in glibc documentation should avoid recommending
>> wrapping close with TEMP_FAILURE_RETRY.
>>
>> [1] http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html
>> [2] http://lkml.org/lkml/2005/9/10/129
>> [3] http://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/commit/?id=ee731f4f7880b09ca147008ab46ad4e5f72cb8b
>>
>> 2013-12-03  Mark Mentovai  <mark@chromium.org>
>>
>>       * manual/llio.texi (Opening and Closing Files): Document that
>>       TEMP_FAILURE_RETRY should not be used with close on Linux.
>>       (Duplicating Descriptors): Don't wrap close in TEMP_FAILURE_RETRY
>>       in example code.

This patch of Mark's really should be applied in some form, I think.
The glibc manual is currently advising users to do something actively
dangerous on at least two popular systems, and it has no justification
in current POSIX.

I'm just in the process of beefing up the Linux close man page on this
subject to say:

       Not checking the return value of close() is a common but neverthe‐
       less  serious programming error.  It is quite possible that errors
       on a previous write(2) operation are first reported at  the  final
       close().   Not checking the return value when closing the file may
       lead to silent loss of data.  This can especially be observed with
       NFS and with disk quota.

       Note, however, that the return value should be used only for diag‐
       nostics.  Retrying the close() after an error is the  wrong  thing
       to  do, since this may cause a reused file descriptor from another
       thread to be closed.  (The kernel  releases  the  file  descriptor
       early in the close operation, freeing it for reuse; the steps that
       may return an error, such as flushing data  to  disk,  occur  only
       later  in the close operation.)  In particular, close() should not
       be retried after an EINTR error.  (POSIX.1 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.")

       Careful users who want  to  know  about  I/O  errors  may  precede
       close() with a call to fsync(2).

Cheers,

Michael

>>
>> ---
>>  manual/llio.texi | 10 ++++------
>>  1 file changed, 4 insertions(+), 6 deletions(-)
>>
>> diff --git a/manual/llio.texi b/manual/llio.texi
>> index b6c9260..f04a2cf 100644
>> --- a/manual/llio.texi
>> +++ b/manual/llio.texi
>> @@ -253,11 +253,9 @@ The @var{filedes} argument is not a valid file descriptor.
>>  @item EINTR
>>  The @code{close} call was interrupted by a signal.
>>  @xref{Interrupted Primitives}.
>> -Here is an example of how to handle @code{EINTR} properly:
>> -
>> -@smallexample
>> -TEMP_FAILURE_RETRY (close (desc));
>> -@end smallexample
>> +On Linux, @code{close} will have closed its file descriptor argument even when
>> +reporting @code{EINTR}.  It is not safe to retry calling @code{close} in this
>> +case, as would be done when wrapped in the @code{TEMP_FAILURE_RETRY} macro.
>>
>>  @item ENOSPC
>>  @itemx EIO
>> @@ -2765,7 +2763,7 @@ if (pid == 0)
>>      @dots{}
>>      file = TEMP_FAILURE_RETRY (open (filename, O_RDONLY));
>>      dup2 (file, STDIN_FILENO);
>> -    TEMP_FAILURE_RETRY (close (file));
>> +    close (file);
>>      execv (program, NULL);
>>    @}
>>  @end smallexample
>> --
>> 1.8.3.4
>



More information about the Libc-alpha mailing list