All of lore.kernel.org
 help / color / mirror / Atom feed
* Re: Very bad advice in man 2 dup2
       [not found] ` <20140529210242.GP507-C3MtFaGISjmo6RMmaWD+6Sb1p8zYI1N1@public.gmane.org>
@ 2014-05-30  9:15   ` Michael Kerrisk (man-pages)
       [not found]     ` <53884C27.7020207-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
  0 siblings, 1 reply; 6+ messages in thread
From: Michael Kerrisk (man-pages) @ 2014-05-30  9:15 UTC (permalink / raw)
  To: Rich Felker
  Cc: mtk.manpages-Re5JQEeQqe8AvxtiuMwx3w, linux-man-u79uwXL29TY76Z2rM5mHXA

Hi Rich,

For discussions like this, it really is very important to CC the list,
so that there's an archived record of the reasons for the change.
I've concatenated your two mails below.

I agree that the page needs to be fixed; thanks for the report!
I am mulling over what the best fix is. One proposal below.

On 05/29/2014 11:02 PM, Rich Felker wrote:
> Hi,
> 
> The following text appears in the man page for dup2 and dup3:
> 
>     If newfd was open, any errors that would have been reported at
>     close(2) time are lost. A careful programmer will not use dup2()
>     or dup3() without closing newfd first.
> 
> Such use of close is very bad advice, as it introduces a race
> condition during which the file descriptor could be re-assigned and
> subsequently clobbered by dup2/dup3. 

Agreed. A more fundamental problem in the man page is that
it does not mention the atomicity of dup2() (and dup3()),
and why that is needed to avoid the race condition.
I'll add some words on that.

> This type of bug can lead to
> serious data leaks and/or data loss. The whole point of dup2/dup3 is
> to _atomically_ replace a file descriptor.
> 
> For the most part there are no meaningful errors which close can
> return, probably only obscure NFS behavior with bad caching settings
> which are really not handlable by applications anyway, so I feel it
> would be best to just drop this text (or find a way to detect such
> errors without close, perhaps using fsync, and recommend that).

On 05/30/2014 07:23 AM, Rich Felker wrote:
[...]
> 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.)

> 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 */
               }
           }

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

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Very bad advice in man 2 dup2
       [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>
  0 siblings, 1 reply; 6+ messages in thread
From: Michael Kerrisk (man-pages) @ 2014-06-05 12:54 UTC (permalink / raw)
  To: Rich Felker; +Cc: Michael Kerrisk, linux-man-u79uwXL29TY76Z2rM5mHXA

Rich, Ping!

On Fri, May 30, 2014 at 11:15 AM, Michael Kerrisk (man-pages)
<mtk.manpages-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> Hi Rich,
>
> For discussions like this, it really is very important to CC the list,
> so that there's an archived record of the reasons for the change.
> I've concatenated your two mails below.
>
> I agree that the page needs to be fixed; thanks for the report!
> I am mulling over what the best fix is. One proposal below.
>
> On 05/29/2014 11:02 PM, Rich Felker wrote:
>> Hi,
>>
>> The following text appears in the man page for dup2 and dup3:
>>
>>     If newfd was open, any errors that would have been reported at
>>     close(2) time are lost. A careful programmer will not use dup2()
>>     or dup3() without closing newfd first.
>>
>> Such use of close is very bad advice, as it introduces a race
>> condition during which the file descriptor could be re-assigned and
>> subsequently clobbered by dup2/dup3.
>
> Agreed. A more fundamental problem in the man page is that
> it does not mention the atomicity of dup2() (and dup3()),
> and why that is needed to avoid the race condition.
> I'll add some words on that.
>
>> This type of bug can lead to
>> serious data leaks and/or data loss. The whole point of dup2/dup3 is
>> to _atomically_ replace a file descriptor.
>>
>> For the most part there are no meaningful errors which close can
>> return, probably only obscure NFS behavior with bad caching settings
>> which are really not handlable by applications anyway, so I feel it
>> would be best to just drop this text (or find a way to detect such
>> errors without close, perhaps using fsync, and recommend that).
>
> On 05/30/2014 07:23 AM, Rich Felker wrote:
> [...]
>> 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.)
>
>> 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 */
>                }
>            }
>
> Cheers,
>
> Michael
>
>
> --
> Michael Kerrisk
> Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/
> Linux/UNIX System Programming Training: http://man7.org/training/



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

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Very bad advice in man 2 dup2
       [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>
  0 siblings, 1 reply; 6+ messages in thread
From: Michael Kerrisk (man-pages) @ 2014-06-12 18:53 UTC (permalink / raw)
  To: Rich Felker; +Cc: Michael Kerrisk, linux-man-u79uwXL29TY76Z2rM5mHXA

Another ping....


On Thu, Jun 5, 2014 at 2:54 PM, Michael Kerrisk (man-pages)
<mtk.manpages-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> Rich, Ping!
>
> On Fri, May 30, 2014 at 11:15 AM, Michael Kerrisk (man-pages)
> <mtk.manpages-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>> Hi Rich,
>>
>> For discussions like this, it really is very important to CC the list,
>> so that there's an archived record of the reasons for the change.
>> I've concatenated your two mails below.
>>
>> I agree that the page needs to be fixed; thanks for the report!
>> I am mulling over what the best fix is. One proposal below.
>>
>> On 05/29/2014 11:02 PM, Rich Felker wrote:
>>> Hi,
>>>
>>> The following text appears in the man page for dup2 and dup3:
>>>
>>>     If newfd was open, any errors that would have been reported at
>>>     close(2) time are lost. A careful programmer will not use dup2()
>>>     or dup3() without closing newfd first.
>>>
>>> Such use of close is very bad advice, as it introduces a race
>>> condition during which the file descriptor could be re-assigned and
>>> subsequently clobbered by dup2/dup3.
>>
>> Agreed. A more fundamental problem in the man page is that
>> it does not mention the atomicity of dup2() (and dup3()),
>> and why that is needed to avoid the race condition.
>> I'll add some words on that.
>>
>>> This type of bug can lead to
>>> serious data leaks and/or data loss. The whole point of dup2/dup3 is
>>> to _atomically_ replace a file descriptor.
>>>
>>> For the most part there are no meaningful errors which close can
>>> return, probably only obscure NFS behavior with bad caching settings
>>> which are really not handlable by applications anyway, so I feel it
>>> would be best to just drop this text (or find a way to detect such
>>> errors without close, perhaps using fsync, and recommend that).
>>
>> On 05/30/2014 07:23 AM, Rich Felker wrote:
>> [...]
>>> 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.)
>>
>>> 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 */
>>                }
>>            }
>>
>> Cheers,
>>
>> Michael
>>
>>
>> --
>> Michael Kerrisk
>> Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/
>> Linux/UNIX System Programming Training: http://man7.org/training/
>
>
>
> --
> Michael Kerrisk
> Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/
> Linux/UNIX System Programming Training: http://man7.org/training/



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

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Very bad advice in man 2 dup2
       [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>
  0 siblings, 1 reply; 6+ messages in thread
From: Rich Felker @ 2014-06-29  3:05 UTC (permalink / raw)
  To: Michael Kerrisk (man-pages); +Cc: linux-man-u79uwXL29TY76Z2rM5mHXA

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

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

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Very bad advice in man 2 dup2
       [not found]                 ` <20140629030554.GW179-C3MtFaGISjmo6RMmaWD+6Sb1p8zYI1N1@public.gmane.org>
@ 2014-06-29  7:46                   ` Michael Kerrisk (man-pages)
       [not found]                     ` <53AFC455.8080707-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
  0 siblings, 1 reply; 6+ messages in thread
From: Michael Kerrisk (man-pages) @ 2014-06-29  7:46 UTC (permalink / raw)
  To: Rich Felker
  Cc: mtk.manpages-Re5JQEeQqe8AvxtiuMwx3w, linux-man-u79uwXL29TY76Z2rM5mHXA

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

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Very bad advice in man 2 dup2
       [not found]                     ` <53AFC455.8080707-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
@ 2014-06-29 15:07                       ` Rich Felker
  0 siblings, 0 replies; 6+ messages in thread
From: Rich Felker @ 2014-06-29 15:07 UTC (permalink / raw)
  To: Michael Kerrisk (man-pages); +Cc: linux-man-u79uwXL29TY76Z2rM5mHXA

On Sun, Jun 29, 2014 at 09:46:29AM +0200, Michael Kerrisk (man-pages) wrote:
> > 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 assume you're referring to this text:

       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

The one addition I think would be helpful is some mention of what
exactly these "any errors" might be.

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

My impression is that the kernel folks regard the possibility of error
from close as a design flaw, and would not add additional cases where
it could occur. I can search for citations backing this up.

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

The standards still allow fairly arbitrary failure in a number of
interfaces where there's no reasonable way for an application to
proceed on failure (e.g. pthread_mutex_unlock failing). Many of the
hypothetical errors close can return are borderline for being of this
type.

Conceptually, the failures that potentially happen at the time of
close are equivalent to hardware-level IO errors, which an application
would not detect even on local devices unless (at the very least) it
called fsync after writing. Plenty of applications don't do the latter
(and indeed it's harmful from the standpoint of many users because it
thrashes the disk).

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

Reading it now.

One thing that should be noted is that your advice for EINTR is
Linux-specific and contrary to the (new) requirements of POSIX which
make EINTR for close consistent with all other interfaces. (See
http://austingroupbugs.net/view.php?id=529 for details.) Fortunately
on Linux, as far as I can tell, close never fails with EINTR unless
you write a custom device driver that makes it possible.

Also are you sure the disk quotas text is correct? I can't imagine how
disk quotas could be implemented such that the quota would be enforced
at close time rather than write time.

Anyway, I think referencing the  notes in close(2) would be a good
step for the dup2 man page, but I still think without stronger,
up-to-date details on situations in which close could fail (to allow
programmers to make an educated decision), it all comes across as
promoting cargo-culting.

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

I agree fully that it's an improvement. If anything else comes out of
this discussion maybe it can be added later.

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

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2014-06-29 15:07 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [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)
     [not found]                     ` <53AFC455.8080707-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2014-06-29 15:07                       ` Rich Felker

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.