Subject: bug#10305: coreutils-8.14, "rm -r" fails with EBADF

From: "Joachim Schmitz" <[email-masked]>
Date: Mon, 19 Dec 2011 09:11:39 +0100

> From: Paul Eggert [mailto:[email-masked]]
> Sent: Sunday, December 18, 2011 11:24 PM
> To: Joachim Schmitz
> Cc: [email-masked]; [email-masked]
> Subject: Re: bug#10305: coreutils-8.14, "rm -r" fails with EBADF
> 
> On 12/18/11 02:31, Joachim Schmitz wrote:
> > it seems to go wrong in fcntl.c, line 194:
> >
> >             /* Haiku alpha 2 loses fd flags on original.  */
> >             int flags = fcntl (fd, F_GETFD);    <==
> >             if (flags < 0)
> >               {
> >                 result = -1;
> >                 break;
> >               }
> >
> > that fcntl() returns a -1 with errno set to EBADF.
> 
> What is the value of fd at that point?

3

> Is it actually an invalid file descriptor?

Not sure, and wouldn't know how to tell, it certainly looks valid but apparenlty fcntl() thinks otherwise.
It was my suspicion but I couldn't find it.
But see further down...
 
> > For some reason I can step into that fcntl(), guess it is calling the system's
> fcntl here?
> 
> Yes, that's how it's supposed to work.

OK, I thought so, but weren't sure.

> If you plant a breakpoint on the parent fts_build, and single step through that,
> can you find out whether continue_readdir is true?

No it is not

>  If it's true, then please
> investigate why dirfd (fts.c line 1285) returned an invalid file descriptor.  If it's
> false, then please investigate why opendirat (..., &dir_fd) (fts.c line 1301)
> succeeded but set dir_fd to an invalid file descriptor.

OK, attention, lengthy...:
opendirat() calls openat_safer() which calls openat() which calls openat_permissive() which call rpl_open() which calls orig_open() which calls open() (which succeeds and returns 3) and _gl_register_fd() (which does nothing suspicious to the fd) and finally feeds that into fd_safer(). That does nothing but checking whther fd is not between STDIN and STDERR (which it is not). Everything looks fine so far, to me anyway.
Back in opendirat() and into set_cloexec_flag(), which calls rpl_fcntl(F_GETFD)= which calls the real fcntl() and successful. It calls it again with F_SETFD, this too seems successful.
So it goes into fdopendir(), fdopendir_with_dup(), rpl_dup(), the real dup() (disguised as dup_nothrow(), with success), _gl_register_dup(), nothing but success, no suspicious activity against fd.

Then it calls close(3) !!! then close(4) (the fd from the dup())  errno set to ENOTSUP

Back in fts_build() calling dup_safer(3) (which should be invalid meanwhile, as it got closed above!) rpl_fcntl(), calling the real fcntl() with errno EBADF and errors out.

So in fdopendir_with_dup() the fd gets closed and hence made invalid?!?

Bye, Jojo