Local objects destructed by a goto statement!

B

Brian

I would have thought that C++ would not allow a jump (goto) to a place
before a local object was instantiated, but it works! The local object's
destructor is called and then it is REinstantiated. Is this just the
behavior of VC++? Please discuss why this needs to be allowed and why it
is or could be an exploitable feature. (Comments about use of 'goto' and
programming style are irrelevant and off-topic). Example code and
generated assembly below.

void Foo1()
{
label_before_a:
A a;
// The following goto causes endless loop of constructing then
destructing 'a'!
goto label_before_a;
}

void Foo2(bool repeat_flag)
{
label_before_a:
A a;
// The following goto causes endless loop if repeat_flag is set!
if(repeat_flag)
goto label_before_a;
// do something with 'a'.
}

Generated assembly for above functions:

// Foo1 ------------------------------------------------------

PUBLIC ?Foo1@@YGXXZ ; Foo1
; Function compile flags: /Odtp /RTCcu
; File e:\dev\prod\foundation\test\test.cpp
_TEXT SEGMENT
_a$ = -1 ; size = 1
?Foo1@@YGXXZ PROC ; Foo1

; 18 : {

push ebp
mov ebp, esp
push ecx
$label_before_a$15656:

; 19 : label_before_a:
; 20 : A a;

lea ecx, DWORD PTR _a$[ebp]
call ??0A@@QAE@XZ ; A::A

; 21 : // The following goto causes endless loop of constructing
then destructing 'a'!
; 22 : goto label_before_a;

lea ecx, DWORD PTR _a$[ebp]
call ??1A@@QAE@XZ ; A::~A
jmp SHORT $label_before_a$15656

; 23 : }

mov esp, ebp
pop ebp
ret 0
?Foo1@@YGXXZ ENDP ; Foo1
_TEXT ENDS


// Foo2 ------------------------------------------------------

PUBLIC ?Foo2@@YGXH@Z ; Foo2
; Function compile flags: /Odtp /RTCcu
_TEXT SEGMENT
_a$ = -1 ; size = 1
_repeat_flag$ = 8 ; size = 4
?Foo2@@YGXH@Z PROC ; Foo2

; 26 : {

push ebp
mov ebp, esp
push ecx
$label_before_a$15661:

; 27 : label_before_a:
; 28 : A a;

lea ecx, DWORD PTR _a$[ebp]
call ??0A@@QAE@XZ ; A::A

; 29 : // The following goto causes endless loop if repeat_flag is
set!
; 30 : if(repeat_flag)

cmp DWORD PTR _repeat_flag$[ebp], 0
je SHORT $LN1@Foo2

; 31 : goto label_before_a;

lea ecx, DWORD PTR _a$[ebp]
call ??1A@@QAE@XZ ; A::~A
jmp SHORT $label_before_a$15661
$LN1@Foo2:

; 32 : // do something with 'a'.
; 33 : }

lea ecx, DWORD PTR _a$[ebp]
call ??1A@@QAE@XZ ; A::~A
mov esp, ebp
pop ebp
ret 4
?Foo2@@YGXH@Z ENDP ; Foo2
_TEXT ENDS
 
G

Gert-Jan de Vos

I would have thought that C++ would not allow a jump (goto) to a place
before a local object was instantiated, but it works! The local object's
destructor is called and then it is REinstantiated. Is this just the
behavior of VC++? Please discuss why this needs to be allowed and why it
is or could be an exploitable feature. (Comments about use of 'goto' and
programming style are irrelevant and off-topic). Example code and
generated assembly below.

void Foo1()
{
  label_before_a:
    A a;
    // The following goto causes endless loop of constructing then
destructing 'a'!
    goto label_before_a;

}

6.6-2 explicitly states that this should work as you state:

"On exit from a scope (however accomplished), destructors (12.4) are
called for all constructed objects with automatic storage duration
(3.7.2) (named objects or temporaries) that are declared in that
scope, in the reverse order of their declaration. Transfer out of a
loop, out of a block, or back past an initialized variable with
automatic storage duration involves the destruction of variables
with automatic storage duration that are in scope at the point
transferred from but not at the point transferred to. ..."

Gert-Jan
 
G

gwowen

I would have thought that C++ would not allow a jump (goto) to a place
before a local object was instantiated, but it works! The local object's
destructor is called and then it is REinstantiated. Is this just the
behavior of VC++? Please discuss why this needs to be allowed and why it
is or could be an exploitable feature.

It doesn't need to be allowed. It's not hard to get the same behaviour
without a goto, just use looping constructs, although it can look
terribly contrived. Sometimes what you mean is "Go to", in which
"goto" expresses the intent better than the following:

#include <cstdlib>
#include <ctime>
#include <iostream>
static bool catisdead = false;
static unsigned loopcount = 0;

struct A {
A(){
if(rand() < RAND_MAX/8){
catisdead = true;
std::cout << "Yes\n";
} else {
std::cout << "Nope\n";
}
}

~A() {
++loopcount;
}
};

int main()
{
srand(time(NULL));
do {
A a;
if(catisdead == false) continue;

std::cout << "Erwin was 'ere\n";
} while(catisdead == false);
std::cout << "a was destroyed " << loopcount << " times\n";
}
 
C

changsheng

Jump *after* a place where local objects were instantiated is not
allowed, not *before*. The following block will cause g++ report jump
to label mid_loop skips initialization of A a.

while (0) {
enter_loop:
A a;
mid_loop:
A b;
}
goto enter_loop;
goto mid_loop;


I would have thought that C++ would not allow a jump (goto) to a place
before a local object was instantiated, but it works! The local object's
destructor is called and then it is REinstantiated. Is this just the
behavior of VC++? Please discuss why this needs to be allowed and why it
is or could be an exploitable feature. (Comments about use of 'goto' and
programming style are irrelevant and off-topic). Example code and
generated assembly below.

void Foo1()
{
  label_before_a:
    A a;
    // The following goto causes endless loop of constructing then
destructing 'a'!
    goto label_before_a;

}

void Foo2(bool repeat_flag)
{
  label_before_a:
    A a;
    // The following goto causes endless loop if repeat_flag is set!
    if(repeat_flag)
        goto label_before_a;
   // do something with 'a'.

}

Generated assembly for above functions:

// Foo1 ------------------------------------------------------

PUBLIC ?Foo1@@YGXXZ     ; Foo1
; Function compile flags: /Odtp /RTCcu
; File e:\dev\prod\foundation\test\test.cpp
_TEXT SEGMENT
_a$ = -1      ; size = 1
?Foo1@@YGXXZ PROC     ; Foo1

; 18   : {

 push ebp
 mov ebp, esp
 push ecx
$label_before_a$15656:

; 19   :   label_before_a:
; 20   :     A a;

 lea ecx, DWORD PTR _a$[ebp]
 call ??0A@@QAE@XZ    ; A::A

; 21   :     // The following goto causes endless loop of constructing
then destructing 'a'!
; 22   :     goto label_before_a;

 lea ecx, DWORD PTR _a$[ebp]
 call ??1A@@QAE@XZ    ; A::~A
 jmp SHORT $label_before_a$15656

; 23   : }

 mov esp, ebp
 pop ebp
 ret 0
?Foo1@@YGXXZ ENDP     ; Foo1
_TEXT ENDS

// Foo2 ------------------------------------------------------

PUBLIC ?Foo2@@YGXH@Z     ; Foo2
; Function compile flags: /Odtp /RTCcu
_TEXT SEGMENT
_a$ = -1      ; size = 1
_repeat_flag$ = 8     ; size = 4
?Foo2@@YGXH@Z PROC     ; Foo2

; 26   : {

 push ebp
 mov ebp, esp
 push ecx
$label_before_a$15661:

; 27   :   label_before_a:
; 28   :     A a;

 lea ecx, DWORD PTR _a$[ebp]
 call ??0A@@QAE@XZ    ; A::A

; 29   :     // The following goto causes endless loop if repeat_flag is
set!
; 30   :     if(repeat_flag)

 cmp DWORD PTR _repeat_flag$[ebp], 0
 je SHORT $LN1@Foo2

; 31   :         goto label_before_a;

 lea ecx, DWORD PTR _a$[ebp]
 call ??1A@@QAE@XZ    ; A::~A
 jmp SHORT $label_before_a$15661
$LN1@Foo2:

; 32   :    // do something with 'a'.
; 33   : }

 lea ecx, DWORD PTR _a$[ebp]
 call ??1A@@QAE@XZ    ; A::~A
 mov esp, ebp
 pop ebp
 ret 4
?Foo2@@YGXH@Z ENDP     ; Foo2
_TEXT ENDS
 
B

Brian

changsheng said:
Jump *after* a place where local objects were instantiated is not
allowed, not *before*. The following block will cause g++ report jump
to label mid_loop skips initialization of A a.

while (0) {
enter_loop:
A a;
mid_loop:
A b;
}
goto enter_loop;
goto mid_loop;

Can someone put into proper perspective why jump back causes destructor
calls but jump forward doesn't cause constructor calls?
 
G

gwowen

Can someone put into proper perspective why jump back causes destructor
calls but jump forward doesn't cause constructor calls?

Flowing over a declaration enters the object's scope, and constructs
the object (as noted above, you're not allowed to jump into a scope).
Leaving the objects scope -- however that is achieved, with the
probable exception of longjmp() -- destroys it. Jumping right over an
object's scope does neither.
 
J

James Kanze

Can someone put into proper perspective why jump back causes
destructor calls but jump forward doesn't cause constructor
calls?

Initialization can involve other values and objects. Consider
something like:

goto skipInit;
MyClass x(f(), g(), h());
skipInit:

Should f(), g() and h() be called? And what about:

int i = 0;
goto skipInit;
i = 42;
MyClass x(i);
skipInit:
// what should the value of i be here?
 
M

Mike Oliver

If you want an intuitive sense of why jump forward doesn't destruct
objects but jump back does, think of it like Back to the Future: a
goto back in the program is like going back in time, so the scope has
changed, i.e., its like you back in time to change something.
Necessarily, everything that happened after the point to which you
jumped back to is up for grabs, so the standard requires that all that
info it is destructed. It is like a "do-over".

Similarly, a goto that moves forward in code will preserve objects and
such, so long as you didn't go out of scope (though I don't know what
would happen then...sounds undefined to me, but I have no clue).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,069
Latest member
SimplyleanKetoReviews

Latest Threads

Top