Topic: When bad things happen to good delete


Author: fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/03/21
Raw View
"Bill Wade" <bill.wade@stoner.com> writes:

 >struct link
 >{
 >  ~link();
 >  static void DeleteAll();
 >  link* next;
 >  static link* all_links;
 >  ...
 >};
 >
 >link::~link()
 >{
 >  if(this == all_links)
 >    all_links = next;
 >  ...
 >}
 >
 >void link::DeleteAll()
 >{
 >  while(all_links)
 >    delete all_links;
 >}
 >
 >Is DeleteAll conforming?

Yes, it conforms to the current draft.

 >Your statements lead me to believe that if the operand of delete is an
 >lvalue, the implementation is allowed to have the built-in delete
 >implemented as
 >        p->~T();
 >        free(p);
 >        p = (T*) 0xdeadbeef;
 >which would make DeleteAll() incorrect.

A implementation could only do that if it was sure that `p->~T()'
did not modify `p'.

If the implementation wasn't sure whether `p->~T()' would modify `p',
it could implement `delete p' as

 T* tmp = p;
        tmp->~T();
 if (p == tmp) p = (T*) 0xdeadbeef;
        free(tmp);

--
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: fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/03/21
Raw View
Stephen.Clamage@eng.sun.com (Steve Clamage) writes:

>"Bill Wade" <bill.wade@stoner.com> writes:
> >struct link
> >{
> >  ~link();
> >  static void DeleteAll();
> >  link* next;
> >  static link* all_links;
> >  ...
> >};
> >
> >link::~link()
> >{
> >  if(this == all_links)
> >    all_links = next;
> >  ...
> >}
> >
> >void link::DeleteAll()
> >{
> >  while(all_links)
> >    delete all_links;
> >}
>
>Yikes! I wouldn't expect this code to work, and it doesn't even have
>the virtue of being clear to the reader.

I agree that this code is poor style.  However, according to the current
draft, its behaviour is well-defined, isn't it?  As a practical matter,
I wouldn't want to rely on code like that working, but according the
current draft, it ought to work, don't you agree?

--
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
                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: Stephen.Clamage@eng.sun.com (Steve Clamage)
Date: 1997/03/21
Raw View
In article 2864370a@janeway, "Bill Wade" <bill.wade@stoner.com> writes:
 >Steve Clamage <Stephen.Clamage@eng.sun.com> wrote in article
 ><199703171812.KAA06060@taumet.eng.sun.com>...
 >>
 >> Thus, a program ought not attempt to read the value of a pointer
 >> after its referent has been deleted (or freed). On an architecture
 >> such as I described above, using memcpy as a type-punning mechanism
 >> might not work in any useful way, and I don't think any type-
 >> punning mechanism is ever guaranteed to work when not specifically
 >> assured by the standard to be valid.
 >
 >I'm still not sure I understand if 'pointer' in the above discussion refers
 >to a pointer variable or a pointer value.

Assume a pointer variable p points to a valid object o. The value of
p is the address of o. When the storage for o is deleted or freed
(not necessarily via p, but possibly via some other pointer to o), the
value of p becomes indeterminate. You are not assured that you can
even read the value of p without causing a program abort, or that the
value of p has any useful meaning. On typical implementations, you can
read p and draw conclusions about its value, but that doesn't mean such
code is valid or portable.


 >struct link
 >{
 >  ~link();
 >  static void DeleteAll();
 >  link* next;
 >  static link* all_links;
 >  ...
 >};
 >
 >link::~link()
 >{
 >  if(this == all_links)
 >    all_links = next;
 >  ...
 >}
 >
 >void link::DeleteAll()
 >{
 >  while(all_links)
 >    delete all_links;
 >}

Yikes! I wouldn't expect this code to work, and it doesn't even have
the virtue of being clear to the reader. Why not write the usual
code that walks the list, deleting as it goes? It takes an auxiliary
variable and two extra lines of code, but it has the virtue of being
clear, and I doubt you could measure the difference in performance.

---
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: "Bill Wade" <bill.wade@stoner.com>
Date: 1997/03/20
Raw View
Steve Clamage <Stephen.Clamage@eng.sun.com> wrote in article
<199703171812.KAA06060@taumet.eng.sun.com>...
>
> Thus, a program ought not attempt to read the value of a pointer
> after its referent has been deleted (or freed). On an architecture
> such as I described above, using memcpy as a type-punning mechanism
> might not work in any useful way, and I don't think any type-
> punning mechanism is ever guaranteed to work when not specifically
> assured by the standard to be valid.

I'm still not sure I understand if 'pointer' in the above discussion refers
to a pointer variable or a pointer value.

struct link
{
  ~link();
  static void DeleteAll();
  link* next;
  static link* all_links;
  ...
};

link::~link()
{
  if(this == all_links)
    all_links = next;
  ...
}

void link::DeleteAll()
{
  while(all_links)
    delete all_links;
}

Is DeleteAll conforming?  It doesn't use any deleted pointer value, however
it does use a pointer variable which was a delete argument.  However it
"knows" that the object's destructor left the pointer variable with a new
valid value.

Your statements lead me to believe that if the operand of delete is an
lvalue, the implementation is allowed to have the built-in delete
implemented as
        p->~T();
        free(p);
        p = (T*) 0xdeadbeef;
which would make DeleteAll() incorrect.

If DeleteAll() as shown above is non-conforming, does the following change
make it conforming?
    delete (all_links+0);
In this case the argument to delete is clearly not an lvalue.
---
[ 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: Stephen.Clamage@eng.sun.com (Steve Clamage)
Date: 1997/03/18
Raw View
In article fsf@vx.cit.alcatel.fr, James Kanze <james-albert.kanze@vx.cit.alcatel.fr> writes:
>
>  What I think Fergus is saying is that:
>
>1. An implementation can do anything it pleases, as long as what it does
>doesn't change the visible behavior of a conforming program.  And it can
>do anything it pleases, period, with a non-conforming program.
>
>2. A conforming program cannot access the pointer freed by delete in any
>way what so ever.  So if the implementation has trashed it, it cannot
>tell.
>
>3. A program which does access the pointer is not conforming, so the
>implementation can trash the pointer if it likes.
>
>I'm not 100% sure that Fergus is right.  I think that you can read a
>deleted pointer with memcpy, so a conforming program could detect that
>the implementation had trashed the pointer with: ...

The "value is indeterminate" after a delete is borrowed from the C
standard. The idea is to allow an implementation to detect use of
invalid pointer values. For example, a tagged architecture might
mark a pointer "invalid" when its referent was deleted, or perhaps
the act of reading a pointer value (tagged as  "pointer") would
cause an automatic hardware check of its value. Either way, the
hardware could generate a trap, and the intent is for the standard
to allow such an implemenation to be conforming.

Thus, a program ought not attempt to read the value of a pointer
after its referent has been deleted (or freed). On an architecture
such as I described above, using memcpy as a type-punning mechanism
might not work in any useful way, and I don't think any type-
punning mechanism is ever guaranteed to work when not specifically
assured by the standard to be valid.

Example: In the i86 "large model", two pointers which compare equal
as pointers need not compare equal as sequences of bytes. The
"huge model" freely alters bit patterns of pointers when needed to
achieve a normalized representation. (The pointer's "value" does not
change, but type punning may give unexpected results.)

---
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                             ]