Path: utzoo!utgpu!jarvis.csri.toronto.edu!rutgers!usc!ginosko!aplcen!haven!mimsy!chris From: chris@mimsy.UUCP (Chris Torek) Newsgroups: comp.lang.c Subject: Re: effect of free() Message-ID: <19474@mimsy.UUCP> Date: 8 Sep 89 04:55:31 GMT References: <319@cubmol.BIO.COLUMBIA.EDU> <3756@buengc.BU.EDU> <2054@munnari.oz.au> Organization: U of Maryland, Dept. of Computer Science, Coll. Pk., MD 20742 Lines: 89 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. 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. 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. 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