Path: utzoo!attcan!uunet!merk!spdcc!dyer From: dyer@spdcc.COM (Steve Dyer) Newsgroups: comp.unix.aux Subject: Re: stat(), lstat() again. Message-ID: <4062@ursa-major.SPDCC.COM> Date: 17 Sep 90 14:05:36 GMT References: <3422@dftsrv.gsfc.nasa.gov> <4038@ursa-major.SPDCC.COM> <3430@dftsrv.gsfc.nasa.gov> Reply-To: dyer@ursa-major.spdcc.com.spdcc.COM (Steve Dyer) Organization: S.P. Dyer Computer Consulting, Cambridge MA Lines: 114 In article <3430@dftsrv.gsfc.nasa.gov> jim@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@ursa-major.spdcc.com aka {ima,harvard,rayssd,linus,m2c}!spdcc!dyer dyer@arktouros.mit.edu, dyer@hstbme.mit.edu