[comp.std.c] Out-of-bounds pointers

ed@mtxinu.COM (Ed Gould) (10/03/89)

Is the following code conformant?  It's clear that it's not legal to
dereference the pointer in its "illegal" state, but is the p++ line
guaranteed to return it to a valid value?  What would it (be expected
to) print?


	void
	f() {
		char bug[100];
		char *p;

		p = buf;

		p--;	/* p contains an illegal value: &buf[-1] */
		p++;	/* hopefully, now p == &buf[0] */

		if(p == buf)
			printf("It worked\n");
		else
			printf("It failed\n");
	}

henry@utzoo.uucp (Henry Spencer) (10/03/89)

In article <1009@mtxinu.UUCP> ed@mtxinu.COM (Ed Gould) writes:
>Is the following code conformant?  It's clear that it's not legal to
>dereference the pointer in its "illegal" state, but is the p++ line
>guaranteed to return it to a valid value? ...
>		p = buf;
>		p--;	/* p contains an illegal value: &buf[-1] */
>		p++;	/* hopefully, now p == &buf[0] */

The effect of the `p--' line is undefined, so all bets are off.  Pointer
arithmetic (not dereferencing) is guaranteed to be well-behaved when a
pointer just past the *end* of the array is involved, but no such promises
are made about pointers just before the *beginning*.  It may work; it may
dump core; it may yield random results.
-- 
Nature is blind; Man is merely |     Henry Spencer at U of Toronto Zoology
shortsighted (and improving).  | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

gwyn@smoke.BRL.MIL (Doug Gwyn) (10/04/89)

In article <1009@mtxinu.UUCP> ed@mtxinu.COM (Ed Gould) writes:
>Is the following code conformant?  It's clear that it's not legal to
>dereference the pointer in its "illegal" state, but is the p++ line
>guaranteed to return it to a valid value?

It's not even "legal" to compute an invalid address, whether or not
it is dereferenced.  Your example has implementation-dependent
behavior; it is not too unlikely that it would even abort under some
circumstances.

Pointers one past the end of an array are valid, but not pointers
before the beginning of an array.

scjones@sdrc.UUCP (Larry Jones) (10/04/89)

In article <1009@mtxinu.UUCP>, ed@mtxinu.COM (Ed Gould) writes:
> Is the following code conformant?  It's clear that it's not legal to
> dereference the pointer in its "illegal" state, but is the p++ line
> guaranteed to return it to a valid value?  What would it (be expected
> to) print?
> 
> 		p = buf;
> 		p--;	/* p contains an illegal value: &buf[-1] */
> 		p++;	/* hopefully, now p == &buf[0] */

Nope.  Once you've decremented p you've entered the twilight zone
of undefined behaviour where your program could dump core, play
the Star Spangled Banner, or continue on.  If it continued, the
increment may or may not set it pointing to buf again.
----
Larry Jones                         UUCP: uunet!sdrc!scjones
SDRC                                      scjones@SDRC.UU.NET
2000 Eastman Dr.                    BIX:  ltl
Milford, OH  45150-2789             AT&T: (513) 576-2070
"I have plenty of good sense.  I just choose to ignore it."
-Calvin

doug@letni.UUCP (Doug Davis) (10/04/89)

In article <1009@mtxinu.UUCP> ed@mtxinu.COM (Ed Gould) writes:
>	void
>	f() {
>		char bug[100];
             ^^^^^^^^^^  I assume you mean buf here, right?
>		char *p;
>
>		p = buf;
>
>		p--;	/* p contains an illegal value: &buf[-1] */
        ^^^ Opps,  You have now entered the land of illegal addresses,
                   normally called the twilight zone, incrementing the
                   pointer is no longer guarenteed to be == &buf[0], that
                   is installation dependent.  Most likely it will == something
                   really weird and you get a core dump.  Which one you get
                   is left as an exercise to the reader.
    
                 
doug
__
Doug Davis/1030 Pleasant Valley Lane/Arlington/Texas/76015/817-467-3740
{sys1.tandy.com, motown!sys1, uiucuxc!sys1 lawnet, attctc, texbell} letni!doug
"ack, pifft!  <ZOT> RRREEEEAAAAAWWWWOOOOOO!" -- Sound effects from the "Bill
The Cat'lprod(TM)" Commercial,  Available in better hardware stores near you.

walter@hpclwjm.HP.COM (Walter Murray) (10/05/89)

Ed Gould:

> Is the following code conformant?  It's clear that it's not legal to
> dereference the pointer in its "illegal" state, but is the p++ line
> guaranteed to return it to a valid value?

Henry Spencer:

> The effect of the `p--' line is undefined, so all bets are off.

Doug Gwyn:

> It's not even "legal" to compute an invalid address, whether or not
> it is dereferenced.

I agree with Henry and Doug (generally a safe bet!), but I do think
the wording of 3.3.6 might be misleading:  "Unless both the pointer
operand and the result point to elements of the same array object,
or the pointer operand points one past the last element of an array
object and the result points to an element of the same array object,
the behavior is undefined if the result is used as an operand of
the unary * operator."  Doesn't this imply rather strongly that it
IS legal to compute an invalid address, as long as it isn't
dereferenced?

Walter Murray
walter@hpda.HP.COM
---

scott@bbxsda.UUCP (Scott Amspoker) (10/06/89)

 In article <4199@letni.UUCP> doug@letni.LawNet.Com (Doug Davis) writes:
 >In article <1009@mtxinu.UUCP> ed@mtxinu.COM (Ed Gould) writes:
 >>     void
 >>     f() {
 >>            char bug[100];
 >             ^^^^^^^^^^  I assume you mean buf here, right?
 >>            char *p;
 >>
 >>            p = buf;
 >>
 >>            p--;	/* p contains an illegal value: &buf[-1] */
 >             ^^^ Opps,  You have now entered the land of illegal addresses,
 >                 normally called the twilight zone, incrementing the
 >                 pointer is no longer guarenteed to be == &buf[0], that
 >                 is installation dependent.  Most likely it will == something
 >                 really weird and you get a core dump.  Which one you get
 >                 is left as an exercise to the reader.

Agreed, it is undefined.  However *most likely* it will work just fine (based
on the several dozen systems I've worked with).  I would recommend avoiding
it though.

-- 
Scott Amspoker
Basis International, Albuquerque, NM
(505) 345-5232

davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) (10/06/89)

In article <12570028@hpclwjm.HP.COM>, walter@hpclwjm.HP.COM (Walter Murray) writes:

|  Doug Gwyn:
|  
|  > It's not even "legal" to compute an invalid address, whether or not
|  > it is dereferenced.

  While this is obviously true, I have never understood the rationale of
this decision. Given that (a) there are existing programs which do this,
for reasons other than sloppy programming, (b) most implementations
happily allow this, and (c) if you are allowed to declare an auto
pointer at all then obviously the hardware supports uninitialized
pointers, I fail to see what benefit is gained.

  Yes, I do understand the diference between calculating such a pointer
and dereferencing it, and obviously dereferencing it is obviously
non-portable.

  The only reason I have heard is the argument that there might be (or
is) a machine such that (a) it checks addresses stored in address
registers when stored, (b) the checking can't be turned off by software,
and (c) the machine is incapable of storing the address in memory or any
other pointer and loading it into the pointer register only when
dereference is needed.

  Could someone clarify this, since it certainly is not prevailing
practice? 
-- 
bill davidsen	(davidsen@crdos1.crd.GE.COM -or- uunet!crdgw1!crdos1!davidsen)
"The world is filled with fools. They blindly follow their so-called
'reason' in the face of the church and common sense. Any fool can see
that the world is flat!" - anon

henry@utzoo.uucp (Henry Spencer) (10/06/89)

In article <12570028@hpclwjm.HP.COM> walter@hpclwjm.HP.COM (Walter Murray) writes:
>the wording of 3.3.6 might be misleading:  "Unless both the pointer
>operand and the result point to elements of the same array object,
>or the pointer operand points one past the last element of an array
>object and the result points to an element of the same array object,
>the behavior is undefined if the result is used as an operand of
>the unary * operator."  Doesn't this imply rather strongly that it
>IS legal to compute an invalid address, as long as it isn't
>dereferenced?

Read the previous sentence in 3.3.6:  "If both the pointer operand and
the result point to elements of the same array object, or one past the
last element of the array object, the evaluation shall not produce an
overflow; otherwise the behavior is undefined."
-- 
Nature is blind; Man is merely |     Henry Spencer at U of Toronto Zoology
shortsighted (and improving).  | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

gwyn@smoke.BRL.MIL (Doug Gwyn) (10/07/89)

In article <12570028@hpclwjm.HP.COM> walter@hpclwjm.HP.COM (Walter Murray) writes:
-the wording of 3.3.6 might be misleading:  "Unless both the pointer
-operand and the result point to elements of the same array object,
-or the pointer operand points one past the last element of an array
-object and the result points to an element of the same array object,
-the behavior is undefined if the result is used as an operand of
-the unary * operator."  Doesn't this imply rather strongly that it
-IS legal to compute an invalid address, as long as it isn't
-dereferenced?

Not the way I read it in context, and certainly not the way it was
intended.  There are a lot of constraints on pointer arithmetic in
the same paragraph; the cited sentence is merely one more constraint.
It IS undefined behavior if you try to access some place outside the
actual array.  There are many other ways to obtain undefined behavior
during pointer arithmetic itself.

gwyn@smoke.BRL.MIL (Doug Gwyn) (10/07/89)

In article <868@crdos1.crd.ge.COM> davidsen@crdos1.UUCP (bill davidsen) writes:
>In article <12570028@hpclwjm.HP.COM>, walter@hpclwjm.HP.COM (Walter Murray) writes:
>|  Doug Gwyn:
>|  > It's not even "legal" to compute an invalid address, whether or not
>|  > it is dereferenced.
>  While this is obviously true, I have never understood the rationale of
>this decision. Given that (a) there are existing programs which do this,
>for reasons other than sloppy programming,

How could they?  If a computation produces a genuinely meaningless
result, how can a reasonable program rely on it?

>(b) most implementations happily allow this,

You mean, most implementations don't provide any sensible check for
this.  The worst bugs I've found in UNIX software were due to
computing invalid pointers as an intermediate step in an algorithm.
When it happened to work, it was BY ACCIDENT.  When it failed, it
was a MYSTERY (until I finally tracked it down).  If you think that
this is desirable behavior, then you're nuts.

>(c) if you are allowed to declare an auto pointer at all then obviously
>the hardware supports uninitialized pointers,

Obviously?  Allocation of storage is not the same as accessing its
contents.

>I fail to see what benefit is gained.

The "benefit" is that faster, more natural C implementations are
permitted on architectures where this is an issue.

Another benefit is that we do not have to figure out rules for
assigning meanings to inherently meaningless address-arithmetic
results, as we would be obliged to do if they were guaranteed
legitimate by the standard.

The final benefit is to bring the non-portable (and often unsafe)
nature of such operations clearly before the eyes of the C
programming public.

>Could someone clarify this, since it certainly is not prevailing
>practice? 

I have no idea what you mean by "prevailing practice".  The only
significant prevailing C "standard" was K&R 1st Edition Appendix A.
It certainly did not promise that randomly computed addresses would
always be valid pointer values.

scott@bbxsda.UUCP (Scott Amspoker) (10/07/89)

In article <868@crdos1.crd.ge.COM> davidsen@crdos1.UUCP (bill davidsen) writes:
>|  Doug Gwyn:
>|  
>|  > It's not even "legal" to compute an invalid address, whether or not
>|  > it is dereferenced.
>  While this is obviously true, I have never understood the rationale of
>this decision. Given that (a) there are existing programs which do this,
>for reasons other than sloppy programming, (b) most implementations
>happily allow this, and (c) if you are allowed to declare an auto
>pointer at all then obviously the hardware supports uninitialized
>pointers, I fail to see what benefit is gained.

We just got through an *extremely* long thread in comp.lang.c regarding
this issue (it eventually just fizzled out).  The basic idea is that a
pointer variable containing a bad (or uninitialized) address could be
loaded into an address register which, on some architectures, could
cause a "bad address" trap.  For example, the 286/386 CPUs will trap
if you load just any 'ol garbage into a segment register.  Since
handling a pointer variable might cause such a load operation you
could get a trap even though you did not de-reference the pointer.

This bothered many readers while other readers attempted to jusitfy
such an "implementation specific" detail.  What was once bad coding
style now was considered a bug.  Take the following code fragment as
an example:

my_proc()
   {
   register char *p;

   p = (char*)malloc(1000);
   free(p);  /* free never returns but core dumps instead - why? */
   }

This seemingly innocent code could possibly error out according to the
"rules of comformance" presented by some readers.  Here's why:

   a) pointer p is possibly in an address register which might be
      sensitive to bad addresses.

   b) p is assigned a valid address by malloc() - no problem.

   c) the free() call may return the freed memory block to the host
      system.  It was agreed that a conforming implementation had
      that right.  Therefore, the address in p might no longer be
      part of the process's address map and therefore invalid.

   d) Upon return from free() the (now invalid) value in p is popped
      off the stack back into its address register and... TRAP!

Don't worry - all is not lost.  No one was able to come up with a real
world example of something like this.  In other words - standards and
ANSI drafts aside - you probably will not get into trouble unless you
actually try to *de-reference* a bad pointer.

-- 
Scott Amspoker
Basis International, Albuquerque, NM
(505) 345-5232

gwyn@smoke.BRL.MIL (Doug Gwyn) (10/07/89)

In article <217@bbxsda.UUCP> scott@bbxsda.UUCP (Scott Amspoker) writes:
-my_proc()
-   {
-   register char *p;
-   p = (char*)malloc(1000);
-   free(p);  /* free never returns but core dumps instead - why? */
-   }
-This seemingly innocent code could possibly error out according to the
-"rules of comformance" presented by some readers.

NO NO NO.  You have mispresented the argument.  So long as malloc()
(assumed to be properly declared!) doesn't return a null pointer,
the above will work in ALL conforming implementations.  The trouble
arises only when after the free() the pointer p (NOT what it points
to, that's inarguably invalid) continues to be examined or otherwise
manipulated by the program.

-Don't worry - all is not lost.  No one was able to come up with a real
-world example of something like this.  In other words - standards and
-ANSI drafts aside - you probably will not get into trouble unless you
-actually try to *de-reference* a bad pointer.

Nobody came up with YOUR example, but there were several examples
posted of genuine computer architectures where continued access of
an invalid pointer value would cause problems.

ok@cs.mu.oz.au (Richard O'Keefe) (10/07/89)

In article <868@crdos1.crd.ge.COM> davidsen@crdos1.UUCP (bill davidsen) writes:
>|  Doug Gwyn:
>|  > It's not even "legal" to compute an invalid address, whether or not
>|  > it is dereferenced.
>  While this is obviously true, I have never understood the rationale of
>this decision. Given that (a) there are existing programs which do this,
>for reasons other than sloppy programming, (b) most implementations
>happily allow this, and (c) if you are allowed to declare an auto
>pointer at all then obviously the hardware supports uninitialized
>pointers, I fail to see what benefit is gained.

(b) The implementations which allowed it don't have to stop allowing it.
(c) Not so; the compiler might quietly initialise every pointer variable
    to a valid address.  (Auto variables might be allocated by pushing
    the current value of the SP on the stack, for example.)

In article <217@bbxsda.UUCP>, scott@bbxsda.UUCP (Scott Amspoker) writes:
> We just got through an *extremely* long thread in comp.lang.c regarding
> this issue (it eventually just fizzled out).  ... For example, the
> 286/386 CPUs will trap if you load just any 'ol garbage into a segment
> register.  Since handling a pointer variable might cause such a load
> operation you could get a trap even though you did not de-reference the
> pointer.

I participated in that thread.  Basically it fizzled out because it was
quite clear that the restriction isn't going to go away, so there wasn't
any point in talking further.  But it also became clear that the 80286
and 80386 are *NOT* examples of the problem; you wouldn't do pointer
comparisons or pointer arithmetic in a segment register on an 80286
because you _can't_.

> Don't worry - all is not lost.  No one was able to come up with a real
> world example of something like this.

There is a practical case where this _is_ likely to come up.
C interpreters.  There are already C interpreters which do things like
checking that a pointer doesn't point to anywhere wild and that pointer
comparisons involve pointers into the same array.  It would not be
surprising if ANSI C interpreters make this check too.

You can, if necessary, make your array one element longer than you need.

chris@mimsy.UUCP (Chris Torek) (10/07/89)

[original question was about code similar to

	int array[K];
	p = &array[0];	/* ok */
	p--;		/* invalid */
]

>In article <217@bbxsda.UUCP>, scott@bbxsda.UUCP (Scott Amspoker) writes:
>>We just got through an *extremely* long thread in comp.lang.c regarding
>>this issue ...

Actually, that thread was arguing about the invalid pointer value produced
by freeing a valid malloc()-derived pointer.  These are in fact entirely
different things: the freed pointer became invalid by the freeing action,
even though its bit pattern did not change, while the pointer above became
invalid by a pointer-arithmetic operation (which presumably changed its
bit pattern).

In article <2322@munnari.oz.au> ok@cs.mu.oz.au (Richard O'Keefe) writes:
>I participated in that thread.  Basically it fizzled out because it was
>quite clear that the restriction isn't going to go away, so there wasn't
>any point in talking further.  But it also became clear that the 80286
>and 80386 are *NOT* examples of the problem; you wouldn't do pointer
>comparisons or pointer arithmetic in a segment register on an 80286
>because you _can't_.

Actually, in huge model, p-- really does do arithmetic on a value that
might be automatically reloaded into a segment register.  For instance,

	void f(void) {
		extern char foo[];
		extern int g(int);
		register char *p = foo + g(0);

		while (p != foo + g(1)) p--; /* strange timing loop, perhaps */
		*p++ = 'a'; *p++ = 'b'; *p = 'c';
	}

is as likely to use `es:di' for p as any other register pair (after
all, it is *a* pointer, and is the *only* pointer, and is used *as a*
pointer).  The comparison and decrement are in fact better done from
memory or from si:di, but some compilers are likely to simply say
`this looks like a pointer; we are in huge model; use es:di'.

>You can, if necessary, make your array one element longer than you need.

This is in fact the only certain solution for

	for (p = &array[K]; --p >= array;)

(i.e., change it to

	some_type array_[K+1];		/* dummy element at 0 */
	#define array (array_ + 1)	/* subscript range [-1..K-1] */
	for (p = &array[K]; --p >= array;)

or something equivalent).  Otherwise, if some_type is, e.g.,

	typedef struct enormous { char a[60000]; } sometype;

the operation `--p' will subtract 60,000 bytes from p on a VAX or Sun;
this could easily produce an underflow (32k of text, e.g.).  On a VAX,
it is possible to trap on integer underflow, including pointer
underflow, so you could rig a system to produce a runtime trap here.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris

flaps@dgp.toronto.edu (Alan J Rosenthal) (10/08/89)

scott@bbxsda.UUCP (Scott Amspoker) writes:
>What was once bad coding style now was considered a bug.  Take the following
>code fragment as an example:
>
>my_proc()
>   {
>   register char *p;
>
>   p = (char*)malloc(1000);
>   free(p);  /* free never returns but core dumps instead - why? */
>   }

No, no, no.  It was never said that this code fragment wasn't conforming.  It
is, other than the missing declaration of malloc() which I assume you assumed
was present previously in the file.  free() has to work correctly.  The user
cannot dereference invalid pointers, but the compiler can if it knows that it
is safe to do so, and must not dereference invalid pointers if it doesn't know
whether or not it is safe.

And this fragment was never considered bad coding style, again apart from the
fact that you are mallocing memory and not doing anything with it.  You seem to
be claiming that use of free() is now non-portable and was always considered
bad coding style.

ajr

scott@bbxsda.UUCP (Scott Amspoker) (10/09/89)

In article <1989Oct7.131404.656@jarvis.csri.toronto.edu> flaps@dgp.toronto.edu (Alan J Rosenthal) writes:
>scott@bbxsda.UUCP (Scott Amspoker) writes:
>>What was once bad coding style now was considered a bug.  Take the following
>>code fragment as an example:
>>
>>my_proc()
>>   {
>>   register char *p;
>>
>>   p = (char*)malloc(1000);
>>   free(p);  /* free never returns but core dumps instead - why? */
>>   }

>No, no, no.  It was never said that this code fragment wasn't conforming.  It

It was never said that *this* particular example wasn't conforming.  However,
as I pointed out in my posting, the various "rules" that were stated in the
prior thread would lead to the conclusion that this example was non-conforming.
I presented each rule leading to that conclusion.  Please explain which
of the rules you disagree with.

>is, other than the missing declaration of malloc() which I assume you assumed
>was present previously in the file.  free() has to work correctly.  The user
                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Precisely - so is there something wrong with the ANSI draft or what?

>cannot dereference invalid pointers, but the compiler can if it knows that it
>is safe to do so, and must not dereference invalid pointers if it doesn't know
>whether or not it is safe.

No pointer dereferencing took place at all in the example.  (Now you know
why the prior thread went on so long :-).

>And this fragment was never considered bad coding style, again apart from the
>fact that you are mallocing memory and not doing anything with it.  You seem to

This was a trivial example trying to make a point.  I don't see any reason
to clutter it with "busy code" to make it look real.  It is not too hard
to imagine that something "comforming" was done between malloc() and free().

>be claiming that use of free() is now non-portable and was always considered
>bad coding style.

I am saying that it is possible to come to the conclusion that free() is
not portable depending on how you interpret the ANSI draft.   I was
pointing out something I thought was absurd.  I expect the above program
(assuming the proper declaration of malloc() and that there is available 
memory) to *always* work no matter what.


-- 
Scott Amspoker
Basis International, Albuquerque, NM
(505) 345-5232

msb@sq.sq.com (Mark Brader) (10/10/89)

> > It's not even "legal" to compute an invalid address, whether or not
> > it is dereferenced.
> ... if you are allowed to declare an auto pointer at all
> then obviously the hardware supports uninitialized pointers ...

No; the implementation could elect to initialize everything, say to a null
pointer, as part of the action of bringing the variable into existence in
addressable space.  (And remember, a null pointer could be implemented as
a pointer to a particular anonymous object somewhere.)

-- 
Mark Brader			"You have a truly warped mind.
SoftQuad Inc., Toronto	         I admire that in a person."
utzoo!sq!msb, msb@sq.com				-- Bill Davidsen

This article is in the public domain.

scott@bbxsda.UUCP (Scott Amspoker) (10/10/89)

In article <11234@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:
 >In article <217@bbxsda.UUCP> scott@bbxsda.UUCP (Scott Amspoker) writes:
 >-my_proc()
 >-   {
 >-   register char *p;
 >-   p = (char*)malloc(1000);
 >-   free(p);  /* free never returns but core dumps instead - why? */
 >-   }
 >-This seemingly innocent code could possibly error out according to the
 >-"rules of comformance" presented by some readers.
 >
 >NO NO NO.  You have mispresented the argument.  So long as malloc()
 >(assumed to be properly declared!) doesn't return a null pointer,
 >the above will work in ALL conforming implementations.  The trouble
 >arises only when after the free() the pointer p (NOT what it points
 >to, that's inarguably invalid) continues to be examined or otherwise
 >manipulated by the program.

As my original posting explained, the pointer *is* manipulated by
popping it off the stack upon return from free().  The original
posting explained every step that takes place leading up to the core
dump.  If you believe that any of those steps violates the ANSI draft
then please state which one.

>-Don't worry - all is not lost.  No one was able to come up with a real
>-world example of something like this. 

>Nobody came up with YOUR example, but there were several examples
>posted of genuine computer architectures where continued access of
>an invalid pointer value would cause problems.

I won't rehash this since this whole pointer thing was a topic off the
longest thread I have ever seen in comp.lang.c.  Unless I missed
a posting on the other thread, *no one* was able to provide a specific
example and an architecture *and* an associated C implementation on
*that* architecture that created such a problem for C programs.  In
other words, the code generated by the C compiler did not tempt
Murphy's Law even though the architecture had the potential.

If someone out there knows of such an implementation please share it
with us.  I am interested in seeing the assembly code output of
a C routine the bombs by *assigning* or *comparing* bad pointer.


-- 
Scott Amspoker
Basis International, Albuquerque, NM
(505) 345-5232
unmvax.cs.unm.edu!bbx!bbxsda!scott

davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) (10/11/89)

In article <11232@smoke.BRL.MIL>, gwyn@smoke.BRL.MIL (Doug Gwyn) writes:
|	[ read the original ]
|  
|  How could they?  If a computation produces a genuinely meaningless
|  result, how can a reasonable program rely on it?

  Consider: a program is going to read some LARGE number of values in
the range 30000-30006. It wants to count the number of occurences of
each value. Therefore:
	long ShortVect[10] = {0,0,0,0,0,0,0,0,0,0}, *Ptr, Val;
	
	Ptr = &ShortVect[-30000];
	while ((Val = MyRead()) > 0) Ptr[Val]++;

  Note that by creating the pointer to an invalid address we now can add
the value as a subscript. The address modified is *always* valid (if the
input is in range), the vector doesn't need to be 30007 in length, and
you don't have to subtract 30000 each time.

  I make no claim that this is the only way to do this, simple that
there are programs around which actually do use this trick (I saw it in
a net program). I submit that this is neither sloppy programming or
meaningless, and that it is "not illegal" by K&R 1st Ed. It's a trick to
save some space and CPU, something anyone on a small machine tries to do.

|  
|  was a MYSTERY (until I finally tracked it down).  If you think that
|  this is desirable behavior, then you're nuts.

  That's certainly keeping the discussion on a high technical plane.


|  >I fail to see what benefit is gained.
|  
|  The "benefit" is that faster, more natural C implementations are
|  permitted on architectures where this is an issue.

  Please identify the machines in question and quantify the saving.

|  
|  >Could someone clarify this, since it certainly is not prevailing
|  >practice? 
|  
|  I have no idea what you mean by "prevailing practice".  

  Prevailing practice means what it says, that before compilers were
modified to make it illegal such code would work on most machines, such
as Sun, Vax, PC, Cray, PDP-11, etc. I think that fairly represents at
least 80% of the machines and users running pre-ANSI C.
-- 
bill davidsen	(davidsen@crdos1.crd.GE.COM -or- uunet!crdgw1!crdos1!davidsen)
"The world is filled with fools. They blindly follow their so-called
'reason' in the face of the church and common sense. Any fool can see
that the world is flat!" - anon

terryl@tekcrl.LABS.TEK.COM (10/11/89)

In article <231@bbxsda.UUCP> scott@bbxsda.UUCP (Scott Amspoker) writes:
+In article <11234@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:
+ >In article <217@bbxsda.UUCP> scott@bbxsda.UUCP (Scott Amspoker) writes:
+ >-my_proc()
+ >-   {
+ >-   register char *p;
+ >-   p = (char*)malloc(1000);
+ >-   free(p);  /* free never returns but core dumps instead - why? */
+ >-   }
+ >-This seemingly innocent code could possibly error out according to the
+ >-"rules of comformance" presented by some readers.
+ >
+ >NO NO NO.  You have mispresented the argument.  So long as malloc()
+ >(assumed to be properly declared!) doesn't return a null pointer,
+ >the above will work in ALL conforming implementations.  The trouble
+ >arises only when after the free() the pointer p (NOT what it points
+ >to, that's inarguably invalid) continues to be examined or otherwise
+ >manipulated by the program.
+
+As my original posting explained, the pointer *is* manipulated by
+popping it off the stack upon return from free().  The original
+posting explained every step that takes place leading up to the core
+dump.  If you believe that any of those steps violates the ANSI draft
+then please state which one.


     Um, err, how about the one that says C arguments are "call by value,
and not by reference". If I remember correctly, your initial argument said
that p could live in a special address register, then placed on the stack
for the call to free(), and then popped off of the stack and put back in
that special address register; that's call by value/result, not call by
value, so the last step (put back in that special address register) should
NOT be done at all.....

flaps@dgp.toronto.edu (Alan J Rosenthal) (10/11/89)

scott@bbxsda.UUCP (Scott Amspoker) writes:
>The various "rules" that were stated in the prior thread would lead to the
>conclusion that this example was non-conforming.  I presented each rule leading
>to that conclusion.  Please explain which of the rules you disagree with.

I don't recall a rule saying that the internal workings of the argument or
register stack had to be implemented in a portable way, but if there was such a
rule, that is the one I disagreed with.  No (correct) rules about C describe
the manner in which this stuff is implemented.

>>The user cannot dereference invalid pointers, but the compiler can if ...
>No pointer dereferencing took place at all in the example.  (Now you know
>why the prior thread went on so long :-).

Oops, sorry, I meant manipulate invalid pointers.

>>And this fragment was never considered bad coding style, again apart from the
>>fact that you are mallocing memory and not doing anything with it.  You seem
>>to be claiming that use of free() is now non-portable and was always
>>considered bad coding style.
>
>This was a trivial example trying to make a point.  I don't see any reason
>to clutter it with "busy code" to make it look real.  It is not too hard
>to imagine that something "conforming" was done between malloc() and free().

This appears to be a deliberate misinterpretation of my words.  I was NOT
complaining about the allocating of memory for no purpose.  I assumed that
something reasonable would be done between the malloc() and free().  My
complaint was that Mr Amspoker was making wild assertions about free() having
historically been considered bad coding style.

ajr

scott@bbxsda.UUCP (Scott Amspoker) (10/11/89)

In article <4813@tekcrl.LABS.TEK.COM> terryl@tekcrl.LABS.TEK.COM writes:
 +
 +As my original posting explained, the pointer *is* manipulated by
 +popping it off the stack upon return from free().  The original
 +posting explained every step that takes place leading up to the core
 +dump.  If you believe that any of those steps violates the ANSI draft
 +then please state which one.
 
      Um, err, how about the one that says C arguments are "call by value,
>and not by reference". If I remember correctly, your initial argument said
>that p could live in a special address register, then placed on the stack
>for the call to free(), and then popped off of the stack and put back in
>that special address register; that's call by value/result, not call by
>value, so the last step (put back in that special address register) should
>NOT be done at all.....

It is customary for subroutines (such as free()) to save registers
on the stack and restore their values upon return.  NOTHING IS
BEING PASSED BACK TO THE CALLER!  The subroutine is merely
restoring a register to its original value.  The value of the
pointer is being pushed as an argument to the subroutine.  That
value ultimately is discarded.  However, the register containing
the pointer may also be pushed to preserve its value.  Please check
some assembly code output of practically any C compiler of a function 
that uses register variables.


-- 
Scott Amspoker
Basis International, Albuquerque, NM
(505) 345-5232
unmvax.cs.unm.edu!bbx!bbxsda!scott

mwm@eris.berkeley.edu (Mike (I'll think of something yet) Meyer) (10/11/89)

In article <976@crdos1.crd.ge.COM> davidsen@crdos1.UUCP (bill davidsen) writes:
<  Prevailing practice means what it says, that before compilers were
<modified to make it illegal such code would work on most machines, such
<as Sun, Vax, PC, Cray, PDP-11, etc. I think that fairly represents at
<least 80% of the machines and users running pre-ANSI C.

I've as yet to run across a compiler that was modifed to match ANSI
that made code with undefined behavior illegal. Issue a warning, yes,
but not an error. I'm even used to them issuing only a warning if ANSI
says the situation in question should be an error.

So, can you provide a code sample with undefined behavior, and the
name of a compiler that used to accept such code, and now considers it
an error?

	<mike


--
When logic and proportion have fallen softly dead,	Mike Meyer
And the white knight is talking backwards,		mwm@berkeley.edu
And the red queen's off her head,			ucbvax!mwm
Remember what the dormouse said.			mwm@ucbjade.BITNET

gwyn@smoke.BRL.MIL (Doug Gwyn) (10/11/89)

In article <976@crdos1.crd.ge.COM> davidsen@crdos1.UUCP (bill davidsen) writes:
>	Ptr = &ShortVect[-30000];

There are so many implementation assumptions required to make this work
that it really cannot be considered sane portable programming practice.

>I submit that this is neither sloppy programming or
>meaningless, and that it is "not illegal" by K&R 1st Ed.

I disagree on both counts.

>  That's certainly keeping the discussion on a high technical plane.

Matters of surprise, unreliability, etc. are hardly very technical.

>  Please identify the machines in question and quantify the saving.

No.  I'll conduct my argument on my own terms, thank you very much.

>  Prevailing practice means what it says, that before compilers were
>modified to make it illegal such code would work on most machines, such
>as Sun, Vax, PC, Cray, PDP-11, etc. I think that fairly represents at
>least 80% of the machines and users running pre-ANSI C.

Nobody "modified compilers to make it illegal".  The Standard simply
makes it clearer than it may have previously been that you're asking
for trouble when you write such code.  On some implementations it will
work all the time, on others it will work some of the time, and on
others it will never work.  That's due to variations in computer
architecture, and to require that such dubious practice be made to
work in ALL cases as you seem to expect it to would burden some
implementations for insufficient reason.  (Plus it would be
exceedingly difficult to specify such behavior in the Standard; we
had a hard enough time getting the wording for last+1 right.)

cpcahil@virtech.UUCP (Conor P. Cahill) (10/11/89)

In article <238@bbxsda.UUCP>, scott@bbxsda.UUCP (Scott Amspoker) writes:
> It is customary for subroutines (such as free()) to save registers
> on the stack and restore their values upon return.  NOTHING IS
> BEING PASSED BACK TO THE CALLER!  The subroutine is merely
> restoring a register to its original value.  The value of the
> pointer is being pushed as an argument to the subroutine.  That
> value ultimately is discarded.  However, the register containing
> the pointer may also be pushed to preserve its value.  Please check
> some assembly code output of practically any C compiler of a function 
> that uses register variables.

Normally the subroutine only saves and restores register variables that
it uses within the subroutine.  The person writing the compiler and free()
for a system wherein free() gives the space back to the system and a program
would blow up if an invalid address is loaded into a register could do one
of the following:

	1. write free() so that it does not make use of any pointer 
	   registers that would be used for a "register char *p". so that
	   it would not have to save/restore said register.

	2. free() could examine the pointer registers and invalidate any 
	   of them that would now point to nonsense (before it makes them
	   point to nonsense.  This could be done automatically by the system,
	   since it knows what the adress vars are and what the users address
	   space is.

	3. disreguard the "register" on "register char *p"  or some sub-set
	   thereof (like disreguard it if the pointer is passed out of the
	   subroutine).

These are just three mechanisms that I thought of off the top of my head and
I ain't no compiler writer.  So I would conclude that your example would 
not be a problem (any compiler that had you're behavior would be useless, so
this discussion is really a waste of bandwidth).


-- 
+-----------------------------------------------------------------------+
| Conor P. Cahill     uunet!virtech!cpcahil      	703-430-9247	!
| Virtual Technologies Inc.,    P. O. Box 876,   Sterling, VA 22170     |
+-----------------------------------------------------------------------+

scott@bbxsda.UUCP (Scott Amspoker) (10/11/89)

In article <1989Oct10.163732.3241@jarvis.csri.toronto.edu> flaps@dgp.toronto.edu (Alan J Rosenthal) writes:
>                                                                     My
>complaint was that Mr Amspoker was making wild assertions about free() having
>historically been considered bad coding style.

Sorry if my point was not clear.  I was refering to the notion of handling
bad pointers - not the use of free().  The other thread I was refering to
*began* with a bad example of the use of free().  Then one thing led to
another.  The next thing I knew people were saying that any handling of
a bad pointer, whether it is dereferenced or not, is a *bug*.   I always
just considered it bad style.  Several readers attempted to justify
the notion by referencing various passages in the ANSI draft.  I merely used
those references in an absurd (but correct) way to show that things don't
always add up correctly.  Your indignation about my posting proves the
point I was trying to make - it *is* absurd.

Since free() is probably the only possible way for a *valid* pointer to
suddenly become *invalid* there are some interesting possibilities.
My example showed a case where a register variable contained a valid
pointer that became invalid by a call to free().  Even though the program
never uses the pointer again there is still the possibility of the pointer
being implicitly handled - if anything, by a *signal handler* which must
save and restore *all* registers.  It seems reasonable that an implementer
would have to take all of these things into account before making it
a capital offense to handle a bad pointer (i.e. core dump).  Arrangements
must be made to allow special bad pointers such as NULL and the various
values retuned by signal().

It strikes me as unlikely that such an implementation exists in this
particular manner and is probably not worth the effort.


-- 
Scott Amspoker
Basis International, Albuquerque, NM
(505) 345-5232
unmvax.cs.unm.edu!bbx!bbxsda!scott

afscian@violet.waterloo.edu (Anthony Scian) (10/11/89)

In article <238@bbxsda.UUCP> scott@bbxsda.UUCP (Scott Amspoker) writes:
>In article <4813@tekcrl.LABS.TEK.COM> terryl@tekcrl.LABS.TEK.COM writes:
> +
> +As my original posting explained, the pointer *is* manipulated by
> +popping it off the stack upon return from free().  The original
							  ^^^^^^^^
> +posting explained every step that takes place leading up to the core
> +dump.  If you believe that any of those steps violates the ANSI draft
> +then please state which one.
> 
>> [erroneous argument about call-by-reference]
>>that special address register; that's call by value/result, not call by
>>value, so the last step (put back in that special address register) should
>>NOT be done at all.....
>
>It is customary for subroutines (such as free()) to save registers
>on the stack and restore their values upon return.  NOTHING IS
>BEING PASSED BACK TO THE CALLER!  The subroutine is merely
>restoring a register to its original value.  The value of the
>pointer is being pushed as an argument to the subroutine.  That
>value ultimately is discarded.  However, the register containing
>the pointer may also be pushed to preserve its value.
Being the author of the posting in question, let me say that the example
was supposed to show that it was possible to core dump even if the
"freed" pointer was never explicitly referenced or dereferenced.
Unfortunately, there is an important distinction between a
system-dependent "free" and a pANSI "free". The example for
OS/2 was specific to a low-level system "free" not pANSI "free".
A pANSI compliant compiler cannot allow this to happen with
the reserved library routine "free".

SUMMARY:
If the calling convention allows trapping pointer registers (on load
of bad value) to be saved across calls, it is up to the compiler
run-time library to allow the pointer passed to the pANSI routine
"free" to be LOADable in an address register after the "free" PERIOD.
Though POPable would be the minimum requirement in theory.

Anthony
//// Anthony Scian afscian@violet.uwaterloo.ca afscian@violet.waterloo.edu ////
"I can't believe the news today, I can't close my eyes and make it go away" -U2

bruner@uicsrd.csrd.uiuc.edu (John Bruner) (10/12/89)

It is possible for a machine to push a register containing a bad
pointer onto the stack as part of a procedure call and STILL treat
the use of a bad pointer in user-written code as an error causing a
fault.  The former is an internal operation, while the latter is
user-specified.  To cite another example, presumably the operating
system will not incur a fatal error if it tries to store a bad
user pointer during a context switch.  A machine which performs
type checking in hardware may have some instructions which move
data around in an unchecked fashion, but for maximum runtime type
checking the compiler should generate pointer move instructions for
user-specified pointer operations rather than generic move
instructions.  In addition, is less likely that such a machine
would have an untyped compare than that it would have a simple
untyped move, load, or store.

The S-1 Project at the Lawrence Livermore National Laboratory built
two machines with various degrees of tagged data.  Pointers were not
the same as integers, and the hardware would detect mixing of data
types and cause faults.  One of the machines also provided hardware-
implemented segmentation, so that pointers had to lie within the
valid range of a segment or a trap would occur.

The pANS definitions for pointers make it possible to implement C
on machines that don't resemble the vanilla machines which dominate
the market today.  The warning that it isn't portable is just
that -- a warning.  The same could be said for programs that always
use "int" when they should use "long".  The VAX/Sun/MIPS/whatever
will let you do it, and your program will run on a lot of machines,
but it isn't portable if you store values which are too big or call
routines which expect long arguments (assuming no prototype is in
scope).
--
John Bruner	Center for Supercomputing R&D, University of Illinois
	bruner@uicsrd.csrd.uiuc.edu	(217) 244-4476	

dfp@cbnewsl.ATT.COM (david.f.prosser) (10/12/89)

In article <976@crdos1.crd.ge.COM> davidsen@crdos1.UUCP (bill davidsen) writes:
>  Consider: a program is going to read some LARGE number of values in
>the range 30000-30006. It wants to count the number of occurences of
>each value. Therefore:
>	long ShortVect[10] = {0,0,0,0,0,0,0,0,0,0}, *Ptr, Val;
>	
>	Ptr = &ShortVect[-30000];
>	while ((Val = MyRead()) > 0) Ptr[Val]++;
>
>  Note that by creating the pointer to an invalid address we now can add
>the value as a subscript. The address modified is *always* valid (if the
>input is in range), the vector doesn't need to be 30007 in length, and
>you don't have to subtract 30000 each time.

This relies on the "recovery" of the pointer value through pointer
arithmetic on the invalid value.  On architectures in which overflow
and underflow on pointers produce quiet wrap-around, you are correct
that this sort of approach will work.

>  I make no claim that this is the only way to do this, simple that
>there are programs around which actually do use this trick (I saw it in
>a net program). I submit that this is neither sloppy programming or
>meaningless, and that it is "not illegal" by K&R 1st Ed. It's a trick to
>save some space and CPU, something anyone on a small machine tries to do.

>|  I have no idea what you mean by "prevailing practice".  

>  Prevailing practice means what it says, that before compilers were
>modified to make it illegal such code would work on most machines, such
>as Sun, Vax, PC, Cray, PDP-11, etc. I think that fairly represents at
>least 80% of the machines and users running pre-ANSI C.

I claim that most (all?) of these can be caused to produce some sort
of "noise" on overflow and/or underflow, most likely raising a
computational exception signal.  While, by default, some (all?)
silently wrap, there has been no guarantee that this behavior must
occur.

As such, the prevailing practice does not guarantee that this approach
will behave as desired.

The X3J11 committee tried its best to mean it when it gave guarantees.
It is entirely due to this sort of issue that asynchronous signal
handling is so restrictive.

This doesn't mean that you shouldn't write code like the above, just
that you write it knowing what is required for an architecture to
guarantee this not-strictly-portable approach.

There are portability trade-offs made in virtually all C programs,
unfortunately many are simply not recognized as such by the programmer.

Dave Prosser	...not an official X3J11 answer...

mcdonald@aries.uiuc.edu (Doug McDonald) (10/12/89)

There are most certainly C implementations where previously valid pointers
can become invalid without the program specifically doing anything.
There is grave doubt that this example can be called a "hosted"
C implementation. Actually, pointers coming from casts of
ordinarily declared (static or automatic) variables, and from
"malloc" will not die without a call to "free". But the operating
system return pointers to other things (in the heap or within
the OS itself) that most certainly WILL become invalid without
notice during some future execution of OS code.

This is known as Microsoft Windows. The manual views using "malloc"
as closely related to child molestation. It claims you are supposed
to use the calls that result in dying pointers.

Doug McDonald

7103_300@uwovax.uwo.ca (10/12/89)

In article <238@bbxsda.UUCP>, scott@bbxsda.UUCP (Scott Amspoker) writes:
> It is customary for subroutines (such as free()) to save registers
> on the stack and restore their values upon return.  NOTHING IS
> BEING PASSED BACK TO THE CALLER!  The subroutine is merely
> restoring a register to its original value.  The value of the
> pointer is being pushed as an argument to the subroutine.  That
> value ultimately is discarded.  However, the register containing
> the pointer may also be pushed to preserve its value.  Please check
> some assembly code output of practically any C compiler of a function 
> that uses register variables.

This is an implementation issue, not a language issue. Obviously if
the architecture is such that popping that value off the stack will
cause a fault, then either free() will have to keep the address
valid or the value should not be popped. The user shouldn't be punished
if the compiler attempts to load an invalid address; but if the user
explicitly codes an invalid address calculation, then all bets are off.
--
Eric R. Smith
ersmith@uwovax.uwo.ca
ersmith@uwovax.bitnet

gwyn@smoke.BRL.MIL (Doug Gwyn) (10/12/89)

In article <240@bbxsda.UUCP> scott@bbxsda.UUCP (Scott Amspoker) writes:
>Since free() is probably the only possible way for a *valid* pointer to
>suddenly become *invalid* ...

Not at all.  In any block-structured language with pointers,
it is obvious how to have a valid pointer suddenly become invalid.

john@frog.UUCP (John Woods) (10/13/89)

In article <976@crdos1.crd.ge.COM>, davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) writes:
> |  The "benefit" is that faster, more natural C implementations are
> |  permitted on architectures where this is an issue.
>   Please identify the machines in question and quantify the saving.
>   Prevailing practice means what it says, that before compilers were
> modified to make it illegal such code would work on most machines, such
> as VAX, VAX, VAX, VAX, VAX, etc. I think that fairly represents every single
> machine and user running pre-ANSI C.

Well, I've adjusted those last 2 sentences a teeny bit, but the meaning is
exactly the same.  To review the conclusion of the comp.lang.c.flame
discussion:

main() {
	extern void paint_memory_blue(char *p, int len);
	char *p = malloc(1000);		/* OK. */
	paint_memory_blue(p, 1000);	/* it better accept NULL... */
	free(p);			/* OK */
	if (p == NULL)	/* Right here, a capability machine may fault.
			 * And it is the programmer's fault for writing this.
			 * If you don't know what a capability machine is,
			 * STOP ARGUING WHAT MACHINES WILL OR WON'T DO!!!
			 * In the words of Dennis M. Ritchie,
			 * "This is non-negotiable."
			 */
		;
}

Everybody clear now?
-- 
John Woods, Charles River Data Systems, Framingham MA 508-626-1101
...!decvax!frog!john, john@frog.UUCP, ...!mit-eddie!jfw, jfw@eddie.mit.edu

richard@aiai.ed.ac.uk (Richard Tobin) (10/13/89)

In article <11265@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:
>Not at all.  In any block-structured language with pointers,
>it is obvious how to have a valid pointer suddenly become invalid.

Doug is quite right as far as C goes - just take the address of an
automatic variable.  But it's not true for *any* block-structured 
language with pointers, because the language can prevent it.  Algol 68
is an example of such a language.  (REF != pointer flames >/dev/null)

-- Richard


-- 
Richard Tobin,                       JANET: R.Tobin@uk.ac.ed             
AI Applications Institute,           ARPA:  R.Tobin%uk.ac.ed@nsfnet-relay.ac.uk
Edinburgh University.                UUCP:  ...!ukc!ed.ac.uk!R.Tobin

meissner@dg-rtp.dg.com (Michael Meissner) (10/14/89)

In article <240@bbxsda.UUCP> scott@bbxsda.UUCP (Scott Amspoker) writes:

>  Since free() is probably the only possible way for a *valid* pointer to
>  suddenly become *invalid* there are some interesting possibilities.

Another possible way for a *valid* pointer to suddenly become
*invalid* is if the pointer points to automatic storage and you exit
the function creating the storage with either a return or a longjmp.
--

Michael Meissner, Data General.				If compiles where much
Uucp:		...!mcnc!rti!xyzzy!meissner		faster, when would we
Internet:	meissner@dg-rtp.DG.COM			have time for netnews?

scott@bbxsda.UUCP (Scott Amspoker) (10/16/89)

In article <MEISSNER.89Oct14124500@tiktok.rtp.dg.com> meissner@dg-rtp.dg.com (Michael Meissner) writes:
>In article <240@bbxsda.UUCP> scott@bbxsda.UUCP (Scott Amspoker) writes:
>
>>  Since free() is probably the only possible way for a *valid* pointer to
>>  suddenly become *invalid* there are some interesting possibilities.
>
>Another possible way for a *valid* pointer to suddenly become
>*invalid* is if the pointer points to automatic storage and you exit
>the function creating the storage with either a return or a longjmp.

I've read several of these comments since I've made that posting.  I
decided it really wasn't worth debating but it has happened too many
times.

I was refering to the possibility that free() could be returning the
freed memory back to the system with said block of memory being removed
from the process's memory map.  Attempting to dereference a pointer
to this block of memory would result in an actual trap and code dump.

While I fully agree that a pointer to "expired" stack data is to
be considered "invalid" I am not aware of any implementation that
actually removes stack memory from the memory map upon exit from
a procedure.  That would probably slow things down considerably.
Dereferencing such a pointer would be meaningless but would probably
not cause a trap (although such a program is certainly headed for a
trap :-)

-- 
Scott Amspoker
Basis International, Albuquerque, NM
(505) 345-5232
unmvax.cs.unm.edu!bbx!bbxsda!scott

gwyn@smoke.BRL.MIL (Doug Gwyn) (10/17/89)

In article <252@bbxsda.UUCP> scott@bbxsda.UUCP (Scott Amspoker) writes:
>While I fully agree that a pointer to "expired" stack data is to
>be considered "invalid" I am not aware of any implementation that
>actually removes stack memory from the memory map upon exit from
>a procedure.  That would probably slow things down considerably.
>Dereferencing such a pointer would be meaningless but would probably
>not cause a trap (although such a program is certainly headed for a
>trap :-)

Again, the question is NOT about dereferencing the pointer value,
but rather about accessing the now-invalid pointer value at all.

On the same general class of architectures for which it would be a
problem for free()d pointers, it could easily be a problem for
expired activation records (what you would probably call a "stack
frame", but in a true segmented architecture an activation record
is a separate system-controlled object in its own right, normally
reclaimed by the system immediately upon expiration of its
dynamic scope).  I don't know of any C compilers for the B[56]x00
family of computers, but of there are any I would expect them to
exhibit this behavior.

barmar@kulla (Barry Margolin) (10/17/89)

In article <252@bbxsda.UUCP> scott@bbxsda.UUCP (Scott Amspoker) writes:
>While I fully agree that a pointer to "expired" stack data is to
>be considered "invalid" I am not aware of any implementation that
>actually removes stack memory from the memory map upon exit from
>a procedure.

Multics truncates the stack segment to the page containing the current
frame whenever the process goes blocked.  It's a system call so you're
in the kernel already, and blocks usually wait for a long time (most
blocking is for keyboard input), so the expense is not very
significant.  The benefit is that the extra pages needn't be swapped
out, so the next page fault(s) will be faster, and less swap space is
used.
Barry Margolin, Thinking Machines Corp.

barmar@think.com
{uunet,harvard}!think!barmar