effect of free()

Charles Marslett chasm at attctc.Dallas.TX.US
Sat Sep 9 12:51:11 AEST 1989


In article <19474 at mimsy.UUCP>, chris at mimsy.UUCP (Chris Torek) writes:
:: In article <2054 at munnari.oz.au> ok at 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 <compare> 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 at 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 at attctc.dallas.tx.us



More information about the Comp.lang.c mailing list