All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Michael Kerrisk (man-pages)" <mtk.manpages-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
To: Rich Felker <dalias-8zAoT0mYgF4@public.gmane.org>
Cc: mtk.manpages-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org,
	"linux-man-u79uwXL29TY76Z2rM5mHXA@public.gmane.org"
	<linux-man-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>
Subject: Re: Very bad advice in man 2 dup2
Date: Sun, 29 Jun 2014 09:46:29 +0200	[thread overview]
Message-ID: <53AFC455.8080707@gmail.com> (raw)
In-Reply-To: <20140629030554.GW179-C3MtFaGISjmo6RMmaWD+6Sb1p8zYI1N1@public.gmane.org>

On 06/29/2014 05:05 AM, Rich Felker wrote:
> On Thu, Jun 12, 2014 at 08:53:38PM +0200, Michael Kerrisk (man-pages) wrote:
>> Another ping....
> 
> Sorry for not getting back to you on this!
>
>>>>> Here is a proposed alternate text that was recommended to me by a user
>>>>> on Stack Overflow:
>>>>
>>>> It'd be great to add URLs when citing such discussions... With some
>>>> effort, I found
>>>> http://stackoverflow.com/questions/23440216/race-condition-when-using-dup
>>>>
>>>>>     A careful programmer will first dup() the target descriptor, then
>>>>>     use dup2()/dup3() to replace the target descriptor atomically, and
>>>>>     finally close the initially duplicated target descriptor. This
>>>>>     replaces the target descriptor atomically, but also retains a
>>>>>     duplicate for closing so that close-time errors may be checked
>>>>>     for. (In Linux, close() should only be called once, as the
>>>>>     referred to descriptor is always closed, even in case of
>>>>>     errno==EINTR.)
>>>>>
>>>>> I'm not sure this is the best wording, since it suggests doing a lot
>>>>> of work that's likely overkill (and useless in the case where the
>>>>> target descriptor was read-only, for instance). I might balance such
>>>>> text with a warning that it's an error to use dup2 or dup3 when the
>>>>> target descriptor is not known to be open unless you know the code
>>>>> will only be used in single-threaded programs. And I'm a little bit
>>>>> hesitant on the parenthetical text about close() since the behavior
>>>>> it's documenting is contrary to the requirements of the upcoming issue
>>>>> 8 of POSIX,
>>>>
>>>> Again citing the Issue 8 discussion would be helpful. Could
>>>> you tell me where it is? (It could be useful for the change log.)
> 
> See the issue that added all the new atomic close-on-exec operations:
> http://austingroupbugs.net/view.php?id=411
> 
>     The resolution of 0000149 documented that using close( ) on
>     arbitrary file descriptors is unsafe, and that applications should
>     instead atomically create file descriptors with FD_CLOEXEC already
>     set. [...]
> 
> And the issue referenced there:
> http://austingroupbugs.net/view.php?id=149
> 
>>>>> and rather irrelevant since EINTR cannot happen in Linux's
>>>>> close() except with custom device drivers anyway.
>>>>
>>>> So, how about something like the following (code not
>>>> compile-tested...):
>>>>
>>>>        If newfd was open, any errors that would have been reported  at
>>>>        close(2) time are lost.  If this is of concern, then—unless the
>>>>        program is single-threaded and does not allocate file  descrip‐
>>>>        tors  in  signal  handlers—the correct approach is not to close
>>>>        newfd before calling dup2(),  because  of  the  race  condition
>>>>        described  above.   Instead,  code something like the following
>>>>        could be used:
>>>>
>>>>            /* Obtain a duplicate of 'newfd' that can subsequently
>>>>               be used to check for close() errors; an EBADF error
>>>>               means that 'newfd' was not open. */
>>>>
>>>>            tmpfd = dup(newfd);
>>>>            if (tmpfd == -1 && errno != EBADF) {
>>>>                /* Handle unexpected dup() error */
>>>>            }
>>>>
>>>>            /* Atomically duplicate 'oldfd' on 'newfd' */
>>>>
>>>>            if (dup2(oldfd, newfd) == -1) {
>>>>                /* Handle dup2() error */
>>>>            }
>>>>
>>>>            /* Now check for close() errors on the file originally
>>>>               referred to by 'newfd' */
>>>>
>>>>            if (tmpfd != -1) {
>>>>                if (close(tmpfd) == -1) {
>>>>                    /* Handle errors from close */
>>>>                }
>>>>            }
> 
> This code looks like it works, but it's a lot to be recommending
> without a really good reason to do so. 

Well, I think the para that precedes the code goes some way toward 
explaining why you might want to do this.


> I seem to remember someone
> claiming that the whole "need" to check for error on close is outdated
> and not applicable to modern Linux (even with NFS?) but I can't

All the world is not Linux. And in the future, even Linux
behavior might change. And I do not recall the details,
but as far as I know some NFS errors can be delivered on 
close(). So, it seems to me that robust applications should 
check the status from close.

> remember where. Anyway I think such code should be accompanied by a
> discussion of why one might care about checking for close errors so
> programmers can make an informed decision about whether it's worth the
> trouble rather than cargo-culting it.

There is some text on this in the close(2) page.

So, for the moment, the text as I gave it, is in. It's certainly
an improvement over what was there before. If you feel strongly that 
further a change is needed, please send me a patch (or carefully
worded free text that I can drop into the page).

Cheers,

Michael


-- 
Michael Kerrisk
Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/
Linux/UNIX System Programming Training: http://man7.org/training/
--
To unsubscribe from this list: send the line "unsubscribe linux-man" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

  parent reply	other threads:[~2014-06-29  7:46 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <20140529210242.GP507@brightrain.aerifal.cx>
     [not found] ` <20140529210242.GP507-C3MtFaGISjmo6RMmaWD+6Sb1p8zYI1N1@public.gmane.org>
2014-05-30  9:15   ` Very bad advice in man 2 dup2 Michael Kerrisk (man-pages)
     [not found]     ` <53884C27.7020207-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2014-06-05 12:54       ` Michael Kerrisk (man-pages)
     [not found]         ` <CAKgNAkhFJ6-_RKfTFXYxAAU3jw-ij9ojgeO9G=V9L-WgKZQ62w-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2014-06-12 18:53           ` Michael Kerrisk (man-pages)
     [not found]             ` <CAKgNAkjdM2ZLY6B4==9Pz1kz4X48NTiq7rnSttbALwJV8ru79g-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2014-06-29  3:05               ` Rich Felker
     [not found]                 ` <20140629030554.GW179-C3MtFaGISjmo6RMmaWD+6Sb1p8zYI1N1@public.gmane.org>
2014-06-29  7:46                   ` Michael Kerrisk (man-pages) [this message]
     [not found]                     ` <53AFC455.8080707-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2014-06-29 15:07                       ` Rich Felker

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=53AFC455.8080707@gmail.com \
    --to=mtk.manpages-re5jqeeqqe8avxtiumwx3w@public.gmane.org \
    --cc=dalias-8zAoT0mYgF4@public.gmane.org \
    --cc=linux-man-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.