[comp.lang.c] Yet more on malloc and free and the proposed Standard

msb@sq.sq.com (Mark Brader) (12/13/89)

This article is posted to comp.lang.c only because I just posted there
mentioning the same topic; followups are directed to comp.std.c.
I have to give a lot of background first; I indent it all by one space,
in case you're using rn and want to skip it, but the new stuff at the end
will refer back to an example in the background.

 The topic at issue is whether the proposed Standard (pANS) REQUIRES
 [I apologize for the use of block capitals for emphasis in this article,
 but I need *'s for indirection.]  that the following commonly used code...
 
 		TYPE *p;	/* TYPE being any object type */
 		p = malloc (.....);
 		  ...	/* code using p */
 		free (p);
 
 ...be legitimate, or whether instead it requires that in strict practice
 the result of malloc() be saved without being converted to TYPE *, and the
 saved value given to free().
 
 		void *vp;
 		TYPE *p;
 		vp = malloc (.....);
 		p = vp;
 		  ...	/* code using p */
 		free (vp);
 
 It is agreed that the first version works on all existing systems and that
 the pANS writers intended it to be required to work; the question is whether
 they inadvertently wrote something not restrictive enough.
 
 
 The critical sentence of the pANS seems to be the following one.  This is on
 page 155, lines 13-15 (Dec. 1988 version), near the top of section 4.10.3:
 
 #  The pointer returned if the allocation [i.e. malloc] succeeds is
 #  suitably aligned so that it may be assigned to a pointer to any
 #  type of object and then used to access such an object or an array
 #  of such objects in the space allocated ...
 
 But the description of free() in section 4.10.3.2 requires, at line 37
 of the same page, that its argument "match a pointer earlier returned by"
 malloc() or one of the related functions.
 
 The potential problem is that the word "assigned" may imply a conversion,
 and the semantics of conversion from void * (the type returned by malloc())
 to TYPE * are undefined, except when the void * pointer was created by
 conversion of a TYPE * pointer in the first place, which this one wasn't.
 (See section 3.2.2.3 and 3.3.4: when the pointer WAS so obtained, then
 on being converted back it regains its original value.)
 
 Now consider the following implementation:  Addressing is by the simple
 linear model and ints are 2 bytes and must be even-aligned.  Conversion
 from type void * to int * involves adding 1 if the void * value was not
 even-aligned.  And malloc(n) may return an odd address, but if it does so
 then it will have allocated n+1 bytes.
 
 This example implementation is clearly not in accordance with existing
 implementations, where "ints must be even-aligned" would imply "malloc()
 only returns even addresses".  But it was argued that it would be in
 conformance with the pANS, because of that word "assigned to" in 4.10.3.
 
 For example, if malloc(100) returned 1345, then it would have allocated
 101 bytes from 1345 to 1445.  If TYPE was int in the first example code
 above, then p would point to location 1346, and an array of 50 ints
 (totaling the requested 100 bytes) could be accessed in locations 1346
 to 1445.  But the call free(p) would not work, because the correct argument
 to give to free() would be a pointer of type void * and value 1345.
 
 It has been suggested that the word "match" in 4.10.3.2 could be interpreted
 in such a way that the 1345 and 1346 would match; I don't accept this, as
 I find reasonable no other reading for this word than that the pointers would
 compare equal using the == operator, which they would not.
 
 It has also been suggested that the word "aligned" was meant to have a
 stronger meaning, similar to that in the existing implementations.  For
 instance, if it was taken to mean that the pointer must have a value
 which COULD HAVE been obtained by conversion from any pointer-to-object
 type TYPE *.  (For instance, in the example this would require it to be
 even-aligned.)
 
 In that case, the requirements in 3.2.2.3 and 3.3.4 are activated; when
 the void * is converted to TYPE *, it converts to the TYPE * value that it
 could have been converted from, and therefore, converting that TYPE * value
 back to void * is bound to produce the same void * value as the conversion
 that could have happened before would have produced, i.e., the value
 returned by malloc().  Therefore the pointer passed to free() will indeed
 match the one returned by malloc().
 
 I agree that this may have been what was INTENDED, but I don't think it
 is what the standard SAYS.


However, I have what I think is a new and successful argument that really
does require the first example code to be successful.  First note the
description of malloc(), from section 4.10.3.3, page 156, lines 6 and 8:

#         void *malloc (size_t size);
#
#   The malloc function allocates space for an object whose size is
#   specified by size ...

And now note again the words from 4.10.3:

#  The pointer returned ... may be assigned to a pointer to any
#  type of object and then used to access such an object or an array
#  of such objects in the space allocated ...

Notice the words "in the space allocated"!  While the words quoted in
4.10.3.3 do not forbid malloc() from using storage for its own internal
purposes, they do require that the size of the space allocated be what
the argument of malloc specifies.

The hypothetical implementation discussed above may be interpreted in
three ways, each of which violates some part of this.

(a) The space allocated is 101 bytes, from 1345 to 1445.  This violates
    the "size specified" wording in 4.10.3.3.

(b) The space allocated is 100 bytes, from 1345 to 1444, and location 1445
    is overhead.  This violates the "in the space allocated" wording in
    4.10.3: when the pointer is converted to int * and used to access a
    100-byte, 50-int array, location 1445 is accessed.

(c) The space allocated is 100 bytes, from 1346 to 1445, and location 1345
    is overhead.  This would violate the "in the space allocated" wording
    if the value returned by malloc() was converted to char * rather than
    int *, whereupon location 1345 might be accessed; and in any case it
    also violates line 7 in section 4.10.3, which requires that the pointer
    value returned point to the first location in the allocated space.

Thus the hypothetical implementation is NOT conforming, and so, this is
not a bug in the pANS.  Unless of course there's something that I've missed.


-- 
Mark Brader, Toronto	    "If you feel [that Doug Gwyn] has a bad attitude,
utzoo!sq!msb, msb@sq.com     then use lint (or Chris Torek...)" -- Joe English

This article is in the public domain.