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
next prev 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).