Re: Closing the FILE object.

From: Michael Poole (poole@troilus.org)
Date: Fri Jul 14 2000 - 17:55:18 EST


"Richard B. Johnson" <root@chaos.analogic.com> writes:

> On 14 Jul 2000, Michael Poole wrote:
>
> >
> > Don't you remember what happened the last time you tried to treat
> > FILE* as something besides an opaque pointer? I thought Uli Drepper
> > was clear in his explanation that you're not allowed to do that and
> > expect consistent results. A FILE* may have arbitrary state inside of
> > it, and if you didn't get the FILE* from an fopen() type call (or as
> > stdin, stdout, stderr), then you can't expect fclose() to work.
> >
>
> Yes I remember. And the answer was that FILE wasn't FILE. In
> the failed explaination, the response boiled down to the idea
> of using FILE as FILE was dumb and that I was dumb to even think
> that I could use a FILE pointer as a FILE pointer.

No, using it as a FILE pointer is fine. Assuming *anything* about the
thing on the other side of the pointer is what got you into trouble.
Both then and now.

> > In fact, if you even THOUGHT about it, you'd see that your example is
> > pretty stupid. A FILE* stream is supposed to have a buffer. If the
> > thing pointed to by FILE* contains a pointer to that buffer, then you
> > just gave fclose() something with illegal state. There are a number
> > of other reasons that you should not expect an arbitrary FILE* to be
> > acceptable to fclose().
> >
>
> I gave fclose() a bad file descriptor. It should return EBADF just
> like the documentation states.

Whose documentation is that? On my system, the man pages in
/usr/man/man3 are not written by the same people as my libc, and the
people who wrote libc don't claim to follow any behavior documented
there.

On the other hand, I *do* have a libc.info file that talks about
fclose(), and it says that it returns -1/EBADF *if* an error is
discovered. It doesn't say what happens if it doesn't discover a
fatal error (it doesn't even says it always returns), and it doesn't
say what happens if I decide to fire a shotgun slug through my
computer while fclose() is running.

> > Further, I don't think the standard even mandates that FILE by itself
> > be meaningful. If FILE is typedef'ed as char, no interface would
> > break (although warnings would be harder to diagnose), but your code
> > above would (rightly) break the stdio.h implementation.
> >
>
> FILE can't be typedef'ed to something for one function call and
> be something else for another. Whatever FILE is, it is forever, until
> #undef(ed).

typedef and #define/#undef are very different. As Khimenko Victor
already pointed out, you can't assume anything about what FILE
actually is.

> FILE it typedefed to struct _IO_FILE in the current headers.
> struct _IO_FILE is defined in libio.h.
>
> A pointer to type FILE is, therefore, a pointer to the structure defined
> in libio.h.
>
> A FILE * is therefore a 'struct _IO_FILE *'.

On your system, yes. On my system, I assume they're something quite
different. On my system, don't care what they are, because they work
for me (doing things within the C standard specification).

> stdin, stdout, and stderr are defined in stdio.h. They are:
> extern FILE *stdin, *stdout, *stderr.
>
> Then there is a redefinition:
>
> #define stdin _IO_stdin;
> #define stdout _IO_stdout;
> #define stderr _IO_stderr;
>
> So, let's see what they are:
> In libio.h, they reference _IO_FILE *, initialized with the address
> of the specific structure. They are, therefore, in fact, valid
> pointers of type FILE.
>
> So FILE *stdin is like any other FILE *. It should work by the same
> rules.

If that's what stdin is on your system, then sure, it should work by
the same rules. If you act on assumptions about what a FILE* points
to, you're trying to break those rules, at which point you deserve
whatever happens to your program.

> The BUG, and it is a BUG, is that a seldom used function like fclose()
> has been 'optimized'.
>
> struct _IO_FILE contains some pointers. If these pointers are dereferenced
> during the fclose(), and they are NULL, or point to memory you don't own,
> your program will seg-fault.
>
> fclose() should check these structure members and return EBADF,
> as specified, instead of just crashing.

Except that there's really no way to verify that a pointer is valid
from userspace that doesn't involve possibly triggering a SIGSEGV or
SIGBUS. Even if they're mapped to valid memory, they might have been
free()'d and reallocated later.

> Now, if the specification of fclose() allowed a seg-fault that's quite
> another thing. According to Linux's own documentation, it should
> return with -1 and set errno to EBADF if I give it trash.

The documentation for the Linux kernel doesn't mention fclose(). The
documentation in fclose.3 on my machine isn't canonical for my libc
(and I suspect you will find your fclose.3 isn't canonical for your
libc, either). The documentation for fclose() in libc.info on my
machine doesn't say what happens if I force it to an undiscovered
fatal error.

I'm not sure what you find so confusing about the behavior you're
seeing, and I'm not sure why you're claiming that fclose() has been
``optimized'' in some harmful way. As far as I can tell, the only
error it has made is the fatal error of trusting a foolish user to not
poke its innards and violate invariants on opaque pointers' data that
libc is probably very careful to preserve.

Michael

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Sat Jul 15 2000 - 21:00:21 EST