Re: Closing the FILE object.

From: Khimenko Victor (khim@sch57.msk.ru)
Date: Fri Jul 14 2000 - 14:32:34 EST


In <Pine.LNX.3.95.1000714135537.2187A-100000@chaos.analogic.com> Richard B. Johnson (root@chaos.analogic.com) wrote:
> On 14 Jul 2000, Michael Poole wrote:

>> "Richard B. Johnson" <root@chaos.analogic.com> writes:
>>
>> > This is in reference to the reported seg-faults when attempting to
>> > fclose() an invalid file pointer.
>> >
>> > The following shows that my current 'C' runtime library does not
>> > adhere to any known standard when referencing the FILE object during
>> > fclose(). This is gcc 2.7.2.3 (libc 5.3.12)
>> >
>> > According to existing Linux documentation, this should return
>> > EBADF. It should NOT seg-fault.
>> >
>> > #include <stdio.h>
>> > #include <malloc.h>
>> >
>> > int main ()
>> > {
>> > FILE *fp;
>> > fp = (FILE *)malloc(sizeof(FILE));/* Make sure the pointer is valid */
>> > fprintf(stderr, "%p\n", fp); /* Display valid address */
>> > fclose(fp);
>> > puts("It worked??"); /* Will not occur */
>> > return 0;
>> > }
>>
>> 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.

Exactly. ONLY FILE* has ANY meaning. You CAN use it ONLY as opaque pointer.
FILE can be even typedef for void - it's perfectly legal thing to do.

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

You CAN use FILE pointer just fine. What you CAN NOT is to create it with
malloc, to use sizeof(FILE), etc.

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

Documentation does not states it. Happy now ?

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

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

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

> The BUG, and it is a BUG, is that a seldom used function like fclose()
> has been 'optimized'.

And it's pretty legal thing to do. There are NO standards where stated that
you can assign to stdin or stderr. GLibC allows this but it's still
non-portable.

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

Sorry. EBADF is NOT specified to be returned from EBADF if you are trying
to call it for garbage.

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

Sorry once more. There are NO documetation for fclose in linux. Not at all.
fclose is NOT system call. It's library function so it's userspace issue.
And GLibC documentation does not have such info as well.

>> I don't know what standard you think mandates that library functions
>> protect the user against severe user boneheadedness, but if you want
>> to write sane code, you shouldn't do boneheaded things.

-
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:20 EST