Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!ames!attctc!chasm From: chasm@attctc.Dallas.TX.US (Charles Marslett) Newsgroups: comp.lang.c Subject: Re: effect of free() Summary: I think the issue is more complex Message-ID: <9278@attctc.Dallas.TX.US> Date: 9 Sep 89 02:51:11 GMT References: <319@cubmol.BIO.COLUMBIA.EDU> <3756@buengc.BU.EDU> <19474@mimsy.UUCP> Organization: The Unix(R) Connection, Dallas, Texas Lines: 119 In article <19474@mimsy.UUCP>, chris@mimsy.UUCP (Chris Torek) writes: :: In article <2054@munnari.oz.au> ok@cs.mu.oz.au (Richard O'Keefe) writes: :: >It is perfectly true that loading a (formerly valid, now invalid) address :: >into an address register might cause a trap. BUT there is no reason why :: >a compiler has to translate "if (ptr == 0)" by loading ptr into an :: >address register. :: :: True enough. (Indeed, all `if (ptr0 ptr1)' operations could :: be done without loading either pointer into an address register, on all :: machines of which I have ever heard.) The problem is that the proposed :: ANSI standard does not force compiler writers to do this. It appears from an earlier posting that only certain very baroque architectures could have this idiosyncracy (sp?). . . Because one special invalid pointer has to be acceptable in all expressions except dereferencing (NULL, that is), the processor would have to have built-in NULL pointer detection, as well as the normal protection mechanisms. This is likely to have serious performance penalties. :: Since everyone seems to want an example of a system on which :: :: ptr = malloc(size); :: if (ptr != NULL) { :: free(ptr); :: if (ptr == NULL) ... never gets here ... :: else ... never gets here either! ... :: } :: :: might be the case, perhaps we should think for a moment and construct :: one (an example, not a system). :: :: First, we need a relatively common architecture that traps when :: loading an invalid address into an address register. How about the :: 80286? It has `address' registers called CS, DS, ES, and SS, and :: these trap when a bad segment is loaded and the processor is in :: protected mode. Except for the magic segment numbers 0x0000-0x0007, which can be loaded into any segment register (maybe not CS?) and will not generate a trap. :: Now we need a way to have an invalid address happen. So: :: :: void *malloc(size_t size) { :: segment_t seg = __syscall(_GET_SEGMENT); :: if (seg == _NO_SEGMENT) return NULL; :: return _SEG_TO_ADDR(seg); :: } :: :: void free(void *p) { :: if (__syscall(_RELEASE_SEGMENT(_ADDR_TO_SEG(p)))) :: __runtime_abort("bad argument to free(): %lx", :: (long)p); :: } :: :: Now all we need is a sufficiently stupid compiler. That is not :: hard to write. We construct one that, for every pointer construct, :: compiles to something like this: :: :: ; do something with a pointer :: xor ax,ax ; check for segment 0 => nil :: or ax,[bp + stackoff + 2] :: jz Lptr_was_nil ; it was nil, so do not load it :: mov es,[bp + stackoff + 2] ; not nil, load it. :: mov di,[bp + stackoff] :: ; what are we doing? `ptr == nil'? Oh, we already did that. :: jump Lptr_was_not_nil :: Lptr_was_nil: :: ; code... :: :: (Please ignore any assembly syntax errors above; I have never used an :: 80x86 for any value of x, except perhaps as embedded controllers, where :: I have never had to program them. My only experience with 8086 :: assembly comes from looking over the operating systems class :: assignments back when they were using IBM PCs. [Now they are using :: PS-halves, er, PS/2s, for that class.] But I will say that I think the :: 8085 was nicer, and the Z80 had more reasonable opcodes. [Both of :: these were also descendents of the 4004.]) :: :: The only question the proposed ANSI standard can answer about the above :: is whether this is a conforming implementation, as far as the specification :: goes. That is, is it conforming even though it does in fact generate :: a trap on :: :: ptr = malloc(size); free(ptr); if (ptr == (char *)0) :: :: even though we are not looking at *ptr? :: :: The only *answer* the proposed standard gives is silence. That is, :: it does not say that this implementation is non-conformant (i.e., wrong). :: Thus we must conclude that it does conform, and that the trap is :: legal according to the proposed standard. And it shows just how baroque the microcode in the 80286 really must be! (Since all this code is really part of loading a segment register when running in protected mode!) :: Standards (proposed or otherwise) generally do not say anything about :: implementation quality, but I would have to agree that the implementation :: described above is horrible. It is not, however, outright illegal. :: -- :: In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) :: Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris I would have to say it is rather unlikely as well (or I would have said so before I ran into Intel CPUs ;^). Why don't we just say is it lousy looking code that does this sort of thing -- so don't do it (even though it will work 99999 times out of 10000, or whatever the fraction really is. BTW, I find the example code to be a good argument that referencing invalid pointers ought to be legal (since without that ability, a "safe" malloc like the one above, with reasonable error messages, becomes non-portable). =========================================================================== Charles Marslett STB Systems, Inc. <== Apply all standard disclaimers Wordmark Systems <== No disclaimers required -- that's just me chasm@attctc.dallas.tx.us