Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Pointer use after delete

91 views
Skip to first unread message

mark

unread,
Sep 1, 2015, 6:00:34 AM9/1/15
to
In the paper 'Preventing Use-after-free with Dangling Pointers
Nullification', there is an example of Chromium using a pointer value
after delete:

[...]
delete doc->child;
allChilds[doc->child]=DELETED;
[...]

Is this undefined behavior?

Per C++11 standard:

3.7.4.2 Deallocation functions
<<<
If the argument given to a deallocation function in the standard library
is a pointer that is not the null pointer value (4.10), the deallocation
function shall deallocate the storage referenced by the pointer,
rendering invalid all pointers referring to any part of the deallocated
storage. The effect of using an invalid pointer value (including passing
it to a deallocation function) is undefined.
>>>

Does this imply that the example above has UB? This section is unclear
to me. The "clarification" "including passing it to a deallocation
function" doesn't help, since it implies a different kind of usage as
opposed to just using the pointer as numeric value.

Barry Schwarz

unread,
Sep 1, 2015, 8:34:51 AM9/1/15
to
On Tue, 1 Sep 2015 12:00:16 +0200, mark <ma...@invalid.invalid> wrote:

>In the paper 'Preventing Use-after-free with Dangling Pointers
>Nullification', there is an example of Chromium using a pointer value
>after delete:
>
>[...]
>delete doc->child;

This implies that doc->child is a pointer (call it T*).

>allChilds[doc->child]=DELETED;

How can a pointer be used as the subscript of an array (or vector)? I
guess if allChilds were of type map<T*, int> and DELETED was an
expression compatible with int, this might make syntactic sense.

>[...]
>
>Is this undefined behavior?

Yes. Any attempt to evaluate a pointer after the memory it points to
has been deallocated is UB. It is not limited to dynamic allocation
either. Consider
int* foo()
{int i = 5;
return &i;}
int main()
{int *p;
int i;
p = foo();
i = *p; // UB-1
cout << p; //also UB-1
}
UB-1: You cannot dereference a pointer when you no longer own the
memory it used to point to.
UB-2: While not that common anymore, some hardware systems still
"validate" an address even if you are not accessing the memory at that
address.

>Per C++11 standard:
>
>3.7.4.2 Deallocation functions
><<<
>If the argument given to a deallocation function in the standard library
>is a pointer that is not the null pointer value (4.10), the deallocation
>function shall deallocate the storage referenced by the pointer,
>rendering invalid all pointers referring to any part of the deallocated
>storage. The effect of using an invalid pointer value (including passing
>it to a deallocation function) is undefined.
> >>>
>
>Does this imply that the example above has UB? This section is unclear
>to me. The "clarification" "including passing it to a deallocation
>function" doesn't help, since it implies a different kind of usage as
>opposed to just using the pointer as numeric value.

I don't know why they added the parenthetical note. It adds nothing
to the sentence. It's like saying "Any attempt to perform arithmetic
(including addition) is ...". Any use of an invalid value is
undefined. Note that the pointer does not become invalid until the
function actually deallocates the storage. So
int *p = new int;
delete p; //OK
delete p; //UB

--
Remove del for email

Ralf Goertz

unread,
Sep 1, 2015, 8:57:23 AM9/1/15
to
Am Tue, 01 Sep 2015 05:34:33 -0700
schrieb Barry Schwarz <schw...@dqel.com>:

> On Tue, 1 Sep 2015 12:00:16 +0200, mark <ma...@invalid.invalid> wrote:
>
> >
> > delete doc->child;
>
> > allChilds[doc->child]=DELETED;
>
> > Is this undefined behavior?
>
> Yes. Any attempt to evaluate a pointer after the memory it points to
> has been deallocated is UB. It is not limited to dynamic allocation
> either.

> UB-2: While not that common anymore, some hardware systems still
> "validate" an address even if you are not accessing the memory at that
> address.

Really? I would have guessed there is no problem. Why should there be? A
pointer is nothing but a number. If this number is used as key in a map
why would any hardware try to validate the accessability of the memory
that happens to be situated at that address if not being asked to? If I
move to a new home and someone tries to look up my number in a phone
book the guy who moved into my previous home is not being bothered by
that. Only if that someone actually visits…


Scott Lurndal

unread,
Sep 1, 2015, 9:26:48 AM9/1/15
to
Ralf Goertz <m...@myprovider.invalid> writes:
>Am Tue, 01 Sep 2015 05:34:33 -0700

>> UB-2: While not that common anymore, some hardware systems still
>> "validate" an address even if you are not accessing the memory at that
>> address. =20
>
>Really? I would have guessed there is no problem. Why should there be? A
>pointer is nothing but a number. If this number is used as key in a map
>why would any hardware try to validate the accessability of the memory
>that happens to be situated at that address if not being asked to? If I
>move to a new home and someone tries to look up my number in a phone
>book the guy who moved into my previous home is not being bothered by
>that. Only if that someone actually visits=E2=80=A6

A pointer is many things, depending on the hardware architecture.

It could be a linear address (intel x86 in long mode), it could
be a segment number/offset pair (intel x86 in segmented mode), it
could be an 8-digit signed BCD number (burroughs B4900) or it could
be a capability (burroughs B5500) or it could be an offset from
the stack pointer (HP-3000).

In the capability case, the pointer is an entry on the stack that
cannot be written to by the application and which describes the
bounds of the memory region accessible by the pointer. Been
around since the early 1960's and is still supported in Unisys
Clearpath systems.

R. Schubert

unread,
Sep 1, 2015, 9:44:34 AM9/1/15
to
Working draft version N3797 is a bit more specific:

3.7.4.2
4. [...] Indirection through an invalid pointer value and passing an
invalid pointer value to a deallocation function have undefined behavior.
Any other use of an invalid pointer value has implementation-defined
behavior.[38]

[38] Some implementations might define that copying an invalid pointer
value causes a system-generated runtime fault.

It seems that, according to N3797:
- If operator[] does not dereference its argument and takes it by
reference, everything should be fine.
- If the argument is taken by value or copied internally, you may get an
error, depending on your system. But this behavior should be documented
and is therefore not undefined.
- If the argument is dereferenced (or passed to a deallocation function),
then you get UB.

From your version things are a bit muddier since I don't know your
standard's definition of "using". Instinctively I would have agreed with
Ralf's reasoning, but after reading the newer wording, I'm with Barry.

Chris Vine

unread,
Sep 1, 2015, 9:45:29 AM9/1/15
to
§3.7.4.2/4 if C++11: "If the argument given to a deallocation function
in the standard library is a pointer that is not the null pointer
value, the deallocation function shall deallocate the storage
referenced by the pointer, rendering invalid all pointers referring to
any part of the deallocated storage. The effect of using an invalid
pointer value (including passing it to a deallocation function) is
undefined. [footnote: On some implementations, it causes a
system-generated runtime fault.]".

Copying a pointer which neither points to a valid object nor is null
can apparently cause a trap on some hardware. Although applying the
pointer value as the index of a map after deallocation would count as
"using" it (as would, say, incrementing or decrementing it), it should
be fine on x68/64, and probably on all other common architectures.

It is also invalid in C apparently.

Chris

Chris Vine

unread,
Sep 1, 2015, 9:54:59 AM9/1/15
to
Ahah, as pointed out by R Schubert, C++14 is more relaxed than C++11.
As opposed to C++98 and C++11, it makes copying a pointer an
implementation defined matter rather than being undefined.

§3.7.4.2/4 of C++14: "If the argument given to a deallocation function
in the standard library is a pointer that is not the null pointer
value, the deallocation function shall deallocate the storage
referenced by the pointer, rendering invalid all pointers referring to
any part of the deallocated storage. Indirection through an invalid
pointer value and passing an invalid pointer value to a deallocation
function have undefined behavior. Any other use of an invalid pointer
value has implementation-defined behavior. [footnote: Some
implementations might define that copying an invalid pointer value
causes a system-generated runtime fault.]"

Thanks to him for pointing that out.

Chris

Richard

unread,
Sep 1, 2015, 3:12:04 PM9/1/15
to
[Please do not mail me a copy of your followup]

sl...@pacbell.net spake the secret code
<cIhFx.190$Qb1...@fx04.iad> thusly:

>A pointer is many things, depending on the hardware architecture.
>
>It could be a linear address (intel x86 in long mode), it could
>be a segment number/offset pair (intel x86 in segmented mode), it
>could be an 8-digit signed BCD number (burroughs B4900) or it could
>be a capability (burroughs B5500) or it could be an offset from
>the stack pointer (HP-3000).

How many people under the age of 30 have even *heard* of Burroughs?

I do like the examples, however :-).
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
The Terminals Wiki <http://terminals.classiccmp.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>

Scott Lurndal

unread,
Sep 1, 2015, 4:06:57 PM9/1/15
to
legaliz...@mail.xmission.com (Richard) writes:
>[Please do not mail me a copy of your followup]
>
>sl...@pacbell.net spake the secret code
><cIhFx.190$Qb1...@fx04.iad> thusly:
>
>>A pointer is many things, depending on the hardware architecture.
>>
>>It could be a linear address (intel x86 in long mode), it could
>>be a segment number/offset pair (intel x86 in segmented mode), it
>>could be an 8-digit signed BCD number (burroughs B4900) or it could
>>be a capability (burroughs B5500) or it could be an offset from
>>the stack pointer (HP-3000).
>
>How many people under the age of 30 have even *heard* of Burroughs?

The 'B' in BUNCH, none of which still remain in the
computer business.

https://en.wikipedia.org/wiki/BUNCH

Although the burroughs name has been revived for part of the
payment/check processing hardware/software that Burroughs/Unisys
used to sell.

Jorgen Grahn

unread,
Sep 1, 2015, 6:14:51 PM9/1/15
to
On Tue, 2015-09-01, Richard wrote:
> [Please do not mail me a copy of your followup]
>
> sl...@pacbell.net spake the secret code
> <cIhFx.190$Qb1...@fx04.iad> thusly:
>
>>A pointer is many things, depending on the hardware architecture.
>>
>>It could be a linear address (intel x86 in long mode), it could
>>be a segment number/offset pair (intel x86 in segmented mode), it
>>could be an 8-digit signed BCD number (burroughs B4900) or it could
>>be a capability (burroughs B5500) or it could be an offset from
>>the stack pointer (HP-3000).
>
> How many people under the age of 30 have even *heard* of Burroughs?
>
> I do like the examples, however :-).

The last few examples are significantly weaker though, if these
pointers have never been used to implement C or C++ pointers.
Have they?

I agree that x86 segmented mode is a good example. Whenever I think
"oh but pointers are just integers" I force myself to stop and
consider the 80286 for a while. (Not that I ever used one.)

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Richard Damon

unread,
Sep 1, 2015, 8:48:38 PM9/1/15
to
On 9/1/15 9:54 AM, Chris Vine wrote:
>
> Ahah, as pointed out by R Schubert, C++14 is more relaxed than C++11.
> As opposed to C++98 and C++11, it makes copying a pointer an
> implementation defined matter rather than being undefined.
>
> §3.7.4.2/4 of C++14: "If the argument given to a deallocation function
> in the standard library is a pointer that is not the null pointer
> value, the deallocation function shall deallocate the storage
> referenced by the pointer, rendering invalid all pointers referring to
> any part of the deallocated storage. Indirection through an invalid
> pointer value and passing an invalid pointer value to a deallocation
> function have undefined behavior. Any other use of an invalid pointer
> value has implementation-defined behavior. [footnote: Some
> implementations might define that copying an invalid pointer value
> causes a system-generated runtime fault.]"
>
> Thanks to him for pointing that out.
>
> Chris
>

Interesting, this is the first place that I know of where
'implementation defined' specifically includes 'runtime fault'
(presumably allowing immediate termination without any stack unwinding
or cleanup). This sort of behavior is normally only the domain of
undefined behavior.

Öö Tiib

unread,
Sep 2, 2015, 5:19:25 PM9/2/15
to
The non-normative footnotes are just to illustrate and clarify the
meaning of standard.

I would like the standard to define traits on each case of
"implementation defined" behavior or at least macros like
'COPYABLE_BAD_POINTERS', 'COMPARABLE_BAD_POINTERS' etc.
Otherwise they always take some exotic platform that did
never have C++ compiler as excuse of defining nothing and
keeping C++ non-portable.

Louis Krupp

unread,
Sep 2, 2015, 5:49:59 PM9/2/15
to
On Tue, 1 Sep 2015 19:11:55 +0000 (UTC),
legaliz...@mail.xmission.com (Richard) wrote:

>[Please do not mail me a copy of your followup]
>
>sl...@pacbell.net spake the secret code
><cIhFx.190$Qb1...@fx04.iad> thusly:
>
>>A pointer is many things, depending on the hardware architecture.
>>
>>It could be a linear address (intel x86 in long mode), it could
>>be a segment number/offset pair (intel x86 in segmented mode), it
>>could be an 8-digit signed BCD number (burroughs B4900) or it could
>>be a capability (burroughs B5500) or it could be an offset from
>>the stack pointer (HP-3000).
>
>How many people under the age of 30 have even *heard* of Burroughs?

FWIW, the Burroughs Large Systems architecture survives in a Unisys
product line. Not that too many people under 30 (or even over 30)
have paid much attention to Unisys...

Louis

Bo Persson

unread,
Sep 2, 2015, 6:26:46 PM9/2/15
to
You know that one of these "exotic" systems was 16-bit Windows with far
pointers. A deallocated segment could have its descriptor table entry
removed, causing a trap if loaded into a segment register.

Otherwise agree that we could use a feature test macro, like
__cpp_traps_invalid_pointers, and put that in a static_assert. Just to
be sure.


Bo Persson

mark

unread,
Sep 2, 2015, 7:14:07 PM9/2/15
to
On 2015-09-03 00:26, Bo Persson wrote:
> You know that one of these "exotic" systems was 16-bit Windows with far
> pointers. A deallocated segment could have its descriptor table entry
> removed, causing a trap if loaded into a segment register.

Nice example. It wouldn't even be possible to convert the pointer to
uintptr_t, if the segment were deallocated.

Öö Tiib

unread,
Sep 3, 2015, 10:04:36 AM9/3/15
to
Good point, but that is still hardware from sort of proto-c++ era for
me. People wrote in things like "turbo c++" or "watcom c++" ...
and these were the good ones. The Windows 3.11/95/98/ME line
turned into exotic corpse of dead horse in my eyes at Fall 1996.

I was astonished by NT 4.0. It ran on comparable hardware flawlessly
and it was actually an operating system (not a closed source GUI
framework). There was still 2 years left until standardization of C++
at that time.

>
> Otherwise agree that we could use a feature test macro, like
> __cpp_traps_invalid_pointers, and put that in a static_assert. Just to
> be sure.

Good. Note that my point was generic. Standard committee marks things
"implementation defined" to escape from responsibility to do something
real. For developer it is irrelevant since there are no standard facilities
provided to work around situations where vital optimization on one
platform is fatal error on other platform. At same time (as insult to
injury) all traits handling is done more and more convenient and
concepts and reflection are added and what not. Why I need such
powerful tools when core traits of implementation are unexposed?
0 new messages