Topic: Referencing pointers after delete


Author: thp@cs.ucr.edu (Tom Payne)
Date: 1996/04/08
Raw View
David Vandevoorde (vandevod@cs.rpi.edu) wrote:
: >>>>> "TP" == Tom Payne <thp@cs.ucr.edu> writes:
: [...]
: TP> This is probably the hub of our difference: I have difficulty
: TP> imagining a reasonable architecture that does something other than
: TP> ignoring or trapping the reading of invalid pointers.  (Granted,
: TP> "reasonable" is in the eye of the beholder.)
:
: On the contrary, I think it could be a quite common thing. Many
: architectures memory-map I/O and it's perfectly imaginible that:
:
:  int x = *p;
:
: where p is an invalid pointer triggers some I/O operation because
: p happens to point into such an ``I/O area''. The standard cannot
: reasonably be expected to say what can or cannot happen in those
: cirucmstances.

Right!  Everyone agrees that dereferencing an invalid pointer should
yield undefined hehavior.

By "reading of invalid pointers", I mean the reading of a pointer
object at a time when it happens to contain an invalid value, as in:
     delete p;
     cout << int(p);
which yields "undefined behavior" under the current standards.
As I understand memory-mapped I/O, it is the reading of those
I/O locations themselves that produces the side effects, not
simply reading the value of a pointer to one.

Tom Payne (thp@cs.ucr.edu)
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: thp@cs.ucr.edu (Tom Payne)
Date: 1996/04/08
Raw View
Martin D Kealey (martin@kcbbs.gen.nz) wrote:
[...]
: *Sigh*  This is not supposed to be a complete description, just an
: example.  So ok, change it to invalidate all registers which
: contain pointers to the same type as p, not just the single
: register which contains the value of p.  Also, *all* registers are
: invalidated over (non-inline) function calls, or any other time
: we're not 100% sure what they might contain.
:
: I think now that accidental use of register aliasing is not
: possible -- one has explicitly to use p (or an alias for it) as a
: non-lvalue in the source code to cause this effect, and that means
: the program is non-conforming.

Agreed.  With a sufficiently conservative invalidation policy, the
implementation conforms.  So, would it continue to conform if a valid
program could read an invalid pointer (a pointer object at a time when
it happens to hold an invalid value)?  I think so.  The implementation
can freely assume that a pointer object holds a valid value from the
time it is assigned that value until it is invalidated, since, if the
assigned value was invalid, then the implementation is off the hook
via "undefined behavior."  It would make no difference whether that
invalid value was generated by reading a pointer to a deleted object
or though assigning the presumably invalid value (int*)1.

: I know this is a perverse implementation that does some pretty
: fantastic peephole optimising without bothering to do any dataflow
: analysis, but that alone doesn't currently make it non-conforming.

Right!  Besides, it shows that one could possibly lose opportunities
for optimization if it were permitted to assign invalid values to
a pointer object.

Tom Payne (thp@cs.ucr.edu)
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1996/04/01
Raw View
In article qvt@mulga.cs.mu.OZ.AU, fjh@mundook.cs.mu.OZ.AU (Fergus Henderson) writes:
>
>I don't think there is any disagreement about whether it IS undefined
>behaviour; the question is about whether it should be.  Remember, every
>instance in which the standard leaves the behaviour undefined is one
>more hurdle for programmers trying to write portable programs.  ...
>
>However, the argument that the behaviour should be undefined in order
>to support implementations which perform pointer validity checking in
>hardware is more convincing.

The language standard defines what a valid program is, and the meaning of
valid programs. A particularly difficult part is deciding what the required
response should be to an invalid program, or whether the standard should
try to specify a response.

Some things, like invalid pointers, are impractical or impossible to diagnose
until run time. (Impossible, even theoretically, for the general case.) Then
the decision becomes whether to require specific run-time behavior, or
constrain run-time behavior to a manageable set.

There isn't any way to predict in a platform-independent way what the
range of responses might be to using an invalid pointer, so the choices
boil down to requiring implementations in general to detect invalid
pointers at run time, or not requiring them to detect invalid pointers.

In my view, a requirement like "must raise a signal" as the response is
no better than requiring any other particular behavior. On some systems
raising a signal is the default hardware response and comes for free.
Other systems have no hardware detection or a different hardware
response, so run-time checks would be required to avoid unpredictable or
disallowed behavior. Loosening the requirement to "must raise a signal,
or do A or do B or do C" doesn't change the general case. What if some
platform, such as a system to be built years from now, normally reacts
by doing D?

"Undefined behavior" is the way the standard says, "We don't know a good
way to specify any set of behaviors for this invalid code, so we leave it
up to implementors to do something that satisfies their customers."

Put another way, "undefined behavior" is a hook that allows impelementors
to attach behavior that their customers want in handling potentially
invalid code. On a specific system, a particular response might be
expected which would be inappropriate on other systems.

---
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: thp@cs.ucr.edu (Tom Payne)
Date: 1996/04/01
Raw View
Steve Clamage (clamage@Eng.Sun.COM) wrote:
[...]
: There isn't any way to predict in a platform-independent way what the
: range of responses might be to using an invalid pointer, so the choices

The issue is whether one can specify an efficient, reasonable,
platform-independent response.  In general, it depends on the use.
For instance, Accessing the (pseudo)object obtained by dereferencing
an invalid pointer will produce such a range of possibly bizarre
results that there is visibly no way to specify.  But we are
considering the matter of reading a pointer to an deleted
object.

: boil down to requiring implementations in general to detect invalid
: pointers at run time, or not requiring them to detect invalid pointers.

I know of no acceptably efficient platform-independent mechanism for
detection.

: In my view, a requirement like "must raise a signal" as the response is
: no better than requiring any other particular behavior. On some systems

So, make the signal optional.  In any case, let processing continue
with the standard-specified contract between the running program and
the implementation in force at least until something serious happens,
like a derefercing of the invalid pointer.

: raising a signal is the default hardware response and comes for free.
: Other systems have no hardware detection or a different hardware
                                           ^^^^^^^^^^^^^^^^^^^^^^^^
: response, so run-time checks would be required to avoid unpredictable or
  ^^^^^^^^
This is probably the hub of our difference: I have difficulty
imagining a reasonable architecture that does something other than
ignoring or trapping the reading of invalid pointers.  (Granted,
"reasonable" is in the eye of the beholder.)

: disallowed behavior. Loosening the requirement to "must raise a signal,
: or do A or do B or do C" doesn't change the general case. What if some
: platform, such as a system to be built years from now, normally reacts
: by doing D?

There is no way of knowing how some furture architecture might respond
to any operation, not jut that of the reading of invalid pointers.

The argument that we need to open up all possible options in response
to the detection of an invalid value misses the point that a signal
handler, invoked upon detection of such a value, can generate whatever
behavior the programmer pleases including a call to abort.  (Note that
this puts the programmer, not the implementor in control of that
response, which is as things should be IMHO.)

The real problem, inherited from C, is that in the standard specifies
"undefined behavior" whenever an operation on a valid object might
yield an indeterminate or invalid value, e.g., reading a pointer
object to a deleted object or reading a global variable from a signal
handler.  The contract between the running program and the underlying
implementation is voided even if the program never uses that value.
The common rationale is that such a value can never be put to good use
(e.g., dereferencing the invalid pointer value will yield undefined
behavior anyway), but one might, for instance, cast an invalid pointer
to an integer and print it out for diagnostic purposes.  (In the case
of signal handlers, one can implement locks and signal blocking on the
basis of those indeterminate values.  How else do we handle such
things in assembly language?)

I would like the standard to specify that control stays with the
programmer, whenever it is reasonable to do so.  When the standard
says "undefined behavior" such control is lost.

Tom Payne (thp@cs.ucr.edu)
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: martin@kcbbs.gen.nz (Martin D Kealey)
Date: 1996/04/03
Raw View
It seems that I didn't quite make myself clear; therefore I've
copied this to std-c++ in case anyone else is similarly confused.

I wrote:

> > Consider this implementation ... where every object contains ...
> > task pointer

> > Now, if we run out of normal registers and it is necessary to
> > spill the "current task" register, its value can be reloaded
> > through any register pointing at an object (say because register-
> > indirect might be faster than absolute addressing). The compiler
> > writer knows this will only be done if a valid pointer value is
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> > already in a register (and calling delete will invalidate the
> > register), since it would be more economical to obtain the value
> > some other way if a pointer to it isn't available).

Tom Payne <thp@cs.UCR.edu> wrote:

> If that pointer is chosen statically, one can correctly anticipate
> which pointer it will be and concoct a valid program that deletes the
> pointer's referent prior to the restoration of current-task:
>
>         Widget* p = anticipated();    // Referent gets deleted
>         delete p;                     // via the alias p.
>         // Code to cause current-task to be spilled and restored.
                                                      ^^^^^^^^^^^^

This misses a critical point -- "Code to cause current-task to be
spilled and restored" must explicitly refer to "p" as a non-lvalue
for the value of p to be involved in the reload; see later for the
explanation.

> If, for instance, delete zeros out the deleted object, this valid
> program will crash and burn.  It seems to require run-time checking
> (and overhead) to avoid this.

Only if the risk of aliasing is undetectable at compile time; in
fact even a conservative approach can guarantee non-aliasing in
some cases -- but see later.

> The crashing and burning discussed above is not a result of the
> program reading an invalid pointer but of the implementation
> dereferencing one and using data from its deleted referent.

In a word, no.

I said explicitly that if the pointer was deleted then the
register would be invalidated so that it would NOT be used.

If the program never mentions p again, it won't crash (*) because
the value in the register has been invalidated; if it does use the
value of p, then it doesn't meet the standard's current
requirement.

(*  At least, it won't crash because of this potential problem; I
    can't speak for whatever other bugs the program might have.)

> How do you know that value is "valid"?  Perhaps, we've invalidated it
> by deleting its referent through another pointer to the same object?

*Sigh*  This is not supposed to be a complete description, just an
example.  So ok, change it to invalidate all registers which
contain pointers to the same type as p, not just the single
register which contains the value of p.  Also, *all* registers are
invalidated over (non-inline) function calls, or any other time
we're not 100% sure what they might contain.

I think now that accidental use of register aliasing is not
possible -- one has explicitly to use p (or an alias for it) as a
non-lvalue in the source code to cause this effect, and that means
the program is non-conforming.

I know this is a perverse implementation that does some pretty
fantastic peephole optimising without bothering to do any dataflow
analysis, but that alone doesn't currently make it non-conforming.

- Martin.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: vandevod@cs.rpi.edu (David Vandevoorde)
Date: 1996/04/04
Raw View
>>>>> "TP" == Tom Payne <thp@cs.ucr.edu> writes:
[...]
TP> This is probably the hub of our difference: I have difficulty
TP> imagining a reasonable architecture that does something other than
TP> ignoring or trapping the reading of invalid pointers.  (Granted,
TP> "reasonable" is in the eye of the beholder.)

On the contrary, I think it could be a quite common thing. Many
architectures memory-map I/O and it's perfectly imaginible that:

 int x = *p;

where p is an invalid pointer triggers some I/O operation because
p happens to point into such an ``I/O area''. The standard cannot
reasonably be expected to say what can or cannot happen in those
cirucmstances.

 Daveed
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
Date: 1996/04/01
Raw View
martin@kcbbs.gen.nz (Martin D Kealey) writes:

[example implementation which can crash if a pointer is referenced
after the memory it points to has been deleted]

>Is there anything in such an implementation that is unconforming?
>If not, then I submit that "undefined behaviour" IS the correct
>term to apply to *any* use of an invalidated pointer value.

I don't think there is any disagreement about whether it IS undefined
behaviour; the question is about whether it should be.  Remember, every
instance in which the standard leaves the behaviour undefined is one
more hurdle for programmers trying to write portable programs.  I think
the possible efficiency gains from allowing optimizations such as the
one you described are likely to be very minimal even on those very rare
systems on which they might apply.  If that were the only reason, I
would think that it ought not be undefined behaviour.

However, the argument that the behaviour should be undefined in order
to support implementations which perform pointer validity checking in
hardware is more convincing.

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: thp@cs.ucr.edu (Tom Payne)
Date: 1996/03/22
Raw View
Steve Clamage (clamage@Eng.Sun.COM) wrote:
: In article ft6@galaxy.ucr.edu, thp@cs.ucr.edu (Tom Payne) writes:
: >....  It seems that to accomodate
: >certain hardware-based validity checkers for pointers, the standard
: >has precluded the possibility of portable software-based validity
: >checking.  IMHO an unfortunate decision.
:
: Even if the value of a pointer after its referent was deleted were
: required to be valid, it is still not possible to write a portable pointer
: validity checker for C or C++. Example, assuming that it is always safe
: to read the value of any pointer:
:
:  void foo(int* p) {
:   // is p valid?
:  }
: How are you going to determine portably whether p is valid in foo?

By passing it to a checking routine, of course. ;-)  Given the curent
standard, a call to such a routine can elicit undefined behavior in an
otherwise portable program:

   void bar(int*& p) {
      checkOut(p);  // p is passed by value to checkOut.
      p = 0;
   }

I don't mean to imply that one can portably detect *all* invalid
pointers, but with help from the compiler software-based solutions
can give coverage that is as complete as those based on hardware.
In either case, it is not particularly helpful that the behavior
becomes undefined the moment an invalid value is detected.

Tom Payne (thp@cs.ucr.edu)
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: thp@cs.ucr.edu (Tom Payne)
Date: 1996/03/22
Raw View
Steve Clamage (clamage@Eng.Sun.COM) wrote:
: In article ceo@engnews1.Eng.Sun.COM, "joe (j.) halpin" <jhalpin@bnr.ca>
: writes:
: >
: >In 3.7.3.2.4 the January working paper says:
: >
: >4 A deallocation function can free the storage referenced by the pointer
: >  given  as  its  argument and renders the pointer invalid.  The storage
: >  can be made available for further allocation.  An invalid pointer con-
: >  tains an unusable value:  it cannot even be used in an expression.
[...]
: The reason for the odd-looking condition (which follows the C
: standard) is to allow architectures which validate pointers in
: hardware to be standard-conforming. Example:
[...]
: You can't do anything useful in portable code with uninitialized pointers,
: or pointers which point to objects which are no longer available. The
: standard allows the implementation a great deal of leeway in trying to
: help by identifying such invalid uses.

... just as for arithmetic exceptions, which also engender "undefined
behavior."  But why the leeway to, say, reformat the disk, rather
than specifying the normal practice, which is to invoke a signal
handler, thereby, keeping the behavior defined?

Tom Payne (thp@cs.ucr.edu)


[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1996/03/23
Raw View
In article 5vh@galaxy.ucr.edu, thp@cs.ucr.edu (Tom Payne) writes:
>Steve Clamage (clamage@Eng.Sun.COM) wrote:
>: You can't do anything useful in portable code with uninitialized pointers,
>: or pointers which point to objects which are no longer available. The
>: standard allows the implementation a great deal of leeway in trying to
>: help by identifying such invalid uses.
>
>.... just as for arithmetic exceptions, which also engender "undefined
>behavior."  But why the leeway to, say, reformat the disk, rather
>than specifying the normal practice, which is to invoke a signal
>handler, thereby, keeping the behavior defined?

Why should raising a signal be "normal practice"? Why not throwing an
exception? What about time-critical programs which would rather risk an
invalid pointer than pay the penalty of checking every pointer for validity?

If you specify the set of behaviors that are allowed when using an invalid
pointer, every pointer must be checked except those that can be proved at
compile time to be valid. If the runtime code does not check all the pointers,
there isn't any way to predict what will happen if you use an invalid  one.
(In general, that is. On specific platforms it might be possible, and the
degree to which it is specified is deemed a "quality-of-implementation"
issue.)

I don't know how to write a specification that says, "The implementation
doesn't have to check pointers, but invalid pointers aren't allowed to do
anything bad." I also don't know how to test an implementation for
conformance to such a rule.

The philosophical question here is how much validity checking should be
assured by the programming language. Should the implementation be required to
check for invalid pointers, arithmetic overflow, underflow, loss of precision?

Some languages have such requirements. Some C and C++ implementations do
indeed provide such checking. (The C and C++ definitions allow but do
not require those checks.) My observation is that such programming
languages and C/C++ implementations are not wildly popular. Maybe they
should be, but they are not. That seems to validate the decision to leave
those issues up to the implementation and its customers, in the sense that
many C and C++ programmers don't seem to care much about those things.
---
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1996/03/23
Raw View
In article q3@galaxy.ucr.edu, thp@cs.ucr.edu (Tom Payne) writes:
>Steve Clamage (clamage@Eng.Sun.COM) wrote:
>
>: Even if the value of a pointer after its referent was deleted were
>: required to be valid, it is still not possible to write a portable pointer
>: validity checker for C or C++. Example, assuming that it is always safe
>: to read the value of any pointer:
>:
>:  void foo(int* p) {
>:   // is p valid?
>:  }
>: How are you going to determine portably whether p is valid in foo?
>
>By passing it to a checking routine, of course. ;-)  Given the curent
>standard, a call to such a routine can elicit undefined behavior in an
>otherwise portable program:
>
>   void bar(int*& p) {
>      checkOut(p);  // p is passed by value to checkOut.
>      p = 0;
>   }
>
>I don't mean to imply that one can portably detect *all* invalid
>pointers, but with help from the compiler software-based solutions
>can give coverage that is as complete as those based on hardware.
>In either case, it is not particularly helpful that the behavior
>becomes undefined the moment an invalid value is detected.

"Undefined" doesn't mean "halt and catch fire". (Don't you find annoying
the Star Trek convention, from Classic through Voyager, that any trouble
on board results in the computers bursting into flame?) "Undefined" means
portable programs are given no assurances about behavior.

An implementation can provide all the checking it wants, and can provide
hooks to allow others to do the checking. Documentation may provide clever
third parties the information they need to make add-on checkers. Some
ISV's are making quite a nice living doing exactly that.

Pointer validation isn't going to be portable, as I pointed out before.
I don't see any advantage in trying to restrict the kind or form of checking
that systems are allowed to implement. I would applaud a hardware-based
mechanism, and I would resent a programming language standard that made
it non-conforming.

---
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: johnb@pivotal-dm.ccmail.compuserve.com (John Bain)
Date: 1996/03/23
Raw View
"joe (j.) halpin" <jhalpin@bnr.ca> wrote:

>In 3.7.3.2.4 the January working paper says:
>
>4 A deallocation function can free the storage referenced by the pointer
>  given  as  its  argument and renders the pointer invalid.  The storage
>  can be made available for further allocation.  An invalid pointer con-
>  tains an unusable value:  it cannot even be used in an expression.

I interpret this as meaning the _value_ of the pointer cannot be used in
an expression.

>
>This sounds as though, in the following:
>
>char *pc = new char[128];
>delete pc;
    ^^^^
delete[] pc;

>pc = 0;
>
>it makes the final assignment (an expression) invalid.

The value of the pointer is not used in the expression, so it's OK.

>In fact, it sounds like it also rules out things like 'if(pc == 0)
>...' after the above fragment.

After the final assignment, the pointer's value is no longer invalid, so
it can be used (though obviously not dereferenced while NULL).

By my interpretation, (pc == 0) would, however, be invalid after the
delete but before the assignment of 0.

Cheers,

John
-----------------------------------------------------------------
John Bain
johnb@pivotal-dm.ccmail.compuserve.com
-----------------------------------------------------------------



[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: thp@cs.ucr.edu (Tom Payne)
Date: 1996/03/25
Raw View
Steve Clamage (clamage@Eng.Sun.COM) wrote:
: In article q3@galaxy.ucr.edu, thp@cs.ucr.edu (Tom Payne) writes:
[...]
: >I don't mean to imply that one can portably detect *all* invalid
: >pointers, but with help from the compiler software-based solutions
: >can give coverage that is as complete as those based on hardware.
: >In either case, it is not particularly helpful that the behavior
: >becomes undefined the moment an invalid value is detected.
[...]
: Pointer validation isn't going to be portable, as I pointed out before.

The issue is not so much the portability of the checking but the
portability of programs that want to invoke it without voiding their
defined-behavior contract with the underlying implemention.

: I don't see any advantage in trying to restrict the kind or form of checking
: that systems are allowed to implement. I would applaud a hardware-based
: mechanism, and I would resent a programming language standard that made
: it non-conforming.

The issue is not the form of the checking but the response to it.  The
standard response to a hardware-detected fault is to invoke a signal
handler, which is (or should be) completely conforming.

Tom Payne (thp@cs.ucr.edu)



[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: thp@cs.ucr.edu (Tom Payne)
Date: 1996/03/26
Raw View
Steve Clamage (clamage@Eng.Sun.COM) wrote:
: In article 5vh@galaxy.ucr.edu, thp@cs.ucr.edu (Tom Payne) writes:
: >Steve Clamage (clamage@Eng.Sun.COM) wrote:
: >: You can't do anything useful in portable code with uninitialized
: >: pointers, or pointers which point to objects which are no longer
: >: available. The standard allows the implementation a great deal of
: >: leeway in trying to help by identifying such invalid uses.
: >
: >.... just as for arithmetic exceptions, which also engender "undefined
: >behavior."  But why the leeway to, say, reformat the disk, rather
: >than specifying the normal practice, which is to invoke a signal
: >handler, thereby, keeping the behavior defined?
:
: Why should raising a signal be "normal practice"? Why not throwing an
: exception? What about time-critical programs which would rather risk an

Throwing an exception would be incompatible (or of marginal compatibility)
with C.  ( Too bad signal handlers can't throw exceptions ;-) )

: invalid pointer than pay the penalty of checking every pointer for validity?

Right!  That signal should be optional, not mandatory.  But, perhaps,
the question of whether and when to issue a signal is viewed as a
quality-of-implementatino issue.  If so, then the implementation
already has all the leeway it needs without opening the door to
"undefined behavior."

Tom Payne (thp@cs.ucr.edu)
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1996/03/21
Raw View
In article ft6@galaxy.ucr.edu, thp@cs.ucr.edu (Tom Payne) writes:
>....  It seems that to accomodate
>certain hardware-based validity checkers for pointers, the standard
>has precluded the possibility of portable software-based validity
>checking.  IMHO an unfortunate decision.

Even if the value of a pointer after its referent was deleted were
required to be valid, it is still not possible to write a portable pointer
validity checker for C or C++. Example, assuming that it is always safe
to read the value of any pointer:

 void foo(int* p) {
  // is p valid?
 }
How are you going to determine portably whether p is valid in foo?

I might call foo like this, for example:
 int f2() {
  struct two { int a, b; } ray[10];
  foo(&ray[5].b);
 }
The value of p in foo is valid, even though it does not correspond to any
heap object or the start of any entire object. Furthermore, suppose in foo
I save the value of p in a static variable and use it again in another
invocation of foo. Is it still valid? If the current address of p
is the same as the saved value from a previous invocation, does it refer
to the same object?

These questions can be answered in system-specific ways, but there is
no portable method. A saved address might or might not still be valid.
Addresses that compare equal might refer to the same or to different
objects.
---
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: kuehl@uzwil.informatik.uni-konstanz.de (Dietmar Kuehl)
Date: 1996/03/21
Raw View
Hi,

joe (j.) halpin (jhalpin@bnr.ca) wrote:
: In 3.7.3.2.4 the January working paper says:

: 4 A deallocation function can free the storage referenced by the pointer
:   given  as  its  argument and renders the pointer invalid.  The storage
:   can be made available for further allocation.  An invalid pointer con-
:   tains an unusable value:  it cannot even be used in an expression.

: This sounds as though, in the following:

: char *pc = new char[128];
: delete pc;
: pc = 0;

: it makes the final assignment (an expression) invalid.

No. You cannot use the value of the pointer but you can use the pointer
(actually, you get undefined behavior in the code above because you
delete an array object with 'delete' instead of 'delete[]'; I assume
you took the correct form). Code like this is non-portable:

  char *start = strcpy(new char[10], "foobar");
  char *found = strchr(start, 'o');
  delete[] start;
  if (found == start)
    ...

: Am I misunderstanding something, or is it illegal to zero out pointers
: after they've been deallocated? I'm assuming that the intent was to
: disallow dereferencing of pointers that have been handed to
: delete. The wording seems to disallow the above as well.

The wording is a little bit stronger than just disallowing
dereferencing: There is no portable use of the value of the pointer
after deleting the object. I.e. you cannot use the value to calculate
an pointer to another object and you cannot use the value to compare it
with another pointer.

: In fact, it sounds like it also rules out things like 'if(pc == 0)
: ...' after the above fragment.

No, it doesn't: If you apply 'delete' to a NULL pointer, nothing
happens.
--
dietmar.kuehl@uni-konstanz.de
http://www.informatik.uni-konstanz.de/~kuehl
I am a realistic optimist - that's why I appear to be slightly pessimistic
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: "joe (j.) halpin" <jhalpin@bnr.ca>
Date: 1996/03/21
Raw View
To Moderator: This may be a duplicate, my newsreader software was having
trouble sending this, so I'm mailing it as well.

In 3.7.3.2.4 the January working paper says:

4 A deallocation function can free the storage referenced by the pointer
  given  as  its  argument and renders the pointer invalid.  The storage
  can be made available for further allocation.  An invalid pointer con-
  tains an unusable value:  it cannot even be used in an expression.

This sounds as though, in the following:

char *pc = new char[128];
delete pc;
pc = 0;

it makes the final assignment (an expression) invalid.

Am I misunderstanding something, or is it illegal to zero out pointers
after they've been deallocated? I'm assuming that the intent was to
disallow dereferencing of pointers that have been handed to
delete. The wording seems to disallow the above as well.

In fact, it sounds like it also rules out things like 'if(pc == 0)
...' after the above fragment.

Joe


[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: thp@cs.ucr.edu (Tom Payne)
Date: 1996/03/21
Raw View
joe (j.) halpin (jhalpin@bnr.ca) wrote:
: To Moderator: This may be a duplicate, my newsreader software was having
: trouble sending this, so I'm mailing it as well.
:
: In 3.7.3.2.4 the January working paper says:
:
: 4 A deallocation function can free the storage referenced by the pointer
:   given  as  its  argument and renders the pointer invalid.  The storage
:   can be made available for further allocation.  An invalid pointer con-
:   tains an unusable value:  it cannot even be used in an expression.
[...]
: Am I misunderstanding something, or is it illegal to zero out pointers
: after they've been deallocated? I'm assuming that the intent was to
: disallow dereferencing of pointers that have been handed to
: delete. The wording seems to disallow the above as well.
:
: In fact, it sounds like it also rules out things like 'if(pc == 0)
: ...' after the above fragment.

Apparently there are architectures (80386?) that perform such validity
checks on read access to pointer registers and trap on certain bogus
values.  So, the standard deems that after deallocation a pointer
object might contain a value that is so bogus that even reading it
will provoke "undefined behavior" and assigning the pointer a valid
value is the only valid operation.  It seems that to accomodate
certain hardware-based validity checkers for pointers, the standard
has precluded the possibility of portable software-based validity
checking.  IMHO an unfortunate decision.

Tom Payne (thp@cs.ucr.edu)
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: "joe (j.) halpin" <jhalpin@bnr.ca>
Date: 1996/03/22
Raw View
In article <4isca2$t20@news.BelWue.DE>,
Dietmar Kuehl <dietmar.kuehl@uni-konstanz.de> wrote:
>Hi,
>
>joe (j.) halpin (jhalpin@bnr.ca) wrote:
>: In 3.7.3.2.4 the January working paper says:
>
>: 4 A deallocation function can free the storage referenced by the pointer
>:   given  as  its  argument and renders the pointer invalid.  The storage
>:   can be made available for further allocation.  An invalid pointer con-
>:   tains an unusable value:  it cannot even be used in an expression.
>
>: This sounds as though, in the following:
>
>: char *pc = new char[128];
>: delete pc;
>: pc = 0;
>
>: it makes the final assignment (an expression) invalid.
>
>No. You cannot use the value of the pointer but you can use the pointer

So, in the sentence above - "An invalid pointer contains an unusable
value:  it cannot even be used in an expression." - the word 'it'
refers to the value and not the pointer? That makes sense, although
the phrasing is a bit ambiguous.

>(actually, you get undefined behavior in the code above because you
>delete an array object with 'delete' instead of 'delete[]'; I assume

Mea culpa.

[...]

Thanks

Joe
--
Joe Halpin                             jhalpin@nortel.com
Nortel Wireless                        (214) 684-5657
Richardson, TX  75083-3871             <standard disclaimer applies>
-------------------------------------------------------------------------------

---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1996/03/22
Raw View
In article ceo@engnews1.Eng.Sun.COM, "joe (j.) halpin" <jhalpin@bnr.ca>
writes:
>
>In 3.7.3.2.4 the January working paper says:
>
>4 A deallocation function can free the storage referenced by the pointer
>  given  as  its  argument and renders the pointer invalid.  The storage
>  can be made available for further allocation.  An invalid pointer con-
>  tains an unusable value:  it cannot even be used in an expression.
>
>This sounds as though, in the following:
>
>char *pc = new char[128];
>delete pc;
>pc = 0;
>
>it makes the final assignment (an expression) invalid.

No. It is not the pointer object that becomes unusable, but the value
of the pointer. You can always store a valid value (like a null pointer)
into the object.

The reason for the odd-looking condition (which follows the C
standard) is to allow architectures which validate pointers in
hardware to be standard-conforming. Example:

Suppose a "tagged" architecture marks each pointer as to whether it
contains a valid value.
 char *p; // p initially tagged as invalid value
 p = new char[100]; // p tagged as valid
 delete [] p; // p tagged as invalid
 p = 0; // p tagged as valid null pointer
The hardware might trap any use of an invalid pointer value, even just
reading the value without dereferencing it.

You can't do anything useful in portable code with uninitialized pointers,
or pointers which point to objects which are no longer available. The
standard allows the implementation a great deal of leeway in trying to
help by identifying such invalid uses.

---
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]