stat(), lstat() again.
Steve Dyer
dyer at spdcc.COM
Tue Sep 18 00:05:36 AEST 1990
In article <3430 at dftsrv.gsfc.nasa.gov> jim at jagmac2.gsfc.nasa.gov (Jim Jagielski) writes:
>In the 1st case, before the call, sbuf is set to NULL, thus ensuring that it
>points to nothing, so my comment that it is pointing somewhere dangerous is
>wrong (this setting of sbuf IS done in my program but was NOT included in
>my posting... sorry).
Sigh. You were a Pascal programmer in a previous life, I'll bet.
Um, it doens't point to nothing, it points to the memory location pointed
to by NULL. This is usually location 0.
>The following WILL work (although it may NOT be portable and is NOT mentioned
>in K&R):
>
> int *pint;
> pint = NULL;
> *pint = 10;
K&R or ANSI C don't prevent you from writing incorrect programs.
Unless you have explicit control over the loading of your program (say,
under an embedded system), and you KNOW that memory starting at location
0 (and going on for however long) is a valid destination, you simply
can't say anything whatsoever about whether this code fragment works.
You might as well be arguing that the following works:
int *pint;
pint = 476;
*pint = 10;
Now, some architectures have location 0 mapped into a user's process
space as writable, and will gleefully allow you to overwrite whatever
is there. On such architectures, location 0 frequently points to
once-only startup code, so since you never re-execute the now corrupted
machine code, you'll never notice the problem. More modern
architectures make such code segments read-only and sharable to begin
with, so such an attempt to overwrite the segment will fail. Even in
architectures where location 0 points to the user's data space, such an
assignment (or the call to stat/lstat) holds the possibility of
corrupting whatever the loader decided to place there. Such
architectures (like the PDP-11 I&D machines) always reserve the first
word of the data segment which starts at 0 so that no variable address
would have the value 0 (that is, to insure that &var will never equal
NULL). On such architectures, your example above would "succeed", but
the call to stat/lstat would still corrupt additional memory because
the system call would attempt to overwrite sizeof(struct stat) bytes
and not the sizeof(int) bytes in your example above. It might not
cause an exception, but that doesn't mean that it's correct in any
sense of the word. Presumably you had some data stored in those
subsequent memory locations which is now trash.
>Now some C compilers (such as GreenHills and Vax-C) will accept lstat("/unix",
>sbuf) and some won't. ALL will accept lstat("/unix",&sbuf) (assuming, of
>course, that sbuf is defined correctly, 'natch). As was mentioned in a mail
>message to me, some compilers may push a pointer to the struct in both cases,
>(although this does NOT adhere to the X3J11 standard, which says that when
>a structure is passed, the function gets an IMAGE of the structure). Therefore,
>in the compilers that DO accept this, the function is either getting a pointer
>or else the original structure. (observe that lstat expects a pointer to
>a struct)
This is not a standards issue. I don't see anywhere in your examples:
struct stat *sbuf;
lstat("/unix", sbuf);
versus
struct stat sbuf;
lstat("/unix", &sbuf);
where the issue of compilers enters in. ANY compiler should accept either
of these, and in both examples, there is no confusion about what it should
do. Issues of structure passing and how it's implemented are not relevant
here because in both cases you're not passing structures, you're passing
a pointer. It just happens that the first example is an uninitialized pointer
and the second points to something we KNOW we own.
There is a third possibility which you did not present as an example
(but which I believe you are confusing things with), namely:
struct stat sbuf;
lstat("/unix", sbuf);
Now, this is simply wrong because most system calls deal in pointers
or other scalar types, and not structures passed by value. It's
possibly that a compiler which implements structure passing by
passing pointers would cause this example to "work", but it seems
irrelevant to me, and incorrect for entirely different reasons than the
ones you are trying to propose.
>In any case, the second method (passing &sbuf) IS portable and is standard.
>This is NOT, however, picked up by lint...
Most lints would say "sbuf may be used before set" in your first example
IFF you didn't initialize it. But if you DID initialize it, even if
you initialized it to NULL, lint would no longer catch it. lint carries
no semantics which tells it that some initialization values are OK and
others aren't.
>PS: This program was a port from VaxC, which ran it with no problem. It was
> and old piece of code which I did very quick-and-dry...
So what? The VAX is one of the architectures which allows you to trash
location 0.
>PPS: I understand C quite well thank you...
It doesn't seem so.
I would be very wary of taking a device driver written by someone who
exhibits the kind of misunderstanding of pointers which you have here.
This is not a personal dig, just a reasonable observation.
--
Steve Dyer
dyer at ursa-major.spdcc.com aka {ima,harvard,rayssd,linus,m2c}!spdcc!dyer
dyer at arktouros.mit.edu, dyer at hstbme.mit.edu
More information about the Comp.unix.aux
mailing list